summaryrefslogtreecommitdiff
path: root/utils/hlfaceposer
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hlfaceposer')
-rw-r--r--utils/hlfaceposer/AnimationBrowser.cpp1500
-rw-r--r--utils/hlfaceposer/AnimationBrowser.h187
-rw-r--r--utils/hlfaceposer/CloseCaptionTool.cpp995
-rw-r--r--utils/hlfaceposer/CloseCaptionTool.h74
-rw-r--r--utils/hlfaceposer/EdgeProperties.cpp271
-rw-r--r--utils/hlfaceposer/EdgeProperties.h42
-rw-r--r--utils/hlfaceposer/EditPhrase.cpp82
-rw-r--r--utils/hlfaceposer/EditPhrase.h39
-rw-r--r--utils/hlfaceposer/GestureTool.cpp1986
-rw-r--r--utils/hlfaceposer/GestureTool.h174
-rw-r--r--utils/hlfaceposer/ICloseCaptionManager.h33
-rw-r--r--utils/hlfaceposer/ProgressDialog.cpp243
-rw-r--r--utils/hlfaceposer/ProgressDialog.h26
-rw-r--r--utils/hlfaceposer/RampTool.cpp2379
-rw-r--r--utils/hlfaceposer/RampTool.h205
-rw-r--r--utils/hlfaceposer/SceneRampTool.cpp2244
-rw-r--r--utils/hlfaceposer/SceneRampTool.h201
-rw-r--r--utils/hlfaceposer/VGuiWnd.cpp324
-rw-r--r--utils/hlfaceposer/VGuiWnd.h69
-rw-r--r--utils/hlfaceposer/actorproperties.cpp77
-rw-r--r--utils/hlfaceposer/actorproperties.h28
-rw-r--r--utils/hlfaceposer/addsoundentry.cpp164
-rw-r--r--utils/hlfaceposer/addsoundentry.h40
-rw-r--r--utils/hlfaceposer/audiowaveoutput.h127
-rw-r--r--utils/hlfaceposer/basedialogparams.cpp65
-rw-r--r--utils/hlfaceposer/basedialogparams.h29
-rw-r--r--utils/hlfaceposer/cbase.h21
-rw-r--r--utils/hlfaceposer/cclookup.cpp240
-rw-r--r--utils/hlfaceposer/cclookup.h27
-rw-r--r--utils/hlfaceposer/channelproperties.cpp124
-rw-r--r--utils/hlfaceposer/channelproperties.h38
-rw-r--r--utils/hlfaceposer/choiceproperties.cpp125
-rw-r--r--utils/hlfaceposer/choiceproperties.h42
-rw-r--r--utils/hlfaceposer/choreoactorwidget.cpp415
-rw-r--r--utils/hlfaceposer/choreoactorwidget.h118
-rw-r--r--utils/hlfaceposer/choreochannelwidget.cpp1207
-rw-r--r--utils/hlfaceposer/choreochannelwidget.h120
-rw-r--r--utils/hlfaceposer/choreoeventwidget.cpp784
-rw-r--r--utils/hlfaceposer/choreoeventwidget.h88
-rw-r--r--utils/hlfaceposer/choreoglobaleventwidget.cpp216
-rw-r--r--utils/hlfaceposer/choreoglobaleventwidget.h59
-rw-r--r--utils/hlfaceposer/choreoview.cpp11647
-rw-r--r--utils/hlfaceposer/choreoview.h832
-rw-r--r--utils/hlfaceposer/choreoviewcolors.h50
-rw-r--r--utils/hlfaceposer/choreowidget.cpp163
-rw-r--r--utils/hlfaceposer/choreowidget.h79
-rw-r--r--utils/hlfaceposer/choreowidgetdrawhelper.cpp1069
-rw-r--r--utils/hlfaceposer/choreowidgetdrawhelper.h122
-rw-r--r--utils/hlfaceposer/controlpanel.cpp976
-rw-r--r--utils/hlfaceposer/controlpanel.h161
-rw-r--r--utils/hlfaceposer/curveeditorhelpers.h370
-rw-r--r--utils/hlfaceposer/eventproperties.cpp657
-rw-r--r--utils/hlfaceposer/eventproperties.h105
-rw-r--r--utils/hlfaceposer/eventproperties_expression.cpp298
-rw-r--r--utils/hlfaceposer/eventproperties_expression.h18
-rw-r--r--utils/hlfaceposer/eventproperties_face.cpp234
-rw-r--r--utils/hlfaceposer/eventproperties_face.h18
-rw-r--r--utils/hlfaceposer/eventproperties_firetrigger.cpp240
-rw-r--r--utils/hlfaceposer/eventproperties_firetrigger.h18
-rw-r--r--utils/hlfaceposer/eventproperties_flexanimation.cpp203
-rw-r--r--utils/hlfaceposer/eventproperties_flexanimation.h18
-rw-r--r--utils/hlfaceposer/eventproperties_generic.cpp272
-rw-r--r--utils/hlfaceposer/eventproperties_generic.h18
-rw-r--r--utils/hlfaceposer/eventproperties_gesture.cpp297
-rw-r--r--utils/hlfaceposer/eventproperties_gesture.h18
-rw-r--r--utils/hlfaceposer/eventproperties_interrupt.cpp217
-rw-r--r--utils/hlfaceposer/eventproperties_interrupt.h18
-rw-r--r--utils/hlfaceposer/eventproperties_lookat.cpp327
-rw-r--r--utils/hlfaceposer/eventproperties_lookat.h18
-rw-r--r--utils/hlfaceposer/eventproperties_moveto.cpp368
-rw-r--r--utils/hlfaceposer/eventproperties_moveto.h18
-rw-r--r--utils/hlfaceposer/eventproperties_permitresponses.cpp217
-rw-r--r--utils/hlfaceposer/eventproperties_permitresponses.h18
-rw-r--r--utils/hlfaceposer/eventproperties_sequence.cpp251
-rw-r--r--utils/hlfaceposer/eventproperties_sequence.h18
-rw-r--r--utils/hlfaceposer/eventproperties_speak.cpp673
-rw-r--r--utils/hlfaceposer/eventproperties_speak.h18
-rw-r--r--utils/hlfaceposer/eventproperties_subscene.cpp234
-rw-r--r--utils/hlfaceposer/eventproperties_subscene.h18
-rw-r--r--utils/hlfaceposer/expclass.cpp735
-rw-r--r--utils/hlfaceposer/expclass.h74
-rw-r--r--utils/hlfaceposer/expression.cpp472
-rw-r--r--utils/hlfaceposer/expression.h114
-rw-r--r--utils/hlfaceposer/expressionproperties.cpp78
-rw-r--r--utils/hlfaceposer/expressionproperties.h27
-rw-r--r--utils/hlfaceposer/expressions.cpp692
-rw-r--r--utils/hlfaceposer/expressions.h51
-rw-r--r--utils/hlfaceposer/expressiontool.cpp4816
-rw-r--r--utils/hlfaceposer/expressiontool.h368
-rw-r--r--utils/hlfaceposer/faceposer_models.cpp1142
-rw-r--r--utils/hlfaceposer/faceposer_models.h193
-rw-r--r--utils/hlfaceposer/faceposer_vgui.cpp140
-rw-r--r--utils/hlfaceposer/faceposer_vgui.h40
-rw-r--r--utils/hlfaceposer/faceposertoolwindow.cpp746
-rw-r--r--utils/hlfaceposer/faceposertoolwindow.h108
-rw-r--r--utils/hlfaceposer/faceposerworkspace.cpp258
-rw-r--r--utils/hlfaceposer/fileloaderthread.cpp422
-rw-r--r--utils/hlfaceposer/flexpanel.cpp1092
-rw-r--r--utils/hlfaceposer/flexpanel.h144
-rw-r--r--utils/hlfaceposer/globaleventproperties.cpp205
-rw-r--r--utils/hlfaceposer/globaleventproperties.h48
-rw-r--r--utils/hlfaceposer/hlfaceposer.cpp744
-rw-r--r--utils/hlfaceposer/hlfaceposer.h83
-rw-r--r--utils/hlfaceposer/hlfaceposer.rc898
-rw-r--r--utils/hlfaceposer/hlfaceposer.vpc411
-rw-r--r--utils/hlfaceposer/ico00001.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00002.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00003.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00004.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00005.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00006.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ico00007.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/icon1.icobin0 -> 29926 bytes
-rw-r--r--utils/hlfaceposer/icon2.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/ifaceposersound.h55
-rw-r--r--utils/hlfaceposer/ifaceposerworkspace.h40
-rw-r--r--utils/hlfaceposer/ifileloader.h31
-rw-r--r--utils/hlfaceposer/inputproperties.cpp80
-rw-r--r--utils/hlfaceposer/inputproperties.h29
-rw-r--r--utils/hlfaceposer/mapentities.h29
-rw-r--r--utils/hlfaceposer/matsyswin.cpp806
-rw-r--r--utils/hlfaceposer/matsyswin.h210
-rw-r--r--utils/hlfaceposer/mdlviewer.cpp2752
-rw-r--r--utils/hlfaceposer/mdlviewer.h196
-rw-r--r--utils/hlfaceposer/mxbitmapbutton.cpp96
-rw-r--r--utils/hlfaceposer/mxbitmapbutton.h34
-rw-r--r--utils/hlfaceposer/mxbitmaptools.cpp154
-rw-r--r--utils/hlfaceposer/mxbitmaptools.h35
-rw-r--r--utils/hlfaceposer/mxbitmapwindow.cpp84
-rw-r--r--utils/hlfaceposer/mxbitmapwindow.h35
-rw-r--r--utils/hlfaceposer/mxexpressionslider.cpp723
-rw-r--r--utils/hlfaceposer/mxexpressionslider.h91
-rw-r--r--utils/hlfaceposer/mxexpressiontab.cpp62
-rw-r--r--utils/hlfaceposer/mxexpressiontab.h29
-rw-r--r--utils/hlfaceposer/mxexpressiontray.cpp1212
-rw-r--r--utils/hlfaceposer/mxexpressiontray.h161
-rw-r--r--utils/hlfaceposer/mxstatuswindow.cpp311
-rw-r--r--utils/hlfaceposer/mxstatuswindow.h61
-rw-r--r--utils/hlfaceposer/phonemeeditor.cpp8799
-rw-r--r--utils/hlfaceposer/phonemeeditor.h591
-rw-r--r--utils/hlfaceposer/phonemeeditorcolors.h14
-rw-r--r--utils/hlfaceposer/phonemeproperties.cpp434
-rw-r--r--utils/hlfaceposer/phonemeproperties.h31
-rw-r--r--utils/hlfaceposer/project.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/project1.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/resource.h149
-rw-r--r--utils/hlfaceposer/scene.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/snd_audio_source.cpp63
-rw-r--r--utils/hlfaceposer/snd_audio_source.h124
-rw-r--r--utils/hlfaceposer/snd_wave_mixer.cpp537
-rw-r--r--utils/hlfaceposer/snd_wave_mixer.h24
-rw-r--r--utils/hlfaceposer/snd_wave_mixer_adpcm.cpp516
-rw-r--r--utils/hlfaceposer/snd_wave_mixer_adpcm.h24
-rw-r--r--utils/hlfaceposer/snd_wave_mixer_private.h102
-rw-r--r--utils/hlfaceposer/snd_wave_source.cpp605
-rw-r--r--utils/hlfaceposer/snd_wave_source.h78
-rw-r--r--utils/hlfaceposer/sound.cpp1800
-rw-r--r--utils/hlfaceposer/sound.h97
-rw-r--r--utils/hlfaceposer/soundlookup.cpp140
-rw-r--r--utils/hlfaceposer/soundlookup.h33
-rw-r--r--utils/hlfaceposer/tabwindow.cpp480
-rw-r--r--utils/hlfaceposer/tabwindow.h102
-rw-r--r--utils/hlfaceposer/timelineitem.cpp1763
-rw-r--r--utils/hlfaceposer/timelineitem.h161
-rw-r--r--utils/hlfaceposer/vcd1.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/vcdbrowser.cpp881
-rw-r--r--utils/hlfaceposer/vcdbrowser.h120
-rw-r--r--utils/hlfaceposer/wav1.icobin0 -> 1078 bytes
-rw-r--r--utils/hlfaceposer/wavebrowser.cpp1182
-rw-r--r--utils/hlfaceposer/wavebrowser.h125
-rw-r--r--utils/hlfaceposer/wavefile.cpp132
-rw-r--r--utils/hlfaceposer/wavefile.h86
-rw-r--r--utils/hlfaceposer/workspac.icobin0 -> 1078 bytes
173 files changed, 76134 insertions, 0 deletions
diff --git a/utils/hlfaceposer/AnimationBrowser.cpp b/utils/hlfaceposer/AnimationBrowser.cpp
new file mode 100644
index 0000000..b487250
--- /dev/null
+++ b/utils/hlfaceposer/AnimationBrowser.cpp
@@ -0,0 +1,1500 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <windows.h>
+#include "AnimationBrowser.h"
+#include "hlfaceposer.h"
+#include "ChoreoView.h"
+#include "StudioModel.h"
+#include "ViewerSettings.h"
+#include "choreowidgetdrawhelper.h"
+#include "faceposer_models.h"
+#include "tabwindow.h"
+#include "inputproperties.h"
+#include "KeyValues.h"
+#include "filesystem.h"
+#include "tier1/KeyValues.h"
+#include "tier1/UtlBuffer.h"
+
+#define MAX_THUMBNAILSIZE 256
+#define MIN_THUMBNAILSIZE 64
+#define THUMBNAIL_SIZE_STEP 4
+#define DEFAULT_THUMBNAIL_SIZE 128
+#define TOP_GAP 70
+
+AnimationBrowser *g_pAnimationBrowserTool = 0;
+extern double realtime;
+
+void CreatePath( const char *pPath );
+
+void CCustomAnim::LoadFromFile()
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ KeyValues *kv = new KeyValues( "CustomAnimation" );
+ if ( kv->LoadFromFile( filesystem, fn, "MOD" ) )
+ {
+ for ( KeyValues *sub = kv->GetFirstSubKey(); sub ; sub = sub->GetNextKey() )
+ {
+ CUtlSymbol anim;
+ anim = sub->GetString();
+
+ m_Animations.AddToTail( anim );
+ }
+ }
+ kv->deleteThis();
+}
+
+void CCustomAnim::SaveToFile()
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.Printf( "\"%s\"\n", m_ShortName.String() );
+ buf.Printf( "{\n" );
+ for ( int i = 0; i < m_Animations.Count(); ++i )
+ {
+ buf.Printf( "\t\"item%d\" \"%s\"\n", i + 1, m_Animations[ i ].String() );
+ }
+ buf.Printf( "}\n" );
+
+ CreatePath( fn );
+ filesystem->WriteFile( fn, "MOD", buf );
+}
+
+bool CCustomAnim::HasAnimation( char const *search )
+{
+ CUtlSymbol searchSym;
+ searchSym = search;
+ if ( m_Animations.Find( searchSym ) != m_Animations.InvalidIndex() )
+ return true;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAnimBrowserTab : public CTabWindow
+{
+ typedef CTabWindow BaseClass;
+
+public:
+
+
+ CAnimBrowserTab( AnimationBrowser *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
+ CTabWindow( (mxWindow *)parent, x, y, w, h, id, style )
+ {
+ // SetInverted( true );
+ }
+
+ void Init( void )
+ {
+ add( "all" );
+ add( "gestures" );
+ add( "postures" );
+ add( "search results" );
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my )
+ {
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ // New scene, edit comments
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ pop->add ("&New Group...", IDC_AB_CREATE_CUSTOM );
+
+ mxPopupMenu *sub = NULL;
+ for ( int i = 0; i < m_CustomGroups.Count(); ++i )
+ {
+ if ( !sub )
+ {
+ sub = new mxPopupMenu();
+ }
+ sub->add( va( "%s", m_CustomGroups[ i ].String() ), IDC_AB_DELETEGROUPSTART + i );
+ }
+ if ( sub )
+ {
+ pop->addMenu( "Delete Group", sub );
+ }
+
+ pop->addSeparator();
+
+ sub = new mxPopupMenu();
+ for ( int i = 0; i < m_CustomGroups.Count(); ++i )
+ {
+ sub->add( va( "%s", m_CustomGroups[ i ].String() ), IDC_AB_RENAMEGROUPSTART + i );
+ }
+
+ pop->addMenu( "Rename Group", sub );
+
+ pop->popup( getParent(), pt.x, pt.y );
+ }
+
+ void UpdateCustomTabs( CUtlVector< CCustomAnim * >& list )
+ {
+ m_CustomGroups.Purge();
+
+ while ( getItemCount() > AnimationBrowser::FILTER_FIRST_CUSTOM )
+ {
+ remove( getItemCount() - 1 );
+ }
+
+ for ( int i = 0; i < list.Count(); ++i )
+ {
+ const CCustomAnim *anim = list[ i ];
+ add( anim->m_ShortName.String() );
+ m_CustomGroups.AddToTail( anim->m_ShortName );
+ }
+ }
+
+private:
+
+ CUtlVector< CUtlSymbol > m_CustomGroups;
+};
+
+
+
+AnimationBrowser::AnimationBrowser( mxWindow *parent, int id /*=0*/ )
+: IFacePoserToolWindow( "AnimationBrowser", "Animations" ),
+ mxWindow( parent, 0, 0, 0, 0, "AnimationBrowser", id )
+{
+ setId( id );
+
+ m_nTopOffset = 0;
+ slScrollbar = new mxScrollbar( this, 0, 0, 18, 100, IDC_AB_TRAYSCROLL, mxScrollbar::Vertical );
+
+ m_nLastNumAnimations = -1;
+
+ m_nGranularity = 10;
+
+ m_nCurCell = -1;
+
+ m_nClickedCell = -1;
+
+ m_nGap = 4;
+ m_nDescriptionHeight = 34;
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsizeanim;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ m_bDragging = false;
+ m_nDragCell = -1;
+
+ m_szSearchString[0]=0;
+
+ m_pFilterTab = new CAnimBrowserTab( this, 5, 5, 240, 20, IDC_AB_FILTERTAB );
+ m_pFilterTab->Init();
+
+ m_pSearchEntry = new mxLineEdit( this, 0, 0, 0, 0, "" );
+
+ m_pThumbnailIncreaseButton = new mxButton( this, 0, 0, 18, 18, "+", IDC_AB_THUMBNAIL_INCREASE );
+ m_pThumbnailDecreaseButton = new mxButton( this, 0, 0, 18, 18, "-", IDC_AB_THUMBNAIL_DECREASE );
+ m_nCurFilter = FILTER_NONE;
+
+ m_flDragTime = 0.0f;
+
+ OnFilter();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : AnimationBrowser::~AnimationBrowser
+//-----------------------------------------------------------------------------
+AnimationBrowser::~AnimationBrowser ( void )
+{
+ g_pAnimationBrowserTool = NULL;
+}
+
+void AnimationBrowser::Shutdown()
+{
+ PurgeCustom();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : cellsize -
+//-----------------------------------------------------------------------------
+void AnimationBrowser::SetCellSize( int cellsize )
+{
+ m_nSnapshotWidth = cellsize;
+ m_nSnapshotHeight = cellsize + m_nDescriptionHeight;
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::Deselect( void )
+{
+ m_nCurCell = -1;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : exp -
+//-----------------------------------------------------------------------------
+void AnimationBrowser::Select( int sequence )
+{
+ m_nCurCell = sequence;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int AnimationBrowser::ComputePixelsNeeded( void )
+{
+ int seqcount = GetSequenceCount();
+
+ if ( !seqcount )
+ return 100;
+
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int rowsneeded = ( ( seqcount + colsperrow - 1 ) / colsperrow );
+ return rowsneeded * ( m_nSnapshotHeight + m_nGap ) + m_nGap + TOP_GAP + GetCaptionHeight();
+}
+
+bool AnimationBrowser::ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch )
+{
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int row, col;
+
+ row = cell / colsperrow;
+ col = cell % colsperrow;
+
+ // don't allow partial columns
+
+ rcx = m_nGap + col * ( m_nSnapshotWidth + m_nGap );
+ rcy = GetCaptionHeight() + TOP_GAP + ( -m_nTopOffset * m_nGranularity ) + m_nGap + row * ( m_nSnapshotHeight + m_nGap );
+
+ // Starts off screen
+ if ( rcx < 0 )
+ return false;
+
+ // Ends off screen
+ if ( rcx + m_nSnapshotWidth + m_nGap > this->w2() )
+ return false;
+
+ // Allow partial in y direction
+ if ( rcy > this->h2() )
+ return false;
+
+ if ( rcy + m_nSnapshotHeight + m_nGap < 0 )
+ return false;
+
+ // Some portion is onscreen
+ rcw = m_nSnapshotWidth;
+ rch = m_nSnapshotHeight;
+ return true;
+}
+
+void AnimationBrowser::DrawSequenceFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr )
+{
+ helper.DrawOutlinedRect( clr, PS_SOLID, 4, x, y, x + w, y + h );
+}
+
+void AnimationBrowser::DrawSequenceDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, int sequence, mstudioseqdesc_t &seqdesc )
+{
+ int textheight = 15;
+
+ RECT textRect;
+ textRect.left = x + 5;
+ textRect.top = y + h - 2 * textheight - 12;
+ textRect.right = x + w - 10;
+ textRect.bottom = y + h - 12;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%s", seqdesc.pszLabel() );
+
+ StudioModel *mdl = models->GetActiveStudioModel();
+ if ( !mdl )
+ return;
+
+ OffsetRect( &textRect, 0, textheight );
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%.2f seconds",
+ mdl->GetDuration( sequence ) );
+
+ textRect.top = y + h - 4 * textheight - 1;
+ textRect.bottom = textRect.top + textheight;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 50, 200, 255 ), textRect, "frames %i",
+ mdl->GetNumFrames( sequence ) );
+
+ OffsetRect( &textRect, 0, textheight - 4 );
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 50, 200, 255 ), textRect, "fps %.2f",
+ (float)mdl->GetFPS( sequence ) );
+
+}
+
+bool AnimationBrowser::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+void AnimationBrowser::DrawThumbNail( int sequence, CChoreoWidgetDrawHelper& helper, int rcx, int rcy, int rcw, int rch )
+{
+ HDC dc = helper.GrabDC();
+
+ helper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rcx, rcy, rcw + rcx, rch + rcy );
+
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( sequence );
+ if ( !pseqdesc )
+ return;
+
+ mxbitmapdata_t *bm = models->GetBitmapForSequence( models->GetActiveModelIndex(), TranslateSequenceNumber( sequence ) );
+ if ( bm && bm->valid )
+ {
+ DrawBitmapToDC( dc, rcx, rcy, rcw, rch - m_nDescriptionHeight, *bm );
+ helper.DrawOutlinedRect( RGB( 127, 127, 127 ), PS_SOLID, 1, rcx, rcy, rcx + rcw, rcy + rch - m_nDescriptionHeight );
+ }
+
+ DrawSequenceDescription( helper, rcx, rcy, rcw, rch, TranslateSequenceNumber( sequence ), *pseqdesc );
+
+ if ( sequence == m_nCurCell )
+ {
+ DrawSequenceFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 255, 100, 63 ) );
+ }
+}
+
+void AnimationBrowser::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ bool updateSelection = false;
+
+ int curcount = GetSequenceCount();
+ if ( curcount != m_nLastNumAnimations )
+ {
+ m_nTopOffset = 0;
+ RepositionSlider();
+ m_nLastNumAnimations = curcount;
+ updateSelection = true;
+ }
+
+ CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
+ HandleToolRedraw( helper );
+
+ int w, h;
+ w = w2();
+ h = h2();
+
+ RECT clipRect;
+ helper.GetClientRect( clipRect );
+
+ clipRect.top += TOP_GAP + GetCaptionHeight();
+
+ helper.StartClipping( clipRect );
+
+ int rcx, rcy, rcw, rch;
+
+ EnableStickySnapshotMode( );
+
+ int c = curcount;
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !ComputeRect( i, rcx, rcy, rcw, rch ) )
+ {
+ // Cache in .bmp no matter what
+ // This was too slow, so turning it back off
+ //models->GetBitmapForSequence( models->GetActiveModelIndex(), TranslateSequenceNumber( i ) );
+ continue;
+ }
+
+ DrawThumbNail( i, helper, rcx, rcy, rcw, rch );
+ }
+
+ DisableStickySnapshotMode( );
+
+ helper.StopClipping();
+
+ RECT rcText;
+ rcText.right = w2();
+ rcText.left = rcText.right - 120;
+ rcText.top = 8;
+ rcText.bottom = rcText.top + 15;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), rcText, "%i sequences",
+ curcount );
+
+}
+
+int AnimationBrowser::GetCellUnderPosition( int x, int y )
+{
+ int count = GetSequenceCount();
+ if ( !count )
+ return -1;
+
+ int rcx, rcy, rcw, rch;
+ int c = 0;
+ while ( c < count )
+ {
+ if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
+ {
+ c++;
+ continue;
+ }
+
+ if ( x >= rcx && x <= rcx + rcw &&
+ y >= rcy && y <= rcy + rch )
+ {
+ return c;
+ }
+
+ c++;
+ }
+ return -1;
+}
+
+void AnimationBrowser::RepositionSlider( void )
+{
+ int trueh = h2() - GetCaptionHeight();
+
+ int heightpixels = trueh / m_nGranularity;
+ int rangepixels = ComputePixelsNeeded() / m_nGranularity;
+
+ if ( rangepixels < heightpixels )
+ {
+ m_nTopOffset = 0;
+ slScrollbar->setVisible( false );
+ }
+ else
+ {
+ slScrollbar->setVisible( true );
+ }
+
+ slScrollbar->setBounds( w2() - 16, GetCaptionHeight() + TOP_GAP, 16, trueh - TOP_GAP );
+
+ m_nTopOffset = max( 0, m_nTopOffset );
+ m_nTopOffset = min( rangepixels, m_nTopOffset );
+
+ slScrollbar->setRange( 0, rangepixels );
+ slScrollbar->setValue( m_nTopOffset );
+ slScrollbar->setPagesize( heightpixels );
+}
+
+void AnimationBrowser::SetClickedCell( int cell )
+{
+ m_nClickedCell = cell;
+ Select( cell );
+}
+
+void AnimationBrowser::ShowRightClickMenu( int mx, int my )
+{
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( !pseqdesc )
+ return;
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ pop->add( va( "New Group..." ), IDC_AB_CREATE_CUSTOM );
+
+ if ( m_CustomAnimationTabs.Count() > 0 )
+ {
+ mxPopupMenu *ca = new mxPopupMenu();
+ Assert( ca );
+
+ for ( int i = 0; i < m_CustomAnimationTabs.Count() ; ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ ca->add( va( "%s", anim->m_ShortName.String() ), IDC_AB_ADDTOGROUPSTART + i );
+ }
+
+ pop->addMenu( "Add to Group", ca );
+
+ ca = new mxPopupMenu();
+
+ bool useMenu = false;
+ for ( int i = 0; i < m_CustomAnimationTabs.Count() ; ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ if ( anim->HasAnimation( pseqdesc->pszLabel() ) )
+ {
+ ca->add( va( "%s", anim->m_ShortName.String() ), IDC_AB_REMOVEFROMGROUPSTART + i );
+ useMenu = true;
+ }
+ }
+
+ if ( useMenu )
+ {
+ pop->addMenu( "Remove from Group", ca );
+ }
+ else
+ {
+ delete ca;
+ }
+ }
+
+ pop->addSeparator();
+
+ pop->add( va( "Re-create thumbnail for '%s'", pseqdesc->pszLabel() ), IDC_AB_CONTEXT_CREATEBITMAP );
+ pop->add( va( "Re-create all thumbnails" ), IDC_AB_CONTEXT_CREATEALLBITMAPS );
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::DrawFocusRect( void )
+{
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &m_rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+static bool IsWindowOrChild( mxWindow *parent, HWND test )
+{
+ HWND parentHwnd = (HWND)parent->getHandle();
+ if ( test == parentHwnd ||
+ IsChild( parentHwnd, test ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+int AnimationBrowser::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ if ( event->action >= IDC_AB_ADDTOGROUPSTART && event->action <= IDC_AB_ADDTOGROUPEND )
+ {
+ int index = event->action - IDC_AB_ADDTOGROUPSTART;
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( pseqdesc )
+ {
+ AddAnimationToCustomFile( index, pseqdesc->pszLabel() );
+ }
+ }
+ else if ( event->action >= IDC_AB_REMOVEFROMGROUPSTART && event->action <= IDC_AB_REMOVEFROMGROUPEND )
+ {
+ int index = event->action - IDC_AB_REMOVEFROMGROUPSTART;
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nCurCell );
+ if ( pseqdesc )
+ {
+ RemoveAnimationFromCustomFile( index, pseqdesc->pszLabel() );
+ }
+ }
+ else if ( event->action >= IDC_AB_DELETEGROUPSTART && event->action <= IDC_AB_DELETEGROUPEND )
+ {
+ int index = event->action - IDC_AB_DELETEGROUPSTART;
+ DeleteCustomFile( index );
+ }
+ else if ( event->action >= IDC_AB_RENAMEGROUPSTART && event->action <= IDC_AB_RENAMEGROUPEND )
+ {
+ int index = event->action - IDC_AB_RENAMEGROUPSTART;
+ RenameCustomFile( index );
+ }
+ else
+ {
+ iret = 0;
+ }
+ break;
+ case IDC_AB_CREATE_CUSTOM:
+ {
+ OnAddCustomAnimationFilter();
+ }
+ break;
+ case IDC_AB_FILTERTAB:
+ {
+ int index = m_pFilterTab->getSelectedIndex();
+ if ( index >= 0 )
+ {
+ m_nCurFilter = index;
+ OnFilter();
+ }
+ }
+ break;
+ case IDC_AB_TRAYSCROLL:
+ {
+ if (event->modifiers == SB_THUMBTRACK)
+ {
+ int offset = event->height;
+
+ slScrollbar->setValue( offset );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEUP )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset -= m_nGranularity;
+ offset = max( offset, slScrollbar->getMinValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEDOWN )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset += m_nGranularity;
+ offset = min( offset, slScrollbar->getMaxValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ }
+ break;
+ case IDC_AB_THUMBNAIL_INCREASE:
+ {
+ ThumbnailIncrease();
+ }
+ break;
+ case IDC_AB_THUMBNAIL_DECREASE:
+ {
+ ThumbnailDecrease();
+ }
+ break;
+ case IDC_AB_CONTEXT_CREATEBITMAP:
+ {
+ int current_model = models->GetActiveModelIndex();
+
+ if ( m_nClickedCell >= 0 )
+ {
+ models->RecreateAnimationBitmap( current_model, TranslateSequenceNumber( m_nClickedCell ) );
+ }
+ redraw();
+ }
+ break;
+ case IDC_AB_CONTEXT_CREATEALLBITMAPS:
+ {
+ int current_model = models->GetActiveModelIndex();
+ models->RecreateAllAnimationBitmaps( current_model );
+ redraw();
+ }
+ break;
+ }
+ break;
+ }
+ case mxEvent::MouseDown:
+ {
+ if ( !( event->buttons & mxEvent::MouseRightButton ) )
+ {
+ // Figure out cell #
+ int cell = GetCellUnderPosition( event->x, event->y );
+ if ( cell >= 0 && cell < GetSequenceCount() )
+ {
+ int cx, cy, cw, ch;
+ if ( ComputeRect( cell, cx, cy, cw, ch ) )
+ {
+ m_flDragTime = realtime;
+ m_bDragging = true;
+ m_nDragCell = cell;
+
+ m_nXStart = (short)event->x;
+ m_nYStart = (short)event->y;
+
+ m_rcFocus.left = cx;
+ m_rcFocus.top = cy;
+ m_rcFocus.right = cx + cw;
+ m_rcFocus.bottom = cy + ch - m_nDescriptionHeight;
+
+ POINT pt;
+ pt.x = pt.y = 0;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ OffsetRect( &m_rcFocus, pt.x, pt.y );
+
+ m_rcOrig = m_rcFocus;
+
+ DrawFocusRect();
+
+ Select( cell );
+ m_nClickedCell = cell;
+ }
+ }
+ else
+ {
+ Deselect();
+ redraw();
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ {
+ if ( m_bDragging )
+ {
+ // Draw drag line of some kind
+ DrawFocusRect();
+
+ // update pos
+ m_rcFocus = m_rcOrig;
+ OffsetRect( &m_rcFocus, ( (short)event->x - m_nXStart ),
+ ( (short)event->y - m_nYStart ) );
+
+ DrawFocusRect();
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ iret = 1;
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ SetClickedCell( GetCellUnderPosition( (short)event->x, (short)event->y ) );
+ ShowRightClickMenu( (short)event->x, (short)event->y );
+ return iret;
+ }
+
+ if ( m_bDragging && m_nClickedCell >= 0 )
+ {
+ mstudioseqdesc_t *pseqdesc = GetSeqDesc( m_nClickedCell );
+
+ DrawFocusRect();
+ m_bDragging = false;
+ // See if we let go on top of the choreo view
+
+ // Convert x, y to screen space
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ HWND maybeTool = WindowFromPoint( pt );
+
+ // Now tell choreo view
+ if ( maybeTool && pseqdesc )
+ {
+ if ( IsWindowOrChild( g_pChoreoView, maybeTool ) )
+ {
+ if ( g_pChoreoView->CreateAnimationEvent( pt.x, pt.y, pseqdesc->pszLabel() ) )
+ {
+ return iret;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int width = w2();
+ // int height = h2();
+
+ int ch = GetCaptionHeight() + 10;
+
+ m_pSearchEntry->setBounds( 5, ch, width - 10 - 170, 18 );
+
+ m_pThumbnailIncreaseButton->setBounds( width - 40, 4 + ch, 16, 16 );
+ m_pThumbnailDecreaseButton->setBounds( width - 20, 4 + ch, 16, 16 );
+
+ m_pFilterTab->setBounds( 5, ch + 20, width - 10, 20 );
+
+ m_nTopOffset = 0;
+ RepositionSlider();
+
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ // Figure out cell #
+ POINT pt;
+
+ pt.x = event->x;
+ pt.y = event->y;
+
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ if ( event->height < 0 )
+ {
+ m_nTopOffset = min( m_nTopOffset + 10, slScrollbar->getMaxValue() );
+ }
+ else
+ {
+ m_nTopOffset = max( m_nTopOffset - 10, 0 );
+ }
+ RepositionSlider();
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ case mxEvent::KeyUp:
+ {
+ bool search = false;
+ // int n = 3;
+ if ( event->key == VK_ESCAPE && m_szSearchString[ 0 ] )
+ {
+ m_pSearchEntry->setLabel( "" );
+ m_szSearchString[ 0 ] = 0;
+ m_pFilterTab->select( FILTER_NONE );
+ m_nCurFilter = FILTER_NONE;
+ OnFilter();
+ }
+ else
+ {
+ // Text changed?
+ char sz[ 512 ];
+ m_pSearchEntry->getText( sz, sizeof( sz ) );
+ if ( Q_stricmp( sz, m_szSearchString ) )
+ {
+ Q_strncpy( m_szSearchString, sz, sizeof( m_szSearchString ) );
+ search = true;
+ }
+ }
+
+ if ( search )
+ {
+ if ( Q_strlen( m_szSearchString ) > 0 )
+ {
+ m_pFilterTab->select( FILTER_STRING );
+ m_nCurFilter = FILTER_STRING;
+ }
+ else
+ {
+ m_pFilterTab->select( FILTER_NONE );
+ m_nCurFilter = FILTER_NONE;
+ }
+ OnFilter();
+ }
+ }
+ break;
+ };
+
+ if ( iret )
+ {
+ SetActiveTool( this );
+ }
+ return iret;
+}
+
+// HACK HACK: VS2005 is generating bogus code for this little operation in the function below...
+#pragma optimize( "g", off )
+float roundcycle( float cycle )
+{
+ int rounded = (int)(cycle);
+ float cy2 = cycle - rounded;
+ return cy2;
+}
+#pragma optimize( "", on )
+
+void AnimationBrowser::Think( float dt )
+{
+ if ( !m_bDragging )
+ return;
+
+ if ( m_nClickedCell < 0 )
+ return;
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ int iSequence = TranslateSequenceNumber( m_nClickedCell );
+ float dur = model->GetDuration( iSequence );
+ if ( dur > 0.0f )
+ {
+ float elapsed = (float)realtime - m_flDragTime;
+
+ float flFrameRate = 0.0f;
+ float flGroundSpeed = 0.0f;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float cycle = roundcycle( elapsed * flFrameRate );
+
+ // This should be the only thing on the model!!!
+ model->ClearAnimationLayers();
+
+ // FIXME: shouldn't sequences always be lower priority than gestures?
+ int iLayer = model->GetNewAnimationLayer( 0 );
+ model->SetOverlaySequence( iLayer, iSequence, 1.0f );
+ model->SetOverlayRate( iLayer, cycle, 0.0f );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::ThumbnailIncrease( void )
+{
+ if ( m_nSnapshotWidth + THUMBNAIL_SIZE_STEP <= MAX_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth += THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::ThumbnailDecrease( void )
+{
+ if ( m_nSnapshotWidth - THUMBNAIL_SIZE_STEP >= MIN_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth -= THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AnimationBrowser::RestoreThumbnailSize( void )
+{
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsizeanim;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsizeanim = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ redraw();
+}
+
+void AnimationBrowser::ReloadBitmaps( void )
+{
+ Assert( 0 );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *model -
+// sequence -
+// Output : static bool
+//-----------------------------------------------------------------------------
+static bool IsTypeOfSequence( StudioModel *model, int sequence, char const *typestring )
+{
+ bool match = false;
+
+ if ( !model->GetStudioHdr() )
+ return match;
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ char const *t = pkvAllFaceposer->GetString( "type", "" );
+ if ( t && !Q_stricmp( t, typestring ) )
+ {
+ match = true;
+ }
+ }
+ }
+
+ seqKeyValues->deleteThis();
+
+ return match;
+}
+
+bool AnimationBrowser::SequencePassesFilter( StudioModel *model, int sequence, mstudioseqdesc_t &seqdesc )
+{
+ if (model->IsHidden( sequence ))
+ return false;
+
+ switch ( m_nCurFilter )
+ {
+ default:
+ {
+
+ int offset = m_nCurFilter - FILTER_FIRST_CUSTOM;
+ if ( offset >= 0 && offset < m_CustomAnimationTabs.Count() )
+ {
+ // Find the name
+ CCustomAnim *anim = m_CustomAnimationTabs[ offset ];
+ return anim->HasAnimation( seqdesc.pszLabel() );
+ }
+ return true;
+ }
+ break;
+ case FILTER_NONE:
+ {
+ return true;
+ }
+ break;
+ case FILTER_GESTURES:
+ if ( IsTypeOfSequence( model, sequence, "gesture" ) )
+ {
+ return true;
+ }
+ break;
+ case FILTER_POSTURES:
+ if ( IsTypeOfSequence( model, sequence, "posture" ) )
+ {
+ return true;
+ }
+ break;
+ case FILTER_STRING:
+ if ( Q_stristr( seqdesc.pszLabel(), m_szSearchString ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AnimationBrowser::OnFilter()
+{
+ m_Filtered.RemoveAll();
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ int count = hdr->GetNumSeq();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( i );
+
+ // if it passes the filter, add it
+ if ( SequencePassesFilter( model, i, seqdesc ) )
+ {
+ m_Filtered.AddToTail( i );
+ }
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int AnimationBrowser::GetSequenceCount()
+{
+ return m_Filtered.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// Output : mstudioseqdesc_t
+//-----------------------------------------------------------------------------
+mstudioseqdesc_t *AnimationBrowser::GetSeqDesc( int index )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return NULL;
+
+ index = TranslateSequenceNumber( index );
+
+ if ( index < 0 || index >= hdr->GetNumSeq() )
+ return NULL;
+
+ return &hdr->pSeqdesc( index );
+}
+
+int AnimationBrowser::TranslateSequenceNumber( int index )
+{
+ if ( index < 0 || index >= m_Filtered.Count() )
+ return NULL;
+
+ // Lookup the true index
+ index = m_Filtered[ index ];
+ return index;
+}
+
+void AnimationBrowser::FindCustomFiles( char const *subdir, CUtlVector< FileNameHandle_t >& files )
+{
+ char search[ 512 ];
+ Q_snprintf( search, sizeof( search ), "%s/*.txt", subdir );
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( search, &findHandle );
+ while( pFileName )
+ {
+ if( !filesystem->FindIsDirectory( findHandle ) )
+ {
+ // Strip off the 'sound/' part of the name.
+ char fn[ 512 ];
+ Q_snprintf( fn, sizeof( fn ), "%s/%s", subdir, pFileName );
+
+ FileNameHandle_t fh;
+ fh = filesystem->FindOrAddFileName( fn );
+ files.AddToTail( fh );
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ filesystem->FindClose( findHandle );
+}
+
+void AnimationBrowser::PurgeCustom()
+{
+ for ( int i = 0; i < m_CustomAnimationTabs.Count(); ++i )
+ {
+ if ( m_CustomAnimationTabs[ i ]->m_bDirty )
+ {
+ m_CustomAnimationTabs[ i ]->SaveToFile();
+ }
+ delete m_CustomAnimationTabs[ i ];
+ }
+ m_CustomAnimationTabs.Purge();
+}
+
+void AnimationBrowser::BuildCustomFromFiles( CUtlVector< FileNameHandle_t >& files )
+{
+ PurgeCustom();
+
+ for ( int i = 0; i < files.Count(); ++i )
+ {
+ char fn[ 512 ];
+ if ( !filesystem->String( files[ i ], fn, sizeof( fn ) ) )
+ continue;
+
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+
+ char basename[ 128 ];
+ Q_FileBase( fn, basename, sizeof( basename ) );
+
+ CCustomAnim *anim = new CCustomAnim( files[ i ] );
+ anim->m_ShortName = basename;
+ anim->LoadFromFile();
+
+ m_CustomAnimationTabs.AddToTail( anim );
+ }
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::RenameCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ Q_snprintf( params.m_szDialogTitle, sizeof( params.m_szDialogTitle ), "Custom Animation Group" );
+ Q_strcpy( params.m_szPrompt, "Group Name:" );
+ Q_strcpy( params.m_szInputText, anim->m_ShortName.String() );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( !params.m_szInputText[ 0 ] )
+ return;
+
+ // No change
+ if ( !Q_stricmp( anim->m_ShortName.String(), params.m_szInputText ) )
+ return;
+
+ char fn[ 512 ];
+ if ( !filesystem->String( anim->m_Handle, fn, sizeof( fn ) ) )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ {
+ return;
+ }
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ // Delete the old file
+ filesystem->RemoveFile( fn, "MOD" );
+
+ anim->m_ShortName = params.m_szInputText;
+
+ char basename[ 128 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( fn, sizeof( fn ), "expressions/%s/animation/%s.txt", basename, params.m_szInputText );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ CreatePath( fn );
+
+ anim->m_Handle = filesystem->FindOrAddFileName( fn );
+
+ anim->m_bDirty = true;
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::AddCustomFile( const FileNameHandle_t& handle )
+{
+ char fn[ 512 ];
+ if ( !filesystem->String( handle, fn, sizeof( fn ) ) )
+ return;
+
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ char basename[ 128 ];
+ Q_FileBase( fn, basename, sizeof( basename ) );
+
+ CCustomAnim *anim = new CCustomAnim( handle );
+ anim->m_ShortName = basename;
+ anim->LoadFromFile();
+ anim->m_bDirty = true;
+
+ if ( m_nCurCell != -1 )
+ {
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( hdr )
+ {
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( m_nCurCell );
+ CUtlSymbol sym;
+ sym = seqdesc.pszLabel();
+ anim->m_Animations.AddToTail( sym );
+ }
+ }
+ }
+
+ m_CustomAnimationTabs.AddToTail( anim );
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::DeleteCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ char fn[ 512 ];
+ if ( !filesystem->String( anim->m_Handle, fn, sizeof( fn ) ) )
+ return;
+
+ m_CustomAnimationTabs.Remove( index );
+ filesystem->RemoveFile( fn );
+ delete anim;
+
+ UpdateCustomTabs();
+}
+
+void AnimationBrowser::UpdateCustomTabs()
+{
+ m_pFilterTab->UpdateCustomTabs( m_CustomAnimationTabs );
+}
+
+void AnimationBrowser::OnModelChanged()
+{
+ CUtlVector< FileNameHandle_t > files;
+
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( model )
+ {
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( hdr )
+ {
+ char subdir[ 512 ];
+ char basename[ 512 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( subdir, sizeof( subdir ), "expressions/%s/animation", basename );
+ Q_FixSlashes( subdir );
+ Q_strlower( subdir );
+ FindCustomFiles( subdir, files );
+ }
+ }
+
+ BuildCustomFromFiles( files );
+
+ RestoreThumbnailSize();
+
+ // Just reapply filter
+ OnFilter();
+}
+
+ void AnimationBrowser::OnAddCustomAnimationFilter()
+ {
+ StudioModel *model = models->GetActiveStudioModel();
+ if ( !model )
+ {
+ return;
+ }
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ Q_snprintf( params.m_szDialogTitle, sizeof( params.m_szDialogTitle ), "Custom Animation Group" );
+ Q_strcpy( params.m_szPrompt, "Group Name:" );
+ Q_strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( !params.m_szInputText[ 0 ] )
+ return;
+
+ if ( FindCustomFile( params.m_szInputText ) != -1 )
+ {
+ Warning( "Can't add duplicate tab '%s'\n", params.m_szInputText );
+ return;
+ }
+
+ // Create it
+ char fn[ 512 ];
+ char basename[ 512 ];
+ Q_StripExtension( hdr->pszName(), basename, sizeof( basename ) );
+ Q_snprintf( fn, sizeof( fn ), "expressions/%s/animation/%s.txt", basename, params.m_szInputText );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+ CreatePath( fn );
+
+ FileNameHandle_t fh = filesystem->FindOrAddFileName( fn );
+ AddCustomFile( fh );
+ }
+
+ int AnimationBrowser::FindCustomFile( char const *shortName )
+ {
+ CUtlSymbol search;
+ search = shortName;
+
+ for ( int i = 0; i < m_CustomAnimationTabs.Count(); ++i )
+ {
+ CCustomAnim *anim = m_CustomAnimationTabs[ i ];
+ if ( anim->m_ShortName == search )
+ return i;
+ }
+ return -1;
+ }
+
+void AnimationBrowser::AddAnimationToCustomFile( int index, char const *animationName )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ CUtlSymbol search;
+ search = animationName;
+
+ if ( anim->m_Animations.Find( search ) == anim->m_Animations.InvalidIndex() )
+ {
+ anim->m_Animations.AddToTail( search );
+ anim->m_bDirty = true;
+ }
+
+ OnFilter();
+}
+
+void AnimationBrowser::RemoveAnimationFromCustomFile( int index, char const *animationName )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+
+ CUtlSymbol search;
+ search = animationName;
+
+ int slot = anim->m_Animations.Find( search );
+ if ( slot != anim->m_Animations.InvalidIndex() )
+ {
+ anim->m_Animations.Remove( slot );
+ anim->m_bDirty = true;
+ OnFilter();
+ }
+}
+
+void AnimationBrowser::RemoveAllAnimationsFromCustomFile( int index )
+{
+ if ( index < 0 || index >= m_CustomAnimationTabs.Count() )
+ return;
+
+ CCustomAnim *anim = m_CustomAnimationTabs[ index ];
+ anim->m_Animations.Purge();
+ anim->m_bDirty = true;
+
+ OnFilter();
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/AnimationBrowser.h b/utils/hlfaceposer/AnimationBrowser.h
new file mode 100644
index 0000000..8d3b53e
--- /dev/null
+++ b/utils/hlfaceposer/AnimationBrowser.h
@@ -0,0 +1,187 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( ANIMATIONBROWSER_H )
+#define ANIMATIONBROWSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define IDC_AB_TRAYSCROLL 1001
+#define IDC_AB_THUMBNAIL_INCREASE 1002
+#define IDC_AB_THUMBNAIL_DECREASE 1003
+#define IDC_AB_CONTEXT_CREATEBITMAP 1004
+#define IDC_AB_CONTEXT_CREATEALLBITMAPS 1005
+#define IDC_AB_FILTERTAB 1006
+
+#define IDC_AB_CREATE_CUSTOM 1007
+
+#define IDC_AB_ADDTOGROUPSTART 1100
+#define IDC_AB_ADDTOGROUPEND 1199
+
+#define IDC_AB_REMOVEFROMGROUPSTART 1200
+#define IDC_AB_REMOVEFROMGROUPEND 1299
+
+#define IDC_AB_DELETEGROUPSTART 1300
+#define IDC_AB_DELETEGROUPEND 1399
+
+#define IDC_AB_RENAMEGROUPSTART 1400
+#define IDC_AB_RENAMEGROUPEND 1499
+
+#define COLOR_TRAYBACKGROUND RGB( 240, 240, 220 )
+
+#include "faceposertoolwindow.h"
+#include "StudioModel.h"
+
+class CAnimBrowserTab;
+
+class CCustomAnim
+{
+public:
+ CCustomAnim( const FileNameHandle_t &h )
+ :
+ m_bDirty( false ),
+ m_ShortName( UTL_INVAL_SYMBOL )
+ {
+ m_Handle = h;
+ }
+
+ void LoadFromFile();
+ void SaveToFile();
+
+ bool HasAnimation( char const *search );
+
+ bool m_bDirty;
+ CUtlSymbol m_ShortName;
+ FileNameHandle_t m_Handle;
+ CUtlVector< CUtlSymbol > m_Animations;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class AnimationBrowser : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ enum
+ {
+ FILTER_NONE = 0,
+ FILTER_GESTURES,
+ FILTER_POSTURES,
+ FILTER_STRING,
+ FILTER_FIRST_CUSTOM
+ };
+
+ AnimationBrowser( mxWindow *parent, int id = 0 );
+ virtual ~AnimationBrowser ( void );
+
+ virtual void Shutdown();
+
+ virtual void redraw ();
+ virtual bool PaintBackground( void );
+
+ virtual int handleEvent (mxEvent *event);
+
+ virtual void Think( float dt );
+
+ void ThumbnailIncrease( void );
+ void ThumbnailDecrease( void );
+ void RestoreThumbnailSize( void );
+
+ void Select( int sequence );
+ void Deselect( void );
+
+ void SetCellSize( int cellsize );
+
+ void ReloadBitmaps( void );
+ virtual void OnModelChanged();
+
+ void OnAddCustomAnimationFilter();
+
+private: // Methods
+
+ void OnFilter();
+ bool SequencePassesFilter( StudioModel *model, int sequence, mstudioseqdesc_t &seqdesc );
+
+ int GetSequenceCount();
+ mstudioseqdesc_t *GetSeqDesc( int index );
+ int TranslateSequenceNumber( int index );
+
+ int GetCellUnderPosition( int x, int y );
+
+ bool ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch );
+ int ComputePixelsNeeded( void );
+
+ void RepositionSlider();
+ void SetClickedCell( int cell );
+ void ShowRightClickMenu( int mx, int my );
+
+ void DrawThumbNail( int sequence, CChoreoWidgetDrawHelper& helper,
+ int rcx, int rcy, int rcw, int rch );
+
+ void DrawSequenceFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr );
+ void DrawSequenceDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, int sequence, mstudioseqdesc_t &seqdesc );
+
+ void DrawFocusRect( void );
+
+ // Custom group tab stuff
+ void FindCustomFiles( char const *subdir, CUtlVector< FileNameHandle_t >& files );
+ void AddCustomFile( const FileNameHandle_t& handle );
+ void RenameCustomFile( int index );
+ void DeleteCustomFile( int index );
+ void PurgeCustom();
+ void BuildCustomFromFiles( CUtlVector< FileNameHandle_t >& files );
+ void UpdateCustomTabs();
+ int FindCustomFile( char const *shortName );
+ void AddAnimationToCustomFile( int index, char const *animationName );
+ void RemoveAnimationFromCustomFile( int index, char const *animationName );
+ void RemoveAllAnimationsFromCustomFile( int index );
+
+private: // Data
+
+ mxScrollbar *slScrollbar;
+ CAnimBrowserTab *m_pFilterTab;
+ mxLineEdit *m_pSearchEntry;
+
+ int m_nTopOffset;
+
+ int m_nLastNumAnimations;
+
+ int m_nGranularity;
+
+ int m_nCurCell;
+ int m_nClickedCell;
+
+ // Formatting
+ int m_nButtonSquare;
+
+ int m_nGap;
+ int m_nDescriptionHeight;
+ int m_nSnapshotWidth;
+ int m_nSnapshotHeight;
+
+ bool m_bDragging;
+ RECT m_rcFocus;
+ RECT m_rcOrig;
+ int m_nDragCell;
+ int m_nXStart;
+ int m_nYStart;
+
+ mxButton *m_pThumbnailIncreaseButton;
+ mxButton *m_pThumbnailDecreaseButton;
+
+ CUtlVector< int > m_Filtered;
+ int m_nCurFilter;
+ char m_szSearchString[ 256 ];
+
+ float m_flDragTime;
+
+ CUtlVector< CCustomAnim * > m_CustomAnimationTabs;
+};
+
+extern AnimationBrowser *g_pAnimationBrowserTool;
+
+#endif // ANIMATIONBROWSER_H \ No newline at end of file
diff --git a/utils/hlfaceposer/CloseCaptionTool.cpp b/utils/hlfaceposer/CloseCaptionTool.cpp
new file mode 100644
index 0000000..670439c
--- /dev/null
+++ b/utils/hlfaceposer/CloseCaptionTool.cpp
@@ -0,0 +1,995 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "CloseCaptionTool.h"
+#include "choreowidgetdrawhelper.h"
+#include <vgui/ILocalize.h>
+
+extern vgui::ILocalize *g_pLocalize;
+
+using namespace vgui;
+
+CloseCaptionTool *g_pCloseCaptionTool = 0;
+
+#define STREAM_FONT "Tahoma"
+#define STREAM_POINTSIZE 12
+#define STREAM_LINEHEIGHT ( STREAM_POINTSIZE + 2 )
+#define STREAM_WEIGHT FW_NORMAL
+
+#define CAPTION_LINGER_TIME 1.5f
+// FIXME: Yahn, what is this, it's coded up as a DELAY before when the closed caption is displayed. That seems odd.
+#define CAPTION_PREDISPLAY_TIME 0.0f // 0.5f
+
+
+
+
+// A work unit is a pre-processed chunk of CC text to display
+// Any state changes (font/color/etc) cause a new work unit to be precomputed
+// Moving onto a new line also causes a new Work Unit
+// The width and height are stored so that layout can be quickly recomputed each frame
+class CCloseCaptionWorkUnit
+{
+public:
+ CCloseCaptionWorkUnit();
+ ~CCloseCaptionWorkUnit();
+
+ void SetWidth( int w );
+ int GetWidth() const;
+
+ void SetHeight( int h );
+ int GetHeight() const;
+
+ void SetPos( int x, int y );
+ void GetPos( int& x, int &y ) const;
+
+ void SetBold( bool bold );
+ bool GetBold() const;
+
+ void SetItalic( bool ital );
+ bool GetItalic() const;
+
+ void SetStream( const wchar_t *stream );
+ const wchar_t *GetStream() const;
+
+ void SetColor( COLORREF clr );
+ COLORREF GetColor() const;
+
+ int GetFontNumber() const
+ {
+ return CloseCaptionTool::GetFontNumber( m_bBold, m_bItalic );
+ }
+
+ void Dump()
+ {
+ char buf[ 2048 ];
+ g_pLocalize->ConvertUnicodeToANSI( GetStream(), buf, sizeof( buf ) );
+
+ Msg( "x = %i, y = %i, w = %i h = %i text %s\n", m_nX, m_nY, m_nWidth, m_nHeight, buf );
+ }
+
+private:
+
+ int m_nX;
+ int m_nY;
+ int m_nWidth;
+ int m_nHeight;
+
+ bool m_bBold;
+ bool m_bItalic;
+ wchar_t *m_pszStream;
+ COLORREF m_Color;
+};
+
+CCloseCaptionWorkUnit::CCloseCaptionWorkUnit() :
+ m_nWidth(0),
+ m_nHeight(0),
+ m_bBold(false),
+ m_bItalic(false),
+ m_pszStream(0),
+ m_Color( RGB( 255, 255, 255 ) )
+{
+}
+
+CCloseCaptionWorkUnit::~CCloseCaptionWorkUnit()
+{
+ delete[] m_pszStream;
+ m_pszStream = NULL;
+}
+
+void CCloseCaptionWorkUnit::SetWidth( int w )
+{
+ m_nWidth = w;
+}
+
+int CCloseCaptionWorkUnit::GetWidth() const
+{
+ return m_nWidth;
+}
+
+void CCloseCaptionWorkUnit::SetHeight( int h )
+{
+ m_nHeight = h;
+}
+
+int CCloseCaptionWorkUnit::GetHeight() const
+{
+ return m_nHeight;
+}
+
+void CCloseCaptionWorkUnit::SetPos( int x, int y )
+{
+ m_nX = x;
+ m_nY = y;
+}
+
+void CCloseCaptionWorkUnit::GetPos( int& x, int &y ) const
+{
+ x = m_nX;
+ y = m_nY;
+}
+
+void CCloseCaptionWorkUnit::SetBold( bool bold )
+{
+ m_bBold = bold;
+}
+
+bool CCloseCaptionWorkUnit::GetBold() const
+{
+ return m_bBold;
+}
+
+void CCloseCaptionWorkUnit::SetItalic( bool ital )
+{
+ m_bItalic = ital;
+}
+
+bool CCloseCaptionWorkUnit::GetItalic() const
+{
+ return m_bItalic;
+}
+
+void CCloseCaptionWorkUnit::SetStream( const wchar_t *stream )
+{
+ delete[] m_pszStream;
+ m_pszStream = NULL;
+
+ int len = wcslen( stream );
+ Assert( len < 4096 );
+ m_pszStream = new wchar_t[ len + 1 ];
+ wcsncpy( m_pszStream, stream, len );
+ m_pszStream[ len ] = L'\0';
+}
+
+const wchar_t *CCloseCaptionWorkUnit::GetStream() const
+{
+ return m_pszStream ? m_pszStream : L"";
+}
+
+void CCloseCaptionWorkUnit::SetColor( COLORREF clr )
+{
+ m_Color = clr;
+}
+
+COLORREF CCloseCaptionWorkUnit::GetColor() const
+{
+ return m_Color;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CCloseCaptionItem
+{
+public:
+ CCloseCaptionItem(
+ wchar_t *stream,
+ float timetolive,
+ float predisplay,
+ bool valid
+ ) :
+ m_flTimeToLive( 0.0f ),
+ m_bValid( false ),
+ m_nTotalWidth( 0 ),
+ m_nTotalHeight( 0 ),
+ m_bSizeComputed( false )
+ {
+ SetStream( stream );
+ SetTimeToLive( timetolive );
+ SetPreDisplayTime( CAPTION_PREDISPLAY_TIME + predisplay );
+ m_bValid = valid;
+ m_bSizeComputed = false;
+ }
+
+ CCloseCaptionItem( const CCloseCaptionItem& src )
+ {
+ SetStream( src.m_szStream );
+ m_flTimeToLive = src.m_flTimeToLive;
+ m_bValid = src.m_bValid;
+ }
+
+ ~CCloseCaptionItem( void )
+ {
+ while ( m_Work.Count() > 0 )
+ {
+ CCloseCaptionWorkUnit *unit = m_Work[ 0 ];
+ m_Work.Remove( 0 );
+ delete unit;
+ }
+ }
+
+ void SetStream( const wchar_t *stream)
+ {
+ wcsncpy( m_szStream, stream, sizeof( m_szStream ) / sizeof( wchar_t ) );
+ }
+
+ const wchar_t *GetStream() const
+ {
+ return m_szStream;
+ }
+
+ void SetTimeToLive( float ttl )
+ {
+ m_flTimeToLive = ttl;
+ }
+
+ float GetTimeToLive( void ) const
+ {
+ return m_flTimeToLive;
+ }
+
+ bool IsValid() const
+ {
+ return m_bValid;
+ }
+
+ void SetHeight( int h )
+ {
+ m_nTotalHeight = h;
+ }
+ int GetHeight() const
+ {
+ return m_nTotalHeight;
+ }
+ void SetWidth( int w )
+ {
+ m_nTotalWidth = w;
+ }
+ int GetWidth() const
+ {
+ return m_nTotalWidth;
+ }
+
+ void AddWork( CCloseCaptionWorkUnit *unit )
+ {
+ m_Work.AddToTail( unit );
+ }
+
+ int GetNumWorkUnits() const
+ {
+ return m_Work.Count();
+ }
+
+ CCloseCaptionWorkUnit *GetWorkUnit( int index )
+ {
+ Assert( index >= 0 && index < m_Work.Count() );
+
+ return m_Work[ index ];
+ }
+
+ void SetSizeComputed( bool computed )
+ {
+ m_bSizeComputed = computed;
+ }
+
+ bool GetSizeComputed() const
+ {
+ return m_bSizeComputed;
+ }
+
+ void SetPreDisplayTime( float t )
+ {
+ m_flPreDisplayTime = t;
+ }
+
+ float GetPreDisplayTime() const
+ {
+ return m_flPreDisplayTime;
+ }
+private:
+ wchar_t m_szStream[ 256 ];
+
+ float m_flPreDisplayTime;
+ float m_flTimeToLive;
+ bool m_bValid;
+ int m_nTotalWidth;
+ int m_nTotalHeight;
+
+ bool m_bSizeComputed;
+
+ CUtlVector< CCloseCaptionWorkUnit * > m_Work;
+};
+
+ICloseCaptionManager *closecaptionmanager = NULL;
+
+CloseCaptionTool::CloseCaptionTool( mxWindow *parent )
+: IFacePoserToolWindow( "CloseCaptionTool", "Close Caption" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_nLastItemCount = -1;
+ closecaptionmanager = this;
+
+ m_hFonts[ CCFONT_NORMAL ] = CreateFont(
+ -STREAM_POINTSIZE,
+ 0,
+ 0,
+ 0,
+ STREAM_WEIGHT,
+ FALSE,
+ FALSE,
+ FALSE,
+ DEFAULT_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ STREAM_FONT );
+
+ m_hFonts[ CCFONT_ITALIC ] = CreateFont(
+ -STREAM_POINTSIZE,
+ 0,
+ 0,
+ 0,
+ STREAM_WEIGHT,
+ TRUE,
+ FALSE,
+ FALSE,
+ DEFAULT_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ STREAM_FONT );
+
+ m_hFonts[ CCFONT_BOLD ] = CreateFont(
+ -STREAM_POINTSIZE,
+ 0,
+ 0,
+ 0,
+ 700,
+ FALSE,
+ FALSE,
+ FALSE,
+ DEFAULT_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ STREAM_FONT );
+
+ m_hFonts[ CCFONT_ITALICBOLD ] = CreateFont(
+ -STREAM_POINTSIZE,
+ 0,
+ 0,
+ 0,
+ 700,
+ TRUE,
+ FALSE,
+ FALSE,
+ DEFAULT_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ STREAM_FONT );
+}
+
+CloseCaptionTool::~CloseCaptionTool( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CloseCaptionTool::Think( float dt )
+{
+ int c = m_Items.Count();
+ int i;
+
+ // Pass one decay all timers
+ for ( i = 0 ; i < c ; ++i )
+ {
+ CCloseCaptionItem *item = m_Items[ i ];
+
+ float predisplay = item->GetPreDisplayTime();
+ if ( predisplay > 0.0f )
+ {
+ predisplay -= dt;
+ predisplay = max( 0.0f, predisplay );
+ item->SetPreDisplayTime( predisplay );
+ }
+ else
+ {
+ // remove time from actual playback
+ float ttl = item->GetTimeToLive();
+ ttl -= dt;
+ ttl = max( 0.0f, ttl );
+ item->SetTimeToLive( ttl );
+ }
+ }
+
+ // Pass two, remove from head until we get to first item with time remaining
+ bool foundfirstnondeletion = false;
+ for ( i = 0 ; i < c ; ++i )
+ {
+ CCloseCaptionItem *item = m_Items[ i ];
+
+ // Skip items not yet showing...
+ float predisplay = item->GetPreDisplayTime();
+ if ( predisplay > 0.0f )
+ {
+ continue;
+ }
+
+ float ttl = item->GetTimeToLive();
+ if ( ttl > 0.0f )
+ {
+ foundfirstnondeletion = true;
+ continue;
+ }
+
+ // Skip the remainder of the items after we find the first/oldest active item
+ if ( foundfirstnondeletion )
+ {
+ continue;
+ }
+
+ delete item;
+ m_Items.Remove( i );
+ --i;
+ --c;
+ }
+
+ if ( m_Items.Count() != m_nLastItemCount )
+ {
+ redraw();
+ }
+ m_nLastItemCount = m_Items.Count();
+}
+
+struct VisibleStreamItem
+{
+ int height;
+ CCloseCaptionItem *item;
+};
+
+void CloseCaptionTool::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ RECT rcOutput;
+ drawHelper.GetClientRect( rcOutput );
+
+ RECT rcText = rcOutput;
+ drawHelper.DrawFilledRect( RGB( 0, 0, 0 ), rcText );
+ drawHelper.DrawOutlinedRect( RGB( 200, 245, 150 ), PS_SOLID, 2, rcText );
+ InflateRect( &rcText, -4, 0 );
+
+ int avail_width = rcText.right - rcText.left;
+
+ int totalheight = 0;
+ int i;
+ CUtlVector< VisibleStreamItem > visibleitems;
+ int c = m_Items.Count();
+ for ( i = 0; i < c; i++ )
+ {
+ CCloseCaptionItem *item = m_Items[ i ];
+
+ // Not ready for display yet.
+ if ( item->GetPreDisplayTime() > 0.0f )
+ {
+ continue;
+ }
+
+ if ( !item->GetSizeComputed() )
+ {
+ ComputeStreamWork( drawHelper, avail_width, item );
+ }
+
+ int itemheight = item->GetHeight();
+
+ totalheight += itemheight;
+
+ VisibleStreamItem si;
+ si.height = itemheight;
+ si.item = item;
+
+ visibleitems.AddToTail( si );
+ }
+
+ rcText.bottom -= 2;
+ rcText.top = rcText.bottom - totalheight;
+
+ // Now draw them
+ c = visibleitems.Count();
+ for ( i = 0; i < c; i++ )
+ {
+ VisibleStreamItem *si = &visibleitems[ i ];
+
+ int height = si->height;
+ CCloseCaptionItem *item = si->item;
+
+ rcText.bottom = rcText.top + height;
+
+ DrawStream( drawHelper, rcText, item );
+
+ OffsetRect( &rcText, 0, height );
+
+ if ( rcText.top >= rcOutput.bottom )
+ break;
+ }
+}
+
+int CloseCaptionTool::handleEvent( mxEvent *event )
+{
+ //MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ return iret;
+}
+
+bool CloseCaptionTool::PaintBackground()
+{
+ redraw();
+ return false;
+}
+
+void CloseCaptionTool::Reset( void )
+{
+ while ( m_Items.Count() > 0 )
+ {
+ CCloseCaptionItem *i = m_Items[ 0 ];
+ delete i;
+ m_Items.Remove( 0 );
+ }
+}
+
+void CloseCaptionTool::Process( char const *tokenname, float duration, int languageid )
+{
+ bool valid = true;
+ wchar_t stream[ 256 ];
+ if ( !LookupUnicodeText( languageid, tokenname, stream, sizeof( stream ) / sizeof( wchar_t ) ) )
+ {
+ valid = false;
+ g_pLocalize->ConvertANSIToUnicode( va( "--> Missing Caption[%s]", tokenname ), stream, sizeof( stream ) );
+ }
+
+ if ( !wcsncmp( stream, L"!!!", wcslen( L"!!!" ) ) )
+ {
+ // It's in the text file, but hasn't been translated...
+ valid = false;
+ }
+
+ // Nothing to do...
+ if ( wcslen( stream ) == 0 )
+ {
+ return;
+ }
+
+ float delay = 0.0f;
+
+ wchar_t phrase[ 1024 ];
+ wchar_t *out = phrase;
+
+ for ( const wchar_t *curpos = stream; curpos && *curpos != L'\0'; ++curpos )
+ {
+ wchar_t cmd[ 256 ];
+ wchar_t args[ 256 ];
+
+ if ( SplitCommand( &curpos, cmd, args ) )
+ {
+ if ( !wcscmp( cmd, L"delay" ) )
+ {
+
+ // End current phrase
+ *out = L'\0';
+
+ if ( wcslen( phrase ) > 0 )
+ {
+ CCloseCaptionItem *item = new CCloseCaptionItem( phrase, duration + CAPTION_LINGER_TIME, delay, valid );
+ m_Items.AddToTail( item );
+ }
+
+ // Start new phrase
+ out = phrase;
+
+ // Delay must be positive
+ delay = max( 0.0f, (float)wcstod( args, NULL ) );
+
+ continue;
+ }
+ }
+
+ *out++ = *curpos;
+ }
+
+ // End final phrase, if any
+ *out = L'\0';
+ if ( wcslen( phrase ) > 0 )
+ {
+ CCloseCaptionItem *item = new CCloseCaptionItem( phrase, duration + CAPTION_LINGER_TIME, delay, valid );
+ m_Items.AddToTail( item );
+ }
+}
+
+bool CloseCaptionTool::LookupUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count )
+{
+ wchar_t *outstr = g_pLocalize->Find( token );
+ if ( !outstr )
+ {
+ wcsncpy( outbuf, L"<can't find entry>", count );
+ return false;
+ }
+
+ wcsncpy( outbuf, outstr, count );
+
+ return true;
+}
+
+bool CloseCaptionTool::LookupStrippedUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count )
+{
+ wchar_t *outstr = g_pLocalize->Find( token );
+ if ( !outstr )
+ {
+ wcsncpy( outbuf, L"<can't find entry>", count );
+ return false;
+ }
+
+ const wchar_t *curpos = outstr;
+ wchar_t *out = outbuf;
+ size_t outlen = 0;
+
+ for ( ;
+ curpos && *curpos != L'\0' && outlen < count;
+ ++curpos )
+ {
+ wchar_t cmd[ 256 ];
+ wchar_t args[ 256 ];
+
+ if ( SplitCommand( &curpos, cmd, args ) )
+ {
+ continue;
+ }
+
+ *out++ = *curpos;
+ ++outlen;
+ }
+
+ *out = L'\0';
+
+ return true;
+}
+
+bool CloseCaptionTool::SplitCommand( wchar_t const **ppIn, wchar_t *cmd, wchar_t *args ) const
+{
+ const wchar_t *in = *ppIn;
+ const wchar_t *oldin = in;
+
+ if ( in[0] != L'<' )
+ {
+ *ppIn += ( oldin - in );
+ return false;
+ }
+
+ args[ 0 ] = 0;
+ cmd[ 0 ]= 0;
+ wchar_t *out = cmd;
+ in++;
+ while ( *in != L'\0' && *in != L':' && *in != L'>' && !isspace( *in ) )
+ {
+ *out++ = *in++;
+ }
+ *out = L'\0';
+
+ if ( *in != L':' )
+ {
+ *ppIn += ( in - oldin );
+ return true;
+ }
+
+ in++;
+ out = args;
+ while ( *in != L'\0' && *in != L'>' )
+ {
+ *out++ = *in++;
+ }
+ *out = L'\0';
+
+ //if ( *in == L'>' )
+ // in++;
+
+ *ppIn += ( in - oldin );
+ return true;
+}
+
+struct WorkUnitParams
+{
+ WorkUnitParams()
+ {
+ Q_memset( stream, 0, sizeof( stream ) );
+ out = stream;
+ x = 0;
+ y = 0;
+ width = 0;
+ bold = italic = false;
+ clr = RGB( 255, 255, 255 );
+ newline = false;
+ }
+
+ ~WorkUnitParams()
+ {
+ }
+
+ void Finalize()
+ {
+ *out = L'\0';
+ }
+
+ void Next()
+ {
+ // Restart output
+ Q_memset( stream, 0, sizeof( stream ) );
+ out = stream;
+
+ x += width;
+
+ width = 0;
+ // Leave bold, italic and color alone!!!
+
+ if ( newline )
+ {
+ newline = false;
+ x = 0;
+ y += STREAM_LINEHEIGHT;
+ }
+ }
+
+ int GetFontNumber()
+ {
+ return CloseCaptionTool::GetFontNumber( bold, italic );
+ }
+
+ wchar_t stream[ 1024 ];
+ wchar_t *out;
+
+ int x;
+ int y;
+ int width;
+ bool bold;
+ bool italic;
+ COLORREF clr;
+ bool newline;
+};
+
+void CloseCaptionTool::AddWorkUnit( CCloseCaptionItem *item,
+ WorkUnitParams& params )
+{
+ params.Finalize();
+
+ if ( wcslen( params.stream ) > 0 )
+ {
+ CCloseCaptionWorkUnit *wu = new CCloseCaptionWorkUnit();
+
+ wu->SetStream( params.stream );
+ wu->SetColor( params.clr );
+ wu->SetBold( params.bold );
+ wu->SetItalic( params.italic );
+ wu->SetWidth( params.width );
+ wu->SetHeight( STREAM_LINEHEIGHT );
+ wu->SetPos( params.x, params.y );
+
+
+ int curheight = item->GetHeight();
+ int curwidth = item->GetWidth();
+
+ curheight = max( curheight, params.y + wu->GetHeight() );
+ curwidth = max( curwidth, params.x + params.width );
+
+ item->SetHeight( curheight );
+ item->SetWidth( curwidth );
+
+ // Add it
+ item->AddWork( wu );
+
+ params.Next();
+ }
+}
+
+void CloseCaptionTool::ComputeStreamWork( CChoreoWidgetDrawHelper &helper, int available_width, CCloseCaptionItem *item )
+{
+ // Start with a clean param block
+ WorkUnitParams params;
+
+ const wchar_t *curpos = item->GetStream();
+
+ CUtlVector< COLORREF > colorStack;
+
+ for ( ; curpos && *curpos != L'\0'; ++curpos )
+ {
+ wchar_t cmd[ 256 ];
+ wchar_t args[ 256 ];
+
+ if ( SplitCommand( &curpos, cmd, args ) )
+ {
+ if ( !wcscmp( cmd, L"cr" ) )
+ {
+ params.newline = true;
+ AddWorkUnit( item, params);
+ }
+ else if ( !wcscmp( cmd, L"clr" ) )
+ {
+ AddWorkUnit( item, params );
+
+ if ( args[0] == 0 && colorStack.Count()>= 2)
+ {
+ colorStack.Remove( colorStack.Count() - 1 );
+ params.clr = colorStack[ colorStack.Count() - 1 ];
+ }
+ else
+ {
+ int r = 0, g = 0, b = 0;
+ COLORREF newcolor;
+ if ( 3 == swscanf( args, L"%i,%i,%i", &r, &g, &b ) )
+ {
+ newcolor = RGB( r, g, b );
+ colorStack.AddToTail( newcolor );
+ params.clr = colorStack[ colorStack.Count() - 1 ];
+ }
+ }
+ }
+ else if ( !wcscmp( cmd, L"playerclr" ) )
+ {
+ AddWorkUnit( item, params );
+
+ if ( args[0] == 0 && colorStack.Count()>= 2)
+ {
+ colorStack.Remove( colorStack.Count() - 1 );
+ params.clr = colorStack[ colorStack.Count() - 1 ];
+ }
+ else
+ {
+ // player and npc color selector
+ // e.g.,. 255,255,255:200,200,200
+ int pr = 0, pg = 0, pb = 0, nr = 0, ng = 0, nb = 0;
+ COLORREF newcolor;
+ if ( 6 == swscanf( args, L"%i,%i,%i:%i,%i,%i", &pr, &pg, &pb, &nr, &ng, &nb ) )
+ {
+ // FIXME: nothing in .vcds is ever from the player...
+ newcolor = /*item->IsFromPlayer()*/ false ? RGB( pr, pg, pb ) : RGB( nr, ng, nb );
+ colorStack.AddToTail( newcolor );
+ params.clr = colorStack[ colorStack.Count() - 1 ];
+ }
+ }
+ }
+ else if ( !wcscmp( cmd, L"I" ) )
+ {
+ AddWorkUnit( item, params );
+ params.italic = !params.italic;
+ }
+ else if ( !wcscmp( cmd, L"B" ) )
+ {
+ AddWorkUnit( item, params );
+ params.bold = !params.bold;
+ }
+
+ continue;
+ }
+
+ HFONT useF = m_hFonts[ params.GetFontNumber() ];
+
+ int w = helper.CalcTextWidthW( useF, L"%c", *curpos );
+
+ if ( ( params.x + params.width ) + w > available_width )
+ {
+ params.newline = true;
+ AddWorkUnit( item, params );
+ }
+ *params.out++ = *curpos;
+ params.width += w;
+ }
+
+ // Add the final unit.
+ params.newline = true;
+ AddWorkUnit( item, params );
+
+ item->SetSizeComputed( true );
+
+ // DumpWork( item );
+}
+
+void CloseCaptionTool:: DumpWork( CCloseCaptionItem *item )
+{
+ int c = item->GetNumWorkUnits();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CCloseCaptionWorkUnit *wu = item->GetWorkUnit( i );
+ wu->Dump();
+ }
+}
+
+void CloseCaptionTool::DrawStream( CChoreoWidgetDrawHelper &helper, RECT &rcText, CCloseCaptionItem *item )
+{
+ int c = item->GetNumWorkUnits();
+
+ RECT rcOut;
+ rcOut.left = rcText.left;
+
+ for ( int i = 0 ; i < c; ++i )
+ {
+ int x = 0;
+ int y = 0;
+
+ CCloseCaptionWorkUnit *wu = item->GetWorkUnit( i );
+
+ HFONT useF = m_hFonts[ wu->GetFontNumber() ];
+
+ wu->GetPos( x, y );
+
+ rcOut.left = rcText.left + x;
+ rcOut.right = rcOut.left + wu->GetWidth();
+ rcOut.top = rcText.top + y;
+ rcOut.bottom = rcOut.top + wu->GetHeight();
+
+ COLORREF useColor = wu->GetColor();
+
+ if ( !item->IsValid() )
+ {
+ useColor = RGB( 255, 255, 255 );
+ rcOut.right += 2;
+ helper.DrawFilledRect( RGB( 100, 100, 40 ), rcOut );
+ }
+
+ helper.DrawColoredTextW( useF, useColor,
+ rcOut, L"%s", wu->GetStream() );
+
+ }
+}
+
+int CloseCaptionTool::GetFontNumber( bool bold, bool italic )
+{
+ if ( bold || italic )
+ {
+ if( bold && italic )
+ {
+ return CloseCaptionTool::CCFONT_ITALICBOLD;
+ }
+
+ if ( bold )
+ {
+ return CloseCaptionTool::CCFONT_BOLD;
+ }
+
+ if ( italic )
+ {
+ return CloseCaptionTool::CCFONT_ITALIC;
+ }
+ }
+
+ return CloseCaptionTool::CCFONT_NORMAL;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/CloseCaptionTool.h b/utils/hlfaceposer/CloseCaptionTool.h
new file mode 100644
index 0000000..d16f23d
--- /dev/null
+++ b/utils/hlfaceposer/CloseCaptionTool.h
@@ -0,0 +1,74 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CLOSECAPTIONTOOL_H
+#define CLOSECAPTIONTOOL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "utlvector.h"
+#include "faceposertoolwindow.h"
+#include "iclosecaptionmanager.h"
+
+class CCloseCaptionItem;
+struct WorkUnitParams;
+
+class CloseCaptionTool : public mxWindow, public IFacePoserToolWindow, public ICloseCaptionManager
+{
+public:
+ // Construction
+ CloseCaptionTool( mxWindow *parent );
+ ~CloseCaptionTool( void );
+
+ // ICloseCaptionManager
+ virtual void Reset( void );
+ virtual void Process( char const *tokenname, float duration, int languageid );
+ virtual bool LookupUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count );
+ virtual bool LookupStrippedUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count );
+
+ // End ICloseCaptionManager
+
+ virtual void Think( float dt );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground();
+
+ enum
+ {
+ CCFONT_NORMAL = 0,
+ CCFONT_ITALIC,
+ CCFONT_BOLD,
+ CCFONT_ITALICBOLD
+ };
+
+ static int GetFontNumber( bool bold, bool italic );
+
+private:
+ void ComputeStreamWork( CChoreoWidgetDrawHelper &helper, int available_width, CCloseCaptionItem *item );
+ void DrawStream( CChoreoWidgetDrawHelper &helper, RECT &rcText, CCloseCaptionItem *item );
+ bool SplitCommand( const wchar_t **in, wchar_t *cmd, wchar_t *args ) const;
+
+ void ParseCloseCaptionStream( const wchar_t *in, int available_width );
+
+ void DumpWork( CCloseCaptionItem *item );
+
+ void AddWorkUnit(
+ CCloseCaptionItem *item,
+ WorkUnitParams& params );
+
+ CUtlVector< CCloseCaptionItem * > m_Items;
+
+ HFONT m_hFonts[ 4 ]; // normal, italic, bold, bold + italic
+
+ int m_nLastItemCount;
+};
+
+extern CloseCaptionTool *g_pCloseCaptionTool;
+
+#endif // CLOSECAPTIONTOOL_H
diff --git a/utils/hlfaceposer/EdgeProperties.cpp b/utils/hlfaceposer/EdgeProperties.cpp
new file mode 100644
index 0000000..fe28498
--- /dev/null
+++ b/utils/hlfaceposer/EdgeProperties.cpp
@@ -0,0 +1,271 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EdgeProperties.h"
+#include "mdlviewer.h"
+#include "hlfaceposer.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "expressions.h"
+#include "choreoactor.h"
+#include "ifaceposersound.h"
+#include "expclass.h"
+#include "scriplib.h"
+
+static CEdgePropertiesParams g_Params;
+
+void CEdgePropertiesParams::SetFromFlexTrack( CFlexAnimationTrack *track )
+{
+ for ( int i = 0 ; i < 2; ++i )
+ {
+ m_bActive[ i ] = track->IsEdgeActive( i == 0 ? true : false );
+
+ int curveType = 0;
+ track->GetEdgeInfo( i == 0 ? true : false, curveType, m_flValue[ i ] );
+
+ if ( i == 0 )
+ {
+ m_InterpolatorType[ i ] = GET_RIGHT_CURVE( curveType );
+ }
+ else
+ {
+ m_InterpolatorType[ i ] = GET_LEFT_CURVE( curveType );
+ }
+ }
+}
+
+void CEdgePropertiesParams::ApplyToTrack( CFlexAnimationTrack *track )
+{
+ for ( int i = 0 ; i < 2; ++i )
+ {
+ track->SetEdgeActive( i == 0 ? true : false, m_bActive[ i ] );
+
+ int curveType = 0;
+ if ( i == 0 )
+ {
+ curveType = MAKE_CURVE_TYPE( 0, m_InterpolatorType[ i ] );
+ }
+ else
+ {
+ curveType = MAKE_CURVE_TYPE( m_InterpolatorType[ i ], 0 );
+ }
+
+ track->SetEdgeInfo( i == 0 ? true : false, curveType, m_flValue[ i ] );
+ }
+}
+
+void CEdgePropertiesParams::SetFromCurve( CCurveData *ramp )
+{
+ for ( int i = 0 ; i < 2; ++i )
+ {
+ m_bActive[ i ] = ramp->IsEdgeActive( i == 0 ? true : false );
+
+ int curveType = 0;
+ ramp->GetEdgeInfo( i == 0 ? true : false, curveType, m_flValue[ i ] );
+
+ if ( i == 0 )
+ {
+ m_InterpolatorType[ i ] = GET_RIGHT_CURVE( curveType );
+ }
+ else
+ {
+ m_InterpolatorType[ i ] = GET_LEFT_CURVE( curveType );
+ }
+ }
+}
+
+void CEdgePropertiesParams::ApplyToCurve( CCurveData *ramp )
+{
+ for ( int i = 0 ; i < 2; ++i )
+ {
+ ramp->SetEdgeActive( i == 0 ? true : false, m_bActive[ i ] );
+
+ int curveType = 0;
+ if ( i == 0 )
+ {
+ curveType = MAKE_CURVE_TYPE( 0, m_InterpolatorType[ i ] );
+ }
+ else
+ {
+ curveType = MAKE_CURVE_TYPE( m_InterpolatorType[ i ], 0 );
+ }
+
+ ramp->SetEdgeInfo( i == 0 ? true : false, curveType, m_flValue[ i ] );
+ }
+}
+
+
+static void PopulateCurveType( HWND control, CEdgePropertiesParams *params, bool isLeftEdge )
+{
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ for ( int i = 0; i < NUM_INTERPOLATE_TYPES; ++i )
+ {
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)Interpolator_NameForInterpolator( i, true ) );
+ }
+
+ SendMessage( control, CB_SETCURSEL , params->m_InterpolatorType[ isLeftEdge ? 0 : 1 ], 0 );
+
+}
+
+static void Reset( HWND hwndDlg, bool left )
+{
+ SendMessage( GetDlgItem( hwndDlg, left ? IDC_LEFT_ACTIVE : IDC_RIGHT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM )BST_UNCHECKED,
+ ( LPARAM )0 );
+ SendMessage( GetDlgItem( hwndDlg, left ? IDC_LEFT_CURVETYPE : IDC_RIGHT_CURVETYPE ), CB_SETCURSEL, 0, 0 );
+ SetDlgItemText( hwndDlg, left ? IDC_LEFT_ZEROVALUE : IDC_RIGHT_ZEROVALUE, "0.0" );
+ SendMessage( GetDlgItem( hwndDlg, IDC_HOLD_OUT ), BM_SETCHECK,
+ ( WPARAM )BST_UNCHECKED,
+ ( LPARAM )0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EdgePropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ PopulateCurveType( GetDlgItem( hwndDlg, IDC_LEFT_CURVETYPE ), &g_Params, true );
+ PopulateCurveType( GetDlgItem( hwndDlg, IDC_RIGHT_CURVETYPE ), &g_Params, false );
+
+ SetDlgItemText( hwndDlg, IDC_LEFT_ZEROVALUE, va( "%f", g_Params.m_flValue[ 0 ] ) );
+ SetDlgItemText( hwndDlg, IDC_RIGHT_ZEROVALUE, va( "%f", g_Params.m_flValue[ 1 ] ) );
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_LEFT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bActive[ 0 ] ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_RIGHT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bActive[ 1 ] ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_LEFT_ZEROVALUE ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_LEFT_RESET:
+ {
+ Reset( hwndDlg, true );
+ }
+ break;
+ case IDC_RIGHT_RESET:
+ {
+ Reset( hwndDlg, false );
+ }
+ break;
+ case IDC_LEFT_CURVETYPE:
+ {
+ if ( HIWORD( wParam ) == CBN_SELCHANGE )
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_LEFT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) BST_CHECKED,
+ ( LPARAM )0 );
+ }
+ }
+ break;
+ case IDC_RIGHT_CURVETYPE:
+ {
+ if ( HIWORD( wParam ) == CBN_SELCHANGE )
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_RIGHT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) BST_CHECKED,
+ ( LPARAM )0 );
+ }
+ }
+ break;
+ case IDC_LEFT_ZEROVALUE:
+ {
+ if ( HIWORD( wParam ) == EN_CHANGE )
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_LEFT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) BST_CHECKED,
+ ( LPARAM )0 );
+ }
+ }
+ break;
+ case IDC_RIGHT_ZEROVALUE:
+ {
+ if ( HIWORD( wParam ) == EN_CHANGE )
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_RIGHT_ACTIVE ), BM_SETCHECK,
+ ( WPARAM ) BST_CHECKED,
+ ( LPARAM )0 );
+ }
+ }
+ break;
+ case IDOK:
+ {
+ char sz[ 64 ];
+ GetDlgItemText( hwndDlg, IDC_LEFT_ZEROVALUE, sz, sizeof( sz ) );
+ g_Params.m_flValue[ 0 ] = clamp( Q_atof( sz ), 0.0f, 1.0f );
+ GetDlgItemText( hwndDlg, IDC_RIGHT_ZEROVALUE, sz, sizeof( sz ) );
+ g_Params.m_flValue[ 1 ] = clamp( Q_atof( sz ), 0.0f, 1.0f );
+
+ g_Params.m_bActive[ 0 ] = SendMessage( GetDlgItem( hwndDlg, IDC_LEFT_ACTIVE ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ g_Params.m_bActive[ 1 ] = SendMessage( GetDlgItem( hwndDlg, IDC_RIGHT_ACTIVE ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+
+ int interpolatorType;
+ interpolatorType = SendMessage( GetDlgItem( hwndDlg, IDC_LEFT_CURVETYPE ), CB_GETCURSEL, 0, 0 );
+ if ( interpolatorType != CB_ERR )
+ {
+ g_Params.m_InterpolatorType[ 0 ] = interpolatorType;
+ }
+ interpolatorType = SendMessage( GetDlgItem( hwndDlg, IDC_RIGHT_CURVETYPE ), CB_GETCURSEL, 0, 0 );
+ if ( interpolatorType != CB_ERR )
+ {
+ g_Params.m_InterpolatorType[ 1 ] = interpolatorType;
+ }
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EdgeProperties( CEdgePropertiesParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EDGEPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EdgePropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/EdgeProperties.h b/utils/hlfaceposer/EdgeProperties.h
new file mode 100644
index 0000000..d97b477
--- /dev/null
+++ b/utils/hlfaceposer/EdgeProperties.h
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EDGEPROPERTIES_H
+#define EDGEPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CCurveData;
+class ChoreoScene;
+class CChoreoEvent;
+class CFlexAnimationTrack;
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CEdgePropertiesParams : public CBaseDialogParams
+{
+ // GlobalEvent descriptive name
+ char m_szName[ 256 ];
+
+ void SetFromFlexTrack( CFlexAnimationTrack *track );
+ void ApplyToTrack( CFlexAnimationTrack *track );
+
+ void SetFromCurve( CCurveData *ramp );
+ void ApplyToCurve( CCurveData *ramp );
+
+ bool m_bActive[ 2 ];
+ int m_InterpolatorType[ 2 ];
+ float m_flValue[ 2 ];
+};
+
+int EdgeProperties( CEdgePropertiesParams *params );
+
+#endif // EDGEPROPERTIES_H
diff --git a/utils/hlfaceposer/EditPhrase.cpp b/utils/hlfaceposer/EditPhrase.cpp
new file mode 100644
index 0000000..e6b36cc
--- /dev/null
+++ b/utils/hlfaceposer/EditPhrase.cpp
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#define UNICODE
+
+#include "resource.h"
+#include "EditPhrase.h"
+#include "mxtk/mx.h"
+#include "mdlviewer.h"
+
+static CEditPhraseParams g_Params;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EditPhraseDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+
+ SetDlgItemTextW( hwndDlg, IDC_INPUTSTRING, g_Params.m_szInputText );
+ SetDlgItemTextA( hwndDlg, IDC_STATIC_PROMPT, g_Params.m_szPrompt );
+
+ SetWindowTextA( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_INPUTSTRING ) );
+ SendMessage( GetDlgItem( hwndDlg, IDC_INPUTSTRING ), EM_SETSEL, 0, MAKELONG(0, 0xffff) );
+
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ g_Params.m_szInputText[ 0 ] = 0;
+ GetDlgItemTextW( hwndDlg, IDC_INPUTSTRING, g_Params.m_szInputText, ARRAYSIZE( g_Params.m_szInputText ) );
+ EndDialog( hwndDlg, 1 );
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EditPhrase( CEditPhraseParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EDITPHRASE ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EditPhraseDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/EditPhrase.h b/utils/hlfaceposer/EditPhrase.h
new file mode 100644
index 0000000..6cb0ccc
--- /dev/null
+++ b/utils/hlfaceposer/EditPhrase.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EDITPHRASE_H
+#define EDITPHRASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <stdio.h>
+
+//========= Copyright � 1996-2001, Valve LLC, All rights reserved. ============
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CEditPhraseParams : public CBaseDialogParams
+{
+ char m_szPrompt[ 256 ];
+
+ // i/o input text
+ wchar_t m_szInputText[ 1024 ];
+};
+
+// Display/create dialog
+int EditPhrase( CEditPhraseParams *params );
+
+#endif // EDITPHRASE_H
diff --git a/utils/hlfaceposer/GestureTool.cpp b/utils/hlfaceposer/GestureTool.cpp
new file mode 100644
index 0000000..bc81da0
--- /dev/null
+++ b/utils/hlfaceposer/GestureTool.cpp
@@ -0,0 +1,1986 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "GestureTool.h"
+#include "mdlviewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "TimelineItem.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "choreoevent.h"
+#include "StudioModel.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "ChoreoView.h"
+#include "InputProperties.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include "mxExpressionTray.h"
+#include "ExpressionProperties.h"
+#include "tier1/strtools.h"
+#include "faceposer_models.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "iscenetokenprocessor.h"
+#include "choreoviewcolors.h"
+#include "MatSysWin.h"
+
+GestureTool *g_pGestureTool = 0;
+
+#define TRAY_HEIGHT 20
+#define TRAY_ITEM_INSET 10
+
+#define TAG_TOP ( TRAY_HEIGHT + 32 )
+#define TAG_BOTTOM ( TAG_TOP + 20 )
+
+#define MAX_TIME_ZOOM 1000
+// 10% per step
+#define TIME_ZOOM_STEP 2
+
+float SnapTime( float input, float granularity );
+
+GestureTool::GestureTool( mxWindow *parent )
+: IFacePoserToolWindow( "GestureTool", "Gesture" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_bSuppressLayout = false;
+
+ SetAutoProcess( true );
+
+ m_nFocusEventGlobalID = -1;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+ m_nDragType = DRAGTYPE_NONE;
+
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+
+ m_hPrevCursor = 0;
+
+ m_nStartX = 0;
+ m_nStartY = 0;
+
+ m_pLastEvent = NULL;
+
+ m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 500.0f;
+
+ m_flLastDuration = 0.0f;
+ m_nScrollbarHeight = 12;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_GESTUREHSCROLL, mxScrollbar::Horizontal );
+ m_pHorzScrollBar->setVisible( false );
+
+ m_bInSetEvent = false;
+ m_flScrubberTimeOffset = 0.0f;
+}
+
+GestureTool::~GestureTool( void )
+{
+}
+
+void GestureTool::SetEvent( CChoreoEvent *event )
+{
+ if ( m_bInSetEvent )
+ return;
+
+ m_bInSetEvent = true;
+
+ if ( event == m_pLastEvent )
+ {
+ if ( event )
+ {
+ if ( event->GetDuration() != m_flLastDuration )
+ {
+ m_flLastDuration = event->GetDuration();
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset = 0.0f;
+ InvalidateLayout();
+ }
+
+ m_nFocusEventGlobalID = event->GetGlobalID();
+ }
+
+ m_bInSetEvent = false;
+ return;
+ }
+
+ m_pLastEvent = event;
+
+ m_nFocusEventGlobalID = -1;
+ if ( event )
+ {
+ m_nFocusEventGlobalID = event->GetGlobalID();
+ }
+
+ if ( event )
+ {
+ m_flLastDuration = event->GetDuration();
+ }
+ else
+ {
+ m_flLastDuration = 0.0f;
+ }
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ InvalidateLayout();
+
+ m_bInSetEvent = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoEvent *GestureTool::GetSafeEvent( void )
+{
+ if ( m_nFocusEventGlobalID == -1 )
+ return NULL;
+
+ if ( !g_pChoreoView )
+ return NULL;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return NULL;
+
+ // Find event by name
+ for ( int i = 0; i < scene->GetNumEvents() ; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e || e->GetType() != CChoreoEvent::GESTURE )
+ continue;
+
+ if ( e->GetGlobalID() == m_nFocusEventGlobalID )
+ {
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void GestureTool::GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped )
+{
+ float pixel = 0.0f;
+ if ( w2() > 0 )
+ {
+ pixel = GetPixelForTimeValue( scrub );
+
+ 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;
+}
+
+void GestureTool::GetScrubHandleReferenceRect( RECT& rcHandle, float scrub, bool clipped /*= false*/ )
+{
+ float pixel = 0.0f;
+ if ( w2() > 0 )
+ {
+ pixel = GetPixelForTimeValue( scrub );
+
+ 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() + 195;
+ rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void GestureTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference )
+{
+ HBRUSH br = CreateSolidBrush( reference ? RGB( 150, 0, 0 ) : RGB( 0, 150, 100 ) );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", scrub );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ float st, ed;
+ st = ev->GetStartTime();
+ ed = ev->GetEndTime();
+
+ float dt = ed - st;
+ if ( dt > 0.0f )
+ {
+ sprintf( sz, "%.3f", st + scrub );
+ }
+ }
+
+ 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 GestureTool::IsMouseOverScrubHandle( mxEvent *event )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, 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;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GestureTool::IsProcessing( void )
+{
+ if ( !GetSafeEvent() )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+bool GestureTool::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+void GestureTool::SetScrubTime( float t )
+{
+ m_flScrub = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + m_flScrub;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->DrawScrubHandle();
+ }
+}
+
+void GestureTool::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + m_flScrubTarget;
+
+ g_pChoreoView->SetScrubTargetTime( realtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void GestureTool::Think( float dt )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ bool scrubbing = IsScrubbing();
+ ScrubThink( dt, scrubbing );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void GestureTool::ScrubThink( float dt, bool scrubbing )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ return;
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub + maxmove );
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub - maxmove );
+ }
+ }
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+}
+
+void GestureTool::DrawScrubHandles()
+{
+ RECT rcTray;
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+
+ rcTray = rcHandle;
+ rcTray.left = 0;
+ rcTray.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcTray );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev && ev->GetDuration() > 0.0f )
+ {
+ float scrub = ev->GetOriginalPercentageFromPlaybackPercentage( m_flScrub / ev->GetDuration() ) * ev->GetDuration();
+ GetScrubHandleReferenceRect( rcHandle, scrub, true );
+
+ rcTray = rcHandle;
+ rcTray.left = 0;
+ rcTray.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcTray );
+ DrawScrubHandle( drawHelper, rcHandle, scrub, true );
+ }
+}
+
+void GestureTool::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ RECT rcText;
+ drawHelper.GetClientRect( rcText );
+ rcText.top += GetCaptionHeight()+1;
+ rcText.bottom = rcText.top + 13;
+ rcText.left += 5;
+ rcText.right -= 5;
+
+ OffsetRect( &rcText, 0, 12 );
+
+ int current, total;
+
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ RECT rcUndo = rcText;
+ OffsetRect( &rcUndo, 0, 2 );
+
+ drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
+ "Undo: %i/%i", current, total );
+ }
+
+ rcText.left += 60;
+
+ // Found it, write out description
+ //
+ float seqduration;
+ ev->GetGestureSequenceDuration( seqduration );
+
+ RECT rcTextLine = rcText;
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine,
+ "Event: %s",
+ ev->GetName() );
+
+ OffsetRect( &rcTextLine, 0, 12 );
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine,
+ "Sequence: '%s' %.3f s.",
+ ev->GetParameters(),
+ seqduration );
+
+ RECT rcTimeLine;
+ drawHelper.GetClientRect( rcTimeLine );
+ rcTimeLine.left = 0;
+ rcTimeLine.right = w2();
+ rcTimeLine.top += ( GetCaptionHeight() + 70 );
+
+ float lefttime = GetTimeValueForMouse( 0 );
+ float righttime = GetTimeValueForMouse( w2() );
+
+ DrawTimeLine( drawHelper, rcTimeLine, lefttime, righttime );
+
+ OffsetRect( &rcText, 0, 30 );
+
+ rcText.left = 5;
+
+ RECT timeRect = rcText;
+
+ timeRect.right = timeRect.left + 100;
+
+ char sz[ 32 ];
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", lefttime + ev->GetStartTime() );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+
+ timeRect = rcText;
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", righttime + ev->GetStartTime() );
+
+ int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ timeRect.right = w2() - 10;
+ timeRect.left = timeRect.right - textW;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+ }
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+
+ DrawEventEnd( drawHelper );
+
+ if ( ev && ev->GetDuration() > 0.0f )
+ {
+ float scrub = ev->GetOriginalPercentageFromPlaybackPercentage( m_flScrub / ev->GetDuration() ) * ev->GetDuration();
+ GetScrubHandleReferenceRect( rcHandle, scrub, true );
+ DrawScrubHandle( drawHelper, rcHandle, scrub, true );
+ }
+
+ RECT rcTags = rc;
+ rcTags.top = TAG_TOP + GetCaptionHeight();
+ rcTags.bottom = TAG_BOTTOM + GetCaptionHeight();
+
+ DrawRelativeTags( drawHelper, rcTags );
+
+ DrawAbsoluteTags( drawHelper );
+
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GestureTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
+{
+ // Construct main menu
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ int current, total;
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ if ( current > 0 )
+ {
+ pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_GT );
+ }
+
+ if ( current <= total - 1 )
+ {
+ pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_GT );
+ }
+ pop->addSeparator();
+ }
+
+ CEventAbsoluteTag *tag = IsMouseOverTag( (short)event->x, (short)event->y );
+ if ( tag )
+ {
+ pop->add( va( "Delete '%s'...", tag->GetName() ), IDC_GT_DELETE_TAG );
+ }
+ else
+ {
+ pop->add( "Insert Tag...", IDC_GT_INSERT_TAG );
+ }
+ pop->add( "Revert Tag Timings", IDC_GT_REVERT );
+ pop->add( va( "Change scale..." ), IDC_GT_CHANGESCALE );
+
+ pop->popup( this, (short)event->x, (short)event->y );
+}
+
+void GestureTool::GetWorkspaceLeftRight( int& left, int& right )
+{
+ left = 0;
+ right = w2();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GestureTool::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 );
+}
+
+void GestureTool::SetClickedPos( int x, int y )
+{
+ m_nClickedX = x;
+ m_nClickedY = y;
+}
+
+float GestureTool::GetTimeForClickedPos( void )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return 0.0f;
+
+ float t = GetTimeValueForMouse( m_nClickedX );
+ return t;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dragtype -
+// startx -
+// cursor -
+//-----------------------------------------------------------------------------
+void GestureTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
+{
+ m_nDragType = dragtype;
+ m_nStartX = startx;
+ m_nLastX = startx;
+ m_nStartY = starty;
+ m_nLastY = starty;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ m_hPrevCursor = SetCursor( cursor );
+
+ m_FocusRects.Purge();
+
+ RECT rcStart;
+ rcStart.left = startx;
+ rcStart.right = startx;
+
+ bool addrect = true;
+ switch ( dragtype )
+ {
+ default:
+ case DRAGTYPE_SCRUBBER:
+ {
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, m_flScrub, true );
+
+ rcStart = rcScrub;
+ rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcStart.right = rcStart.left;
+ rcStart.top = rcScrub.bottom;
+
+ rcStart.bottom = h2();
+ }
+ break;
+ case DRAGTYPE_ABSOLUTE_TIMING_TAG:
+ {
+ rcStart.top = 0;
+ rcStart.bottom = h2();
+ }
+ break;
+ }
+
+
+ if ( addrect )
+ {
+ AddFocusRect( rcStart );
+ }
+
+ DrawFocusRect();
+}
+
+void GestureTool::OnMouseMove( mxEvent *event )
+{
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ event->x = (short)mx;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+
+ 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:
+ {
+ ApplyBounds( mx, my );
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ }
+ }
+ break;
+ case DRAGTYPE_ABSOLUTE_TIMING_TAG:
+ {
+ ApplyBounds( mx, my );
+ }
+ break;
+ }
+
+ OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), 0 );
+ }
+
+ DrawFocusRect();
+ }
+ else
+ {
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverTag( mx, my ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ }
+
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+}
+
+int GestureTool::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ int w, h;
+ w = event->width;
+ h = event->height;
+
+ m_nLastHPixelsNeeded = 0;
+ InvalidateLayout();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int tz = g_pChoreoView->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 );
+ }
+
+ g_pChoreoView->SetPreservedTimeZoom( this, tz );
+ }
+ RepositionHSlider();
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ iret = 1;
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetClickedPos( mx, my );
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ return iret;
+ }
+
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ if ( w2() > 0 )
+ {
+ 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;
+ ForceScrubPosition( t );
+ }
+
+ StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverTag( mx, my ) )
+ {
+ StartDragging( DRAGTYPE_ABSOLUTE_TIMING_TAG, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ SetScrubTargetTime( t );
+ }
+ }
+
+ CalcBounds( m_nDragType );
+ }
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ OnMouseMove( event );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ return 1;
+ }
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_NONE:
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ m_flScrubberTimeOffset = 0.0f;
+ }
+ }
+ break;
+ case DRAGTYPE_ABSOLUTE_TIMING_TAG:
+ {
+ ApplyBounds( mx, my );
+
+ CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
+ if ( tag && w2() && GetSafeEvent() )
+ {
+ float t = GetTimeValueForMouse( mx );
+ float lastfrac = t / GetSafeEvent()->GetDuration();
+ lastfrac = clamp( lastfrac, 0.0f, 1.0f );
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "move absolute tag" );
+ tag->SetPercentage( lastfrac );
+ g_pChoreoView->PushRedo( "move absolute tag" );
+
+ g_pChoreoView->InvalidateLayout();
+
+ redraw();
+ }
+
+ }
+ break;
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ iret = g_pChoreoView->HandleZoomKey( this, event->key );
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_UNDO_GT:
+ OnUndo();
+ break;
+ case IDC_REDO_GT:
+ OnRedo();
+ break;
+ case IDC_GT_DELETE_TAG:
+ OnDeleteTag();
+ break;
+ case IDC_GT_INSERT_TAG:
+ OnInsertTag();
+ break;
+ case IDC_GT_REVERT:
+ OnRevert();
+ break;
+ case IDC_GESTUREHSCROLL:
+ {
+ 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_GT_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ }
+ }
+ break;
+ }
+ return iret;
+}
+
+void GestureTool::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+int GestureTool::GetTagTypeForTag( CEventAbsoluteTag const *tag )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return -1;
+
+ for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ )
+ {
+ CChoreoEvent::AbsTagType tagtype = (CChoreoEvent::AbsTagType)t;
+
+ for ( int i = 0; i < e->GetNumAbsoluteTags( tagtype ); i++ )
+ {
+ CEventAbsoluteTag *ptag = e->GetAbsoluteTag( tagtype, i );
+ Assert( ptag );
+ if ( ptag == tag )
+ return t;
+ }
+ }
+
+ return -1;
+}
+
+void GestureTool::CalcBounds( int movetype )
+{
+ switch ( movetype )
+ {
+ default:
+ case DRAGTYPE_NONE:
+ {
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ m_bUseBounds = true;
+ m_nMinX = 0;
+ m_nMaxX = w2();
+ }
+ break;
+ case DRAGTYPE_ABSOLUTE_TIMING_TAG:
+ {
+ m_bUseBounds = true;
+ m_nMinX = 0;
+ m_nMaxX = w2();
+
+ CChoreoEvent *e = GetSafeEvent();
+ CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
+ if ( tag && e && e->GetDuration() )
+ {
+ m_nMinX = GetPixelForTimeValue( 0 );
+ m_nMaxX = max( w2(), GetPixelForTimeValue( e->GetDuration() ) );
+
+ int t = GetTagTypeForTag( tag );
+ if ( t != -1 )
+ {
+ CChoreoEvent::AbsTagType tagtype = (CChoreoEvent::AbsTagType)t;
+
+ CEventAbsoluteTag *prevTag = NULL, *nextTag = NULL;
+ int c = e->GetNumAbsoluteTags( tagtype );
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CEventAbsoluteTag *t = e->GetAbsoluteTag( tagtype, i );
+ Assert( t );
+
+ if ( t == tag )
+ {
+ prevTag = i > 0 ? e->GetAbsoluteTag( tagtype, i-1 ) : NULL;
+ nextTag = i < c - 1 ? e->GetAbsoluteTag( tagtype, i+1 ) : NULL;
+ break;
+ }
+ }
+
+ if ( i < c )
+ {
+ if ( prevTag )
+ {
+ m_nMinX = GetPixelForTimeValue( prevTag->GetPercentage() * e->GetDuration() ) + 1;
+ }
+ if ( nextTag )
+ {
+ m_nMaxX = GetPixelForTimeValue( nextTag->GetPercentage() * e->GetDuration() ) - 1;
+ }
+ }
+ else
+ {
+ Assert( 0 );
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+bool GestureTool::PaintBackground()
+{
+ redraw();
+ return false;
+}
+
+void GestureTool::OnUndo( void )
+{
+ g_pChoreoView->Undo();
+}
+
+void GestureTool::OnRedo( void )
+{
+ g_pChoreoView->Redo();
+}
+
+void GestureTool::ForceScrubPositionFromSceneTime( float scenetime )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e || !e->GetDuration() )
+ return;
+
+ float t = scenetime - e->GetStartTime();
+ m_flScrub = t;
+ m_flScrubTarget = t;
+ DrawScrubHandles();
+}
+
+void GestureTool::ForceScrubPosition( float t )
+{
+ m_flScrub = t;
+ m_flScrubTarget = t;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + t;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->SetScrubTargetTime( realtime );
+
+ g_pChoreoView->DrawScrubHandle();
+ }
+
+ DrawScrubHandles();
+}
+
+void GestureTool::SetMouseOverPos( int x, int y )
+{
+ m_nMousePos[ 0 ] = x;
+ m_nMousePos[ 1 ] = y;
+}
+
+void GestureTool::GetMouseOverPos( int &x, int& y )
+{
+ x = m_nMousePos[ 0 ];
+ y = m_nMousePos[ 1 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcPos -
+//-----------------------------------------------------------------------------
+void GestureTool::GetMouseOverPosRect( RECT& rcPos )
+{
+ rcPos.top = GetCaptionHeight() + 12;
+ rcPos.left = w2() - 200;
+ rcPos.right = w2() - 5;
+ rcPos.bottom = rcPos.top + 13;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcPos -
+//-----------------------------------------------------------------------------
+void GestureTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
+{
+ // Compute time for pixel x
+ float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ t += e->GetStartTime();
+ float snapped = FacePoser_SnapTime( t );
+
+ // Found it, write out description
+ //
+ char sz[ 128 ];
+ if ( t != snapped )
+ {
+ Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), "%.3f", t );
+ }
+
+ int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
+
+ RECT rcText = rcPos;
+ rcText.left = max( rcPos.left, rcPos.right - len );
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GestureTool::DrawMouseOverPos()
+{
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+void GestureTool::AddFocusRect( RECT& rc )
+{
+ RECT rcFocus = rc;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ // Convert to screen space?
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &rcClient -
+// tagtype -
+// rcTray -
+//-----------------------------------------------------------------------------
+void GestureTool::GetTagTrayRect( RECT &rcClient, int tagtype, RECT& rcTray )
+{
+ rcTray = rcClient;
+
+ rcTray.top += ( GetCaptionHeight() + 110 );
+
+ rcTray.bottom = rcTray.top + 6;
+
+ if ( tagtype == CChoreoEvent::ORIGINAL )
+ {
+ OffsetRect( &rcTray, 0, 45 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcClient -
+// *event -
+// tagtype -
+// *tag -
+// rcTag -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GestureTool::GetAbsTagRect( RECT& rcClient, CChoreoEvent *event,
+ int tagtype, CEventAbsoluteTag *tag, RECT& rcTag )
+{
+ rcTag = rcClient;
+
+ GetTagTrayRect( rcClient, tagtype, rcTag );
+
+ bool clipped = false;
+ float t = tag->GetPercentage() * event->GetDuration();
+ int tagx = GetPixelForTimeValue( t, &clipped );
+
+ rcTag.left = tagx - 3;
+ rcTag.right = tagx + 3;
+
+ if ( clipped )
+ return false;
+
+ return true;
+}
+
+void GestureTool::DrawAbsoluteTags( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ bool showDots = true;
+ if ( event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)0 ) !=
+ event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)1 ) )
+ {
+ showDots = false;
+ }
+
+ int t;
+ for ( t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ )
+ {
+ CChoreoEvent::AbsTagType tagtype = ( CChoreoEvent::AbsTagType )t;
+
+ RECT rcTray;
+ GetTagTrayRect( rcClient, tagtype, rcTray );
+
+ drawHelper.DrawColoredLine( RGB( 220, 220, 220 ), PS_SOLID, 1, rcTray.left, rcTray.top, rcTray.right, rcTray.top );
+ drawHelper.DrawColoredLine( RGB( 220, 220, 220 ), PS_SOLID, 1, rcTray.left, rcTray.bottom, rcTray.right, rcTray.bottom );
+
+ RECT rcText;
+ rcText = rcTray;
+
+ InflateRect( &rcText, 0, 4 );
+ OffsetRect( &rcText, 0, t == 0 ? -10 : 10 );
+
+ rcText.left = 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 150, 150, 150 ), rcText, "%s",
+ t == 0 ? "Playback Time" : "Original Time" );
+
+ for ( int i = 0; i < event->GetNumAbsoluteTags( tagtype ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetAbsoluteTag( tagtype, i );
+ if ( !tag )
+ continue;
+
+ RECT rcMark;
+
+ bool visible = GetAbsTagRect( rcClient, event, tagtype, tag, rcMark );
+
+ if ( showDots && t == 1 )
+ {
+ CChoreoEvent::AbsTagType tagtypeOther = (CChoreoEvent::AbsTagType)0;
+
+ RECT rcMark2;
+ CEventAbsoluteTag *otherTag = event->GetAbsoluteTag( tagtypeOther, i );
+ if ( otherTag )
+ {
+ GetAbsTagRect( rcClient, event, tagtypeOther, otherTag, rcMark2 );
+ {
+ int midx1 = ( rcMark.left + rcMark.right ) / 2;
+ int midx2 = ( rcMark2.left + rcMark2.right ) / 2;
+
+ int y1 = rcMark.top;
+ int y2 = rcMark2.bottom;
+
+ drawHelper.DrawColoredLine(
+ RGB( 200, 200, 200 ), PS_SOLID, 1,
+ midx1, y1, midx2, y2 );
+ }
+ }
+ }
+
+ if ( !visible )
+ continue;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 200, 0, 30 ), tagtype != CChoreoEvent::PLAYBACK );
+
+ RECT rcText;
+ rcText = rcMark;
+
+ if ( tagtype == CChoreoEvent::PLAYBACK )
+ {
+ rcText.top -= 15;
+ }
+ else
+ {
+ rcText.top += 10;
+ }
+
+ char text[ 256 ];
+ sprintf( text, "%s", tag->GetName() );
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text );
+ rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 100 ), rcText, text );
+
+ if ( tagtype == CChoreoEvent::PLAYBACK )
+ {
+ rcText.top -= 10;
+ }
+ else
+ {
+ rcText.top += 10;
+ }
+
+ // sprintf( text, "%.3f", tag->GetPercentage() * event->GetDuration() + event->GetStartTime() );
+ sprintf( text, "%.3f", tag->GetPercentage() );
+
+ len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text );
+ rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 100 ), rcText, text );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+// left -
+// right -
+//-----------------------------------------------------------------------------
+void GestureTool::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right )
+{
+ RECT rcLabel;
+ float granularity = 0.5f;
+
+ drawHelper.DrawColoredLine( RGB( 150, 150, 200 ), PS_SOLID, 1, rc.left, rc.top + 2, rc.right, rc.top + 2 );
+
+ float f = SnapTime( left, granularity );
+ while ( f < right )
+ {
+ float frac = ( f - left ) / ( right - left );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ rcLabel.left = GetPixelForTimeValue( f );
+ rcLabel.top = rc.top + 5;
+ rcLabel.bottom = rcLabel.top + 10;
+
+ if ( f != left )
+ {
+ drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1,
+ rcLabel.left, rc.top, 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 );
+
+ RECT rcOut = rcLabel;
+ if ( rcOut.left <= 0 )
+ {
+ OffsetRect( &rcOut, -rcOut.left + 2, 0 );
+ }
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 50, 150 ), rcOut, sz );
+
+ }
+ f += granularity;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : CFlexTimingTag
+//-----------------------------------------------------------------------------
+CEventAbsoluteTag *GestureTool::IsMouseOverTag( int mx, int my )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return NULL;
+
+ RECT rcClient;
+ GetClientRect( (HWND)getHandle(), &rcClient );
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ )
+ {
+ CChoreoEvent::AbsTagType tagtype = ( CChoreoEvent::AbsTagType )t;
+
+ for ( int i = 0; i < event->GetNumAbsoluteTags( tagtype ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetAbsoluteTag( tagtype, i );
+ if ( !tag )
+ continue;
+
+ if ( tag->GetLocked() )
+ continue;
+
+ RECT rcTag;
+
+ if ( !GetAbsTagRect( rcClient, event, tagtype, tag, rcTag ) )
+ continue;
+
+ if ( !PtInRect( &rcTag, pt ) )
+ continue;
+
+ return tag;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : int
+//-----------------------------------------------------------------------------
+int GestureTool::GetTagTypeForMouse( int mx, int my )
+{
+ RECT rcClient;
+ rcClient.left = 0;
+ rcClient.right = w2();
+ rcClient.top = 0;
+ rcClient.bottom = h2();
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ )
+ {
+ RECT rcTray;
+ GetTagTrayRect( rcClient, t, rcTray );
+
+ if ( PtInRect( &rcTray, pt ) )
+ {
+ return t;
+ }
+ }
+ return -1;
+}
+
+void GestureTool::OnInsertTag( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( event->GetType() != CChoreoEvent::GESTURE )
+ {
+ Con_ErrorPrintf( "Absolute Tag: Can only tag GESTURE events\n" );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Absolute Tag Name" );
+ strcpy( params.m_szPrompt, "Name:" );
+
+ strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Timing Tag Name: No name entered!\n" );
+ return;
+ }
+
+ // Convert click to frac
+ float t = GetTimeValueForMouse( m_nClickedX ) / event->GetDuration();
+ float tshifted = event->GetOriginalPercentageFromPlaybackPercentage( t );
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Add Gesture Tag" );
+
+ event->AddAbsoluteTag( CChoreoEvent::ORIGINAL, params.m_szInputText, tshifted );
+ event->AddAbsoluteTag( CChoreoEvent::PLAYBACK, params.m_szInputText, t );
+
+ g_pChoreoView->PushRedo( "Add Gesture Tag" );
+
+ // Redraw this window
+ redraw();
+}
+
+void GestureTool::OnRevert()
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( !event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) )
+ return;
+
+ if ( event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) !=
+ event->GetNumAbsoluteTags( CChoreoEvent::ORIGINAL ) )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Revert Gesture Tags" );
+
+ int c = event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK );
+ for ( int i = 0; i < c; i++ )
+ {
+ CEventAbsoluteTag *original = event->GetAbsoluteTag( CChoreoEvent::ORIGINAL, i );
+ CEventAbsoluteTag *playback = event->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+
+ playback->SetPercentage( original->GetPercentage() );
+ }
+
+
+ g_pChoreoView->PushRedo( "Revert Gesture Tags" );
+
+ // Redraw this window
+ redraw();
+}
+
+void GestureTool::OnDeleteTag( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ CEventAbsoluteTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
+ if ( !tag )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Remove Gesture Tag" );
+
+ char sz[ 512 ];
+ Q_strncpy( sz, tag->GetName(), sizeof( sz ) );
+
+ for ( int t = 0; t < CChoreoEvent::NUM_ABS_TAG_TYPES; t++ )
+ {
+ event->RemoveAbsoluteTag( (CChoreoEvent::AbsTagType)t, sz );
+ }
+
+ g_pChoreoView->PushRedo( "Remove Gesture Tags" );
+
+ // Redraw this window
+ redraw();
+}
+
+void GestureTool::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ CChoreoEvent *gesture = GetSafeEvent();
+ if ( !gesture )
+ return;
+
+ CChoreoScene *scene = gesture->GetScene();
+ if ( !scene )
+ return;
+
+ float starttime = GetTimeValueForMouse( 0 );
+ float endtime = GetTimeValueForMouse( w2() );
+
+ if ( endtime - starttime <= 0.0f )
+ return;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rc, "Timing Tags:" );
+
+ // Loop through all events in scene
+
+ int c = scene->GetNumEvents();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e )
+ continue;
+
+ if ( e->GetNumRelativeTags() <= 0 )
+ continue;
+
+ // See if time overlaps
+ if ( !e->HasEndTime() )
+ continue;
+
+ if ( ( e->GetEndTime() - e->GetStartTime() ) < starttime )
+ continue;
+
+ if ( ( e->GetStartTime() - e->GetStartTime() ) > endtime )
+ continue;
+
+ DrawRelativeTagsForEvent( drawHelper, rc, gesture, e, starttime, endtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void GestureTool::DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *gesture, CChoreoEvent *event, float starttime, float endtime )
+{
+ if ( !event )
+ return;
+
+ //drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" );
+
+ for ( int i = 0; i < event->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() ) - gesture->GetStartTime();
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ bool clipped = false;
+ int left = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ //float frac = ( tagtime - starttime ) / ( endtime - starttime );
+
+ //int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 200 ) );
+
+ RECT rcText;
+ rcText = rc;
+ rcText.bottom = rc.bottom - 10;
+ rcText.top = rcText.bottom - 10;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int GestureTool::ComputeHPixelsNeeded( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return 0;
+
+ int pixels = 0;
+ float maxtime = event->GetDuration();
+ pixels = (int)( ( maxtime ) * GetPixelsPerSecond() ) + 10;
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GestureTool::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ if ( pixelsneeded <= w2() )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2() - 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( w2() );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float GestureTool::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() )/100.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void GestureTool::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = (float)x;
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GestureTool::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ m_bLayoutIsValid = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : st -
+// ed -
+//-----------------------------------------------------------------------------
+void GestureTool::GetStartAndEndTime( float& st, float& ed )
+{
+ st = m_flLeftOffset / GetPixelsPerSecond();
+ ed = st + (float)w2() / GetPixelsPerSecond();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : float
+//-----------------------------------------------------------------------------
+float GestureTool::GetEventEndTime()
+{
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( !ev )
+ return 1.0f;
+
+ return ev->GetDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int GestureTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ float frac = ( time - st ) / ( ed - st );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = ( int )( frac * w2() );
+ return pixel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float GestureTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ if ( clip )
+ {
+ if ( mx < 0 )
+ {
+ return st;
+ }
+ if ( mx > w2() )
+ {
+ return ed;
+ }
+ }
+
+ float frac = (float)( mx ) / (float)( w2() );
+ return st + frac * ( ed - st );
+}
+
+void GestureTool::OnChangeScale( void )
+{
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change Zoom" );
+ strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset= 0.0f;
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
+}
+
+void GestureTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+ if ( !duration )
+ return;
+
+ int leftx = GetPixelForTimeValue( duration );
+ if ( leftx >= w2() )
+ return;
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, GetCaptionHeight() + 73, leftx, rcClient.bottom );
+
+}
+
+void GestureTool::OnModelChanged()
+{
+ redraw();
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/GestureTool.h b/utils/hlfaceposer/GestureTool.h
new file mode 100644
index 0000000..1f332bd
--- /dev/null
+++ b/utils/hlfaceposer/GestureTool.h
@@ -0,0 +1,174 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GESTURETOOL_H
+#define GESTURETOOL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "studio.h"
+#include "utlvector.h"
+#include "faceposertoolwindow.h"
+
+class CChoreoEvent;
+class CChoreoWidgetDrawHelper;
+class CChoreoView;
+class CEventAbsoluteTag;
+
+#define IDC_REDO_GT 1000
+#define IDC_UNDO_GT 1001
+#define IDC_GT_DELETE_TAG 1002
+#define IDC_GT_INSERT_TAG 1003
+#define IDC_GT_REVERT 1004
+
+#define IDC_GT_CHANGESCALE 1005
+#define IDC_GESTUREHSCROLL 1006
+
+class GestureTool : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ // Construction
+ GestureTool( mxWindow *parent );
+ ~GestureTool( void );
+
+ virtual void Think( float dt );
+ void ScrubThink( float dt, bool scrubbing );
+ virtual bool IsScrubbing( void ) const;
+ virtual bool IsProcessing( void );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground();
+
+ void SetEvent( CChoreoEvent *event );
+
+ void GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped = false );
+ void GetScrubHandleReferenceRect( RECT& rcHandle, float scrub, bool clipped = false );
+
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference );
+ void DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right );
+ void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
+
+ void DrawAbsoluteTags( CChoreoWidgetDrawHelper& drawHelper );
+ bool GetAbsTagRect( RECT& rcClient, CChoreoEvent *event, int tagtype, CEventAbsoluteTag *tag, RECT& rcTag );
+ void GetTagTrayRect( RECT &rcClient, int tagtype, RECT& rcTray );
+
+ void SetMouseOverPos( int x, int y );
+ void GetMouseOverPos( int &x, int& y );
+ void GetMouseOverPosRect( RECT& rcPos );
+ void DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos );
+ void DrawMouseOverPos();
+
+ void DrawScrubHandles();
+
+ CChoreoEvent *GetSafeEvent( void );
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ void ForceScrubPosition( float newtime );
+ void ForceScrubPositionFromSceneTime( float scenetime );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+ virtual void OnModelChanged();
+
+private:
+
+ void StartDragging( int dragtype, int startx, int starty, HCURSOR cursor );
+ void AddFocusRect( RECT& rc );
+ void OnMouseMove( mxEvent *event );
+ void DrawFocusRect( void );
+ void ShowContextMenu( mxEvent *event, bool include_track_menus );
+ void GetWorkspaceLeftRight( int& left, int& right );
+ void SetClickedPos( int x, int y );
+ float GetTimeForClickedPos( void );
+
+ void ApplyBounds( int& mx, int& my );
+ void CalcBounds( int movetype );
+ void OnUndo( void );
+ void OnRedo( void );
+
+ CEventAbsoluteTag *IsMouseOverTag( int mx, int my );
+ int GetTagTypeForMouse( int mx, int my );
+
+ int GetTagTypeForTag( CEventAbsoluteTag const *tag );
+ void OnInsertTag( void );
+ void OnDeleteTag( void );
+ void OnRevert( void );
+
+ void DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+ void DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *gesture, CChoreoEvent *event, float starttime, float endtime );
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+ void OnChangeScale();
+ int ComputeHPixelsNeeded( void );
+ float GetPixelsPerSecond( void );
+ void InvalidateLayout( void );
+ void RepositionHSlider( void );
+ void GetStartAndEndTime( float& st, float& ed );
+ float GetEventEndTime();
+ float GetTimeValueForMouse( int mx, bool clip = false );
+ int GetPixelForTimeValue( float time, bool *clipped = NULL );
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_SCRUBBER,
+ DRAGTYPE_ABSOLUTE_TIMING_TAG,
+ };
+
+ int m_nFocusEventGlobalID;
+
+ int m_nMousePos[ 2 ];
+
+ bool m_bUseBounds;
+ int m_nMinX;
+ int m_nMaxX;
+
+ HCURSOR m_hPrevCursor;
+ int m_nDragType;
+
+ int m_nStartX;
+ int m_nStartY;
+ int m_nLastX;
+ int m_nLastY;
+
+ int m_nClickedX;
+ int m_nClickedY;
+
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+ CChoreoEvent *m_pLastEvent;
+
+ bool m_bSuppressLayout;
+ // Height/width of scroll bars
+ int m_nScrollbarHeight;
+ float m_flLeftOffset;
+ mxScrollbar *m_pHorzScrollBar;
+ int m_nLastHPixelsNeeded;
+ // How many pixels per second we are showing in the UI
+ float m_flPixelsPerSecond;
+ // Do we need to move controls?
+ bool m_bLayoutIsValid;
+ float m_flLastDuration;
+ bool m_bInSetEvent;
+ float m_flScrubberTimeOffset;
+
+ friend class CChoreoView;
+};
+
+extern GestureTool *g_pGestureTool;
+#endif // GESTURETOOL_H
diff --git a/utils/hlfaceposer/ICloseCaptionManager.h b/utils/hlfaceposer/ICloseCaptionManager.h
new file mode 100644
index 0000000..a82a019
--- /dev/null
+++ b/utils/hlfaceposer/ICloseCaptionManager.h
@@ -0,0 +1,33 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ICLOSECAPTIONMANAGER_H
+#define ICLOSECAPTIONMANAGER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+typedef struct tagRECT RECT;
+class CSentence;
+class StudioModel;
+class CChoreoWidgetDrawHelper;
+
+class ICloseCaptionManager
+{
+public:
+ virtual void Reset( void ) = 0;
+
+ virtual void Process( char const *tokenname, float duration, int languageid ) = 0;
+
+ virtual bool LookupUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count ) = 0;
+ // Same as above, except strips out <> command tokens
+ virtual bool LookupStrippedUnicodeText( int languageId, char const *token, wchar_t *outbuf, size_t count ) = 0;
+};
+
+extern ICloseCaptionManager *closecaptionmanager;
+
+#endif // ICLOSECAPTIONMANAGER_H
diff --git a/utils/hlfaceposer/ProgressDialog.cpp b/utils/hlfaceposer/ProgressDialog.cpp
new file mode 100644
index 0000000..92264d5
--- /dev/null
+++ b/utils/hlfaceposer/ProgressDialog.cpp
@@ -0,0 +1,243 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "resource.h"
+#include "ProgressDialog.h"
+#include "mxtk/mx.h"
+#include "mdlviewer.h"
+#include "tier1/utlstring.h"
+#include "tier1/strtools.h"
+#include "tier1/fmtstr.h"
+
+#include <CommCtrl.h>
+
+class CProgressDialog : public IProgressDialog
+{
+public:
+ CProgressDialog();
+
+ void Start( char const *pchTitle, char const *pchText, bool bShowCancel );
+ void Update( float flZeroToOneFraction );
+ void UpdateText( char const *pchFmt, ... );
+ bool IsCancelled();
+ void Finish();
+
+ static BOOL CALLBACK ProgressDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+
+private:
+
+ BOOL ProgressDialogProcImpl( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+
+ CUtlString m_sTitle;
+ CUtlString m_sStatus;
+ float m_flFraction;
+
+ bool m_bShowCancel;
+ bool m_bWantsCancel;
+
+ HWND m_hwndDlg;
+
+ double m_flStartTime;
+};
+
+static CProgressDialog g_ProgressDialog;
+IProgressDialog *g_pProgressDialog = &g_ProgressDialog;
+
+CProgressDialog::CProgressDialog() :
+ m_flFraction( 0.0f ), m_hwndDlg( 0 ), m_bShowCancel( false ), m_bWantsCancel( false ), m_flStartTime( 0.0f )
+{
+}
+
+bool CProgressDialog::IsCancelled()
+{
+ return m_bShowCancel && m_bWantsCancel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+BOOL CALLBACK CProgressDialog::ProgressDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_ProgressDialog.ProgressDialogProcImpl( hwndDlg, uMsg, wParam, lParam );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+void CProgressDialog::Start( char const *pchTitle, char const *pchText, bool bShowCancel )
+{
+ if ( m_hwndDlg )
+ {
+ Finish();
+ }
+ Assert( NULL == m_hwndDlg );
+
+ m_sTitle = pchTitle;
+ m_sStatus = pchText;
+ m_flFraction = 0.0f;
+ m_bShowCancel = bShowCancel;
+ m_bWantsCancel = false;
+ m_flStartTime = Plat_FloatTime();
+
+ m_hwndDlg = CreateDialog( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_PROGRESS ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)ProgressDialogProc );
+}
+
+void CProgressDialog::Update( float flZeroToOneFraction )
+{
+ m_flFraction = clamp( flZeroToOneFraction, 0.0f, 1.0f );
+
+ // Update the progress bar
+ HWND pb = GetDlgItem( m_hwndDlg, IDC_FP_PROGRESS );
+ int pos = clamp( (int)( 1000.0f * flZeroToOneFraction ), 0, 1000 );
+
+ SendMessage( pb, (UINT) PBM_SETPOS, pos, 0 );
+
+ HWND btn = GetDlgItem( m_hwndDlg, IDCANCEL );
+ LRESULT lr = SendMessage( btn, BM_GETSTATE, 0, 0 );
+ if ( lr & BST_PUSHED )
+ {
+ m_bWantsCancel = true;
+ }
+
+ if ( GetAsyncKeyState( VK_ESCAPE ) )
+ {
+ m_bWantsCancel = true;
+ }
+
+ mx::check();
+}
+
+void CProgressDialog::UpdateText( char const *pchFmt, ... )
+{
+ char buf[ 2048 ];
+ va_list argptr;
+ va_start( argptr, pchFmt );
+ Q_vsnprintf( buf, sizeof( buf ), pchFmt, argptr );
+ va_end( argptr );
+ m_sStatus = buf;
+
+ SetDlgItemText( m_hwndDlg, IDC_FP_PROGRESS_TEXT, CFmtStr( "%s", m_sStatus.String() ) );
+ SetDlgItemText( m_hwndDlg, IDC_FP_PROGRESS_PERCENT, CFmtStr( "%.2f %%", m_flFraction * 100.0f ) );
+
+ double elapsed = Plat_FloatTime() - m_flStartTime;
+ double flPercentagePerSecond = 0.0f;
+ if ( m_flFraction > 0.0f )
+ {
+ flPercentagePerSecond = elapsed / m_flFraction;
+ }
+
+ double flSecondsRemaining = flPercentagePerSecond * ( 1.0f - m_flFraction );
+
+ int seconds = (int)flSecondsRemaining;
+
+ CFmtStr string;
+
+ int hours = 0;
+ int minutes = seconds / 60;
+
+ if ( minutes > 0 )
+ {
+ seconds -= (minutes * 60);
+ hours = minutes / 60;
+
+ if ( hours > 0 )
+ {
+ minutes -= (hours * 60);
+ }
+ }
+
+ if ( hours > 0 )
+ {
+ string.sprintf( "Time Remaining: %2i:%02i:%02i", hours, minutes, seconds );
+ }
+ else
+ {
+ string.sprintf( "Time Remaining: %02i:%02i", minutes, seconds );
+ }
+
+ SetDlgItemText( m_hwndDlg, IDC_FP_PROGRESS_ETA, string.Access() );
+}
+
+void CProgressDialog::Finish()
+{
+ if ( !m_hwndDlg )
+ return;
+ DestroyWindow( m_hwndDlg );
+ m_hwndDlg = NULL;
+}
+
+BOOL CProgressDialog::ProgressDialogProcImpl( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ RECT rcDlg;
+ GetWindowRect( hwndDlg, &rcDlg );
+
+ // Get relative to primary monitor instead of actual window parent
+ RECT rcParent;
+ rcParent.left = 0;
+ rcParent.right = rcParent.left + GetSystemMetrics( SM_CXFULLSCREEN );
+ rcParent.top = 0;
+ rcParent.bottom = rcParent.top + GetSystemMetrics( SM_CYFULLSCREEN );
+
+ int dialogw, dialogh;
+ int parentw, parenth;
+
+ parentw = rcParent.right - rcParent.left;
+ parenth = rcParent.bottom - rcParent.top;
+ dialogw = rcDlg.right - rcDlg.left;
+ dialogh = rcDlg.bottom - rcDlg.top;
+
+ int dlgleft, dlgtop;
+ dlgleft = ( parentw - dialogw ) / 2;
+ dlgtop = ( parenth - dialogh ) / 2;
+
+ MoveWindow( hwndDlg,
+ dlgleft,
+ dlgtop,
+ dialogw,
+ dialogh,
+ TRUE
+ );
+
+ SetDlgItemText( hwndDlg, IDC_FP_PROGRESS_TITLE, m_sTitle.String() );
+ SetDlgItemText( hwndDlg, IDC_FP_PROGRESS_TEXT, m_sStatus.String() );
+
+ HWND pb = GetDlgItem( hwndDlg, IDC_FP_PROGRESS );
+ SendMessage( pb, (UINT) PBM_SETRANGE, 0, MAKELPARAM( 0, 1000 ) );
+
+ ShowWindow( GetDlgItem( hwndDlg, IDCANCEL ), m_bShowCancel ? SW_SHOW : SW_HIDE );
+
+ Update( 0.0f );
+ }
+ return FALSE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ m_bWantsCancel = true;
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/ProgressDialog.h b/utils/hlfaceposer/ProgressDialog.h
new file mode 100644
index 0000000..b4a3192
--- /dev/null
+++ b/utils/hlfaceposer/ProgressDialog.h
@@ -0,0 +1,26 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef PROGRESSDIALOG_H
+#define PROGRESSDIALOG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class IProgressDialog
+{
+public:
+
+ virtual void Start( char const *pchTitle, char const *pchText, bool bShowCancel ) = 0;
+ virtual void Update( float flZeroToOneFraction ) = 0;
+ virtual void UpdateText( char const *pchFmt, ... ) = 0;
+ virtual bool IsCancelled() = 0;
+ virtual void Finish() = 0;
+};
+
+extern IProgressDialog *g_pProgressDialog;
+
+#endif // PROGRESSDIALOG_H
diff --git a/utils/hlfaceposer/RampTool.cpp b/utils/hlfaceposer/RampTool.cpp
new file mode 100644
index 0000000..519fc61
--- /dev/null
+++ b/utils/hlfaceposer/RampTool.cpp
@@ -0,0 +1,2379 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "RampTool.h"
+#include "mdlviewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "TimelineItem.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "choreoevent.h"
+#include "StudioModel.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "ChoreoView.h"
+#include "InputProperties.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include "mxExpressionTray.h"
+#include "ExpressionProperties.h"
+#include "tier1/strtools.h"
+#include "faceposer_models.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "iscenetokenprocessor.h"
+#include "choreoviewcolors.h"
+#include "MatSysWin.h"
+#include "curveeditorhelpers.h"
+#include "EdgeProperties.h"
+
+RampTool *g_pRampTool = 0;
+
+#define TRAY_HEIGHT 20
+#define TRAY_ITEM_INSET 10
+
+#define TAG_TOP ( TRAY_HEIGHT + 12 )
+#define TAG_BOTTOM ( TAG_TOP + 20 )
+
+#define MAX_TIME_ZOOM 1000
+// 10% per step
+#define TIME_ZOOM_STEP 2
+
+RampTool::RampTool( mxWindow *parent )
+: IFacePoserToolWindow( "RampTool", "Ramp" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_pHelper = new CCurveEditorHelper< RampTool >( this );
+
+ m_bSuppressLayout = false;
+
+ SetAutoProcess( true );
+
+ m_nFocusEventGlobalID = -1;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+ m_nDragType = DRAGTYPE_NONE;
+
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+
+ m_hPrevCursor = 0;
+
+ m_nStartX = 0;
+ m_nStartY = 0;
+
+ m_pLastEvent = NULL;
+
+ m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 500.0f;
+
+ m_flLastDuration = 0.0f;
+ m_nScrollbarHeight = 12;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_RAMPHSCROLL, mxScrollbar::Horizontal );
+ m_pHorzScrollBar->setVisible( false );
+
+ m_bInSetEvent = false;
+ m_flScrubberTimeOffset = 0.0f;
+
+ m_nUndoSetup = 0;
+}
+
+RampTool::~RampTool( void )
+{
+ delete m_pHelper;
+}
+
+void RampTool::SetEvent( CChoreoEvent *event )
+{
+ if ( m_bInSetEvent )
+ return;
+
+ m_bInSetEvent = true;
+
+ if ( event == m_pLastEvent )
+ {
+ if ( event )
+ {
+ if ( event->GetDuration() != m_flLastDuration )
+ {
+ m_flLastDuration = event->GetDuration();
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset = 0.0f;
+ InvalidateLayout();
+ }
+
+ m_nFocusEventGlobalID = event->GetGlobalID();
+ }
+
+ m_bInSetEvent = false;
+ return;
+ }
+
+ m_pLastEvent = event;
+
+ m_nFocusEventGlobalID = -1;
+ if ( event )
+ {
+ m_nFocusEventGlobalID = event->GetGlobalID();
+ }
+
+ if ( event )
+ {
+ m_flLastDuration = event->GetDuration();
+ }
+ else
+ {
+ m_flLastDuration = 0.0f;
+ }
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ InvalidateLayout();
+
+ m_bInSetEvent = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoEvent *RampTool::GetSafeEvent( void )
+{
+ if ( m_nFocusEventGlobalID == -1 )
+ return NULL;
+
+ if ( !g_pChoreoView )
+ return NULL;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return NULL;
+
+ // Find event by name
+ for ( int i = 0; i < scene->GetNumEvents() ; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e || !e->HasEndTime() )
+ continue;
+
+ if ( e->GetGlobalID() == m_nFocusEventGlobalID )
+ {
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void RampTool::GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped )
+{
+ float pixel = 0.0f;
+ if ( w2() > 0 )
+ {
+ pixel = GetPixelForTimeValue( scrub );
+
+ 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 : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void RampTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference )
+{
+ HBRUSH br = CreateSolidBrush( reference ? RGB( 150, 0, 0 ) : RGB( 0, 150, 100 ) );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", scrub );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ float st, ed;
+ st = ev->GetStartTime();
+ ed = ev->GetEndTime();
+
+ float dt = ed - st;
+ if ( dt > 0.0f )
+ {
+ sprintf( sz, "%.3f", st + scrub );
+ }
+ }
+
+ 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 RampTool::IsMouseOverScrubHandle( mxEvent *event )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, 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;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool RampTool::IsProcessing( void )
+{
+ if ( !GetSafeEvent() )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+bool RampTool::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+void RampTool::SetScrubTime( float t )
+{
+ m_flScrub = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + m_flScrub;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->DrawScrubHandle();
+ }
+}
+
+void RampTool::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + m_flScrubTarget;
+
+ g_pChoreoView->SetScrubTargetTime( realtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void RampTool::Think( float dt )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ bool scrubbing = IsScrubbing();
+ ScrubThink( dt, scrubbing );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void RampTool::ScrubThink( float dt, bool scrubbing )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ return;
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub + maxmove );
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub - maxmove );
+ }
+ }
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+}
+
+void RampTool::DrawScrubHandles()
+{
+ RECT rcTray;
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+
+ rcTray = rcHandle;
+ rcTray.left = 0;
+ rcTray.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcTray );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+}
+
+void RampTool::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ RECT rcText;
+ drawHelper.GetClientRect( rcText );
+ rcText.top += GetCaptionHeight()+1;
+ rcText.bottom = rcText.top + 13;
+ rcText.left += 5;
+ rcText.right -= 5;
+
+ OffsetRect( &rcText, 0, 12 );
+
+ int current, total;
+
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ RECT rcUndo = rcText;
+ OffsetRect( &rcUndo, 0, 2 );
+
+ drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
+ "Undo: %i/%i", current, total );
+ }
+
+ rcText.left += 60;
+
+ // Found it, write out description
+ //
+ RECT rcTextLine = rcText;
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine,
+ "Event: %s",
+ ev->GetName() );
+
+ RECT rcTimeLine;
+ drawHelper.GetClientRect( rcTimeLine );
+ rcTimeLine.left = 0;
+ rcTimeLine.right = w2();
+ rcTimeLine.top += ( GetCaptionHeight() + 50 );
+
+ float lefttime = GetTimeValueForMouse( 0 );
+ float righttime = GetTimeValueForMouse( w2() );
+
+ DrawTimeLine( drawHelper, rcTimeLine, lefttime, righttime );
+
+ OffsetRect( &rcText, 0, 28 );
+
+ rcText.left = 5;
+
+ RECT timeRect = rcText;
+
+ timeRect.right = timeRect.left + 100;
+
+ char sz[ 32 ];
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", lefttime + ev->GetStartTime() );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+
+ timeRect = rcText;
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", righttime + ev->GetStartTime() );
+
+ int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ timeRect.right = w2() - 10;
+ timeRect.left = timeRect.right - textW;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+ }
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+ DrawSamples( drawHelper, rcSamples );
+
+ DrawEventEnd( drawHelper );
+
+ RECT rcTags = rc;
+ rcTags.top = TAG_TOP + GetCaptionHeight();
+ rcTags.bottom = TAG_BOTTOM + GetCaptionHeight();
+
+ DrawTimingTags( drawHelper, rcTags );
+
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
+{
+ // Construct main menu
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ int current, total;
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ if ( current > 0 )
+ {
+ pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_RT );
+ }
+
+ if ( current <= total - 1 )
+ {
+ pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_RT );
+ }
+ pop->addSeparator();
+ }
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ if ( CountSelected() > 0 )
+ {
+ pop->add( va( "Delete" ), IDC_RT_DELETE );
+ pop->add( "Deselect all", IDC_RT_DESELECT );
+ }
+ pop->add( "Select all", IDC_RT_SELECTALL );
+ }
+
+ pop->add( va( "Change scale..." ), IDC_RT_CHANGESCALE );
+ pop->addSeparator();
+ pop->add( "Edge Properties...", IDC_RT_EDGEPROPERTIES );
+
+ pop->popup( this, (short)event->x, (short)event->y );
+}
+
+void RampTool::GetWorkspaceLeftRight( int& left, int& right )
+{
+ left = 0;
+ right = w2();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::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 );
+}
+
+void RampTool::SetClickedPos( int x, int y )
+{
+ m_nClickedX = x;
+ m_nClickedY = y;
+}
+
+float RampTool::GetTimeForClickedPos( void )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return 0.0f;
+
+ float t = GetTimeValueForMouse( m_nClickedX );
+ return t;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dragtype -
+// startx -
+// cursor -
+//-----------------------------------------------------------------------------
+void RampTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
+{
+ m_nDragType = dragtype;
+ m_nStartX = startx;
+ m_nLastX = startx;
+ m_nStartY = starty;
+ m_nLastY = starty;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ m_hPrevCursor = SetCursor( cursor );
+
+ m_FocusRects.Purge();
+
+ RECT rcStart;
+ rcStart.left = startx;
+ rcStart.right = startx;
+
+ bool addrect = true;
+ switch ( dragtype )
+ {
+ case DRAGTYPE_SCRUBBER:
+ {
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, m_flScrub, true );
+
+ rcStart = rcScrub;
+ rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcStart.right = rcStart.left;
+ rcStart.top = rcScrub.bottom;
+
+ rcStart.bottom = h2();
+ }
+ break;
+ default:
+ {
+ rcStart.top = starty;
+ rcStart.bottom = starty;
+ }
+ break;
+ }
+
+
+ if ( addrect )
+ {
+ AddFocusRect( rcStart );
+ }
+
+ DrawFocusRect();
+}
+
+void RampTool::OnMouseMove( mxEvent *event )
+{
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ event->x = (short)mx;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ {
+ OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), ( my - m_nStartY ) );
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ }
+
+ OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), 0 );
+ }
+ break;
+ case DRAGTYPE_MOVEPOINTS_TIME:
+ case DRAGTYPE_MOVEPOINTS_VALUE:
+ {
+ int dx = mx - m_nLastX;
+ int dy = my - m_nLastY;
+
+ if ( !( event->modifiers & mxEvent::KeyCtrl ) )
+ {
+ // Zero out motion on other axis
+ if ( m_nDragType == DRAGTYPE_MOVEPOINTS_VALUE )
+ {
+ dx = 0;
+ mx = m_nLastX;
+ }
+ else
+ {
+ dy = 0;
+ my = m_nLastY;
+ }
+ }
+ else
+ {
+ SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ int height = rcSamples.bottom - rcSamples.top;
+ Assert( height > 0 );
+
+ float dfdx = (float)dx / GetPixelsPerSecond();
+ float dfdy = (float)dy / (float)height;
+
+ MoveSelectedSamples( dfdx, dfdy );
+
+ // Update the scrubber
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ ForceScrubPosition( t );
+ g_pMatSysWindow->Frame();
+ }
+
+ OffsetRect( &f->m_rcFocus, dx, dy );
+ }
+ break;
+ case DRAGTYPE_SELECTION:
+ {
+ RECT rcFocus;
+
+ rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ f->m_rcFocus = rcFocus;
+ }
+ break;
+ }
+ }
+
+ DrawFocusRect();
+ }
+ else
+ {
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ /*
+ else if ( IsMouseOverTag( mx, my ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ */
+
+ // See if anything is selected
+ if ( CountSelected() <= 0 )
+ {
+ // Nothing selected
+ // Draw auto highlight
+ DrawAutoHighlight( event );
+ }
+ }
+
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+}
+
+int RampTool::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ // Give helper a shot at the event
+ if ( m_pHelper->HelperHandleEvent( event ) )
+ {
+ return 1;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ int w, h;
+ w = event->width;
+ h = event->height;
+
+ m_nLastHPixelsNeeded = 0;
+ InvalidateLayout();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int tz = g_pChoreoView->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 );
+ }
+
+ g_pChoreoView->SetPreservedTimeZoom( this, tz );
+ }
+
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+ bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
+
+ iret = 1;
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetClickedPos( mx, my );
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ bool insamplearea = PtInRect( &rcSamples, pt ) ? true : false;
+
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+
+ CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_RT_ADDSAMPLE_TOLERANCE : FP_RT_SELECTION_TOLERANCE );
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ if ( w2() > 0 )
+ {
+ 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;
+ ForceScrubPosition( t );
+ }
+
+ StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( insamplearea )
+ {
+ if ( sample )
+ {
+ if ( shiftdown )
+ {
+ sample->selected = !sample->selected;
+ redraw();
+ }
+ else if ( sample->selected )
+ {
+ PreDataChanged( "move ramp points" );
+
+ StartDragging(
+ rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE,
+ m_nClickedX, m_nClickedY,
+ LoadCursor( NULL, rightbutton ? IDC_SIZEWE : IDC_SIZENS ) );
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ DeselectAll();
+ }
+
+ StartDragging( DRAGTYPE_SELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_ARROW ) );
+ }
+ }
+ else if ( ctrldown )
+ {
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ // Add a sample point
+ float t = GetTimeValueForMouse( mx );
+
+ t = FacePoser_SnapTime( t );
+ float value = 1.0f - (float)( (short)event->y - rcSamples.top ) / (float)( rcSamples.bottom - rcSamples.top );
+ value = clamp( value, 0.0f, 1.0f );
+
+ PreDataChanged( "Add ramp point" );
+
+ e->AddRamp( t, value, false );
+
+ e->ResortRamp();
+
+ PostDataChanged( "Add ramp point" );
+ }
+ }
+ else
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ iret = 1;
+ return iret;
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ DeselectAll();
+ }
+
+ StartDragging( DRAGTYPE_SELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_ARROW ) );
+ }
+ }
+ }
+ else
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ iret = 1;
+ return iret;
+ }
+ else
+ {
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ SetScrubTargetTime( t );
+ }
+ }
+ }
+
+ CalcBounds( m_nDragType );
+ }
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ OnMouseMove( event );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ OnMouseMove( event );
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_NONE:
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ m_flScrubberTimeOffset = 0.0f;
+ }
+ }
+ break;
+ case DRAGTYPE_MOVEPOINTS_VALUE:
+ case DRAGTYPE_MOVEPOINTS_TIME:
+ {
+ PostDataChanged( "move ramp points" );
+ }
+ break;
+ case DRAGTYPE_SELECTION:
+ {
+ SelectPoints();
+ }
+ break;
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ redraw();
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_UNDO_RT:
+ {
+ OnUndo();
+ }
+ break;
+ case IDC_REDO_RT:
+ {
+ OnRedo();
+ }
+ break;
+ case IDC_RT_DELETE:
+ {
+ Delete();
+ }
+ break;
+ case IDC_RT_DESELECT:
+ {
+ DeselectAll();
+ }
+ break;
+ case IDC_RT_SELECTALL:
+ {
+ SelectAll();
+ }
+ break;
+ case IDC_RAMPHSCROLL:
+ {
+ 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_RT_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ case IDC_RT_EDGEPROPERTIES:
+ {
+ OnEdgeProperties();
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ iret = 1;
+ switch ( event->key )
+ {
+ default:
+ iret = g_pChoreoView->HandleZoomKey( this, event->key );
+ break;
+ case VK_ESCAPE:
+ DeselectAll();
+ break;
+ case VK_DELETE:
+ Delete();
+ break;
+ }
+ }
+ }
+ return iret;
+}
+
+void RampTool::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+void RampTool::CalcBounds( int movetype )
+{
+ switch ( movetype )
+ {
+ default:
+ case DRAGTYPE_NONE:
+ {
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ m_bUseBounds = true;
+ m_nMinX = 0;
+ m_nMaxX = w2();
+ }
+ break;
+ }
+}
+
+bool RampTool::PaintBackground()
+{
+ redraw();
+ return false;
+}
+
+void RampTool::OnUndo( void )
+{
+ g_pChoreoView->Undo();
+}
+
+void RampTool::OnRedo( void )
+{
+ g_pChoreoView->Redo();
+}
+
+void RampTool::ForceScrubPositionFromSceneTime( float scenetime )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e || !e->GetDuration() )
+ return;
+
+ float t = scenetime - e->GetStartTime();
+ m_flScrub = t;
+ m_flScrubTarget = t;
+ DrawScrubHandles();
+}
+
+void RampTool::ForceScrubPosition( float t )
+{
+ m_flScrub = t;
+ m_flScrubTarget = t;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e && e->GetDuration() )
+ {
+ float realtime = e->GetStartTime() + t;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->SetScrubTargetTime( realtime );
+
+ g_pChoreoView->DrawScrubHandle();
+ }
+
+ DrawScrubHandles();
+}
+
+void RampTool::SetMouseOverPos( int x, int y )
+{
+ m_nMousePos[ 0 ] = x;
+ m_nMousePos[ 1 ] = y;
+}
+
+void RampTool::GetMouseOverPos( int &x, int& y )
+{
+ x = m_nMousePos[ 0 ];
+ y = m_nMousePos[ 1 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcPos -
+//-----------------------------------------------------------------------------
+void RampTool::GetMouseOverPosRect( RECT& rcPos )
+{
+ rcPos.top = GetCaptionHeight() + 12;
+ rcPos.left = w2() - 200;
+ rcPos.right = w2() - 5;
+ rcPos.bottom = rcPos.top + 13;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcPos -
+//-----------------------------------------------------------------------------
+void RampTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
+{
+ // Compute time for pixel x
+ float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ t += e->GetStartTime();
+ float snapped = FacePoser_SnapTime( t );
+
+ // Found it, write out description
+ //
+ char sz[ 128 ];
+ if ( t != snapped )
+ {
+ Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), "%.3f", t );
+ }
+
+ int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
+
+ RECT rcText = rcPos;
+ rcText.left = max( rcPos.left, rcPos.right - len );
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::DrawMouseOverPos()
+{
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+void RampTool::AddFocusRect( RECT& rc )
+{
+ RECT rcFocus = rc;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ // Convert to screen space?
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+// left -
+// right -
+//-----------------------------------------------------------------------------
+void RampTool::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right )
+{
+ RECT rcLabel;
+ float granularity = 0.5f;
+
+ drawHelper.DrawColoredLine( RGB( 150, 150, 200 ), PS_SOLID, 1, rc.left, rc.top + 2, rc.right, rc.top + 2 );
+
+ float f = SnapTime( left, granularity );
+ while ( f < right )
+ {
+ float frac = ( f - left ) / ( right - left );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ rcLabel.left = GetPixelForTimeValue( f );
+ rcLabel.top = rc.top + 5;
+ rcLabel.bottom = rcLabel.top + 10;
+
+ if ( f != left )
+ {
+ drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1,
+ rcLabel.left, rc.top, 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 );
+
+ RECT rcOut = rcLabel;
+ if ( rcOut.left <= 0 )
+ {
+ OffsetRect( &rcOut, -rcOut.left + 2, 0 );
+ }
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 50, 150 ), rcOut, sz );
+
+ }
+ f += granularity;
+ }
+}
+
+void RampTool::DrawTimingTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ CChoreoEvent *rampevent = GetSafeEvent();
+ if ( !rampevent )
+ return;
+
+ CChoreoScene *scene = rampevent->GetScene();
+ if ( !scene )
+ return;
+
+ float starttime = GetTimeValueForMouse( 0 );
+ float endtime = GetTimeValueForMouse( w2() );
+
+ if ( endtime - starttime <= 0.0f )
+ return;
+
+ RECT rcText = rc;
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, "Timing Tags:" );
+
+ // Loop through all events in scene
+
+ int c = scene->GetNumEvents();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e )
+ continue;
+
+ // See if time overlaps
+ if ( !e->HasEndTime() )
+ continue;
+
+ if ( ( e->GetEndTime() - e->GetStartTime() ) < starttime )
+ continue;
+
+ if ( ( e->GetStartTime() - e->GetStartTime() ) > endtime )
+ continue;
+
+ if ( e->GetNumRelativeTags() > 0 )
+ {
+ DrawRelativeTagsForEvent( drawHelper, rc, rampevent, e, starttime, endtime );
+ }
+ if ( e->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) > 0 )
+ {
+ DrawAbsoluteTagsForEvent( drawHelper, rc, rampevent, e, starttime, endtime );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// &rc -
+//-----------------------------------------------------------------------------
+void RampTool::DrawAbsoluteTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT &rc, CChoreoEvent *rampevent, CChoreoEvent *event, float starttime, float endtime )
+{
+ if ( !event )
+ return;
+
+ for ( int i = 0; i < event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+ if ( !tag )
+ continue;
+
+ float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() ) - rampevent->GetStartTime();
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ bool clipped = false;
+ int left = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ // Don't add gesture tags except for the current event
+ if ( event != rampevent &&
+ event->GetType() == CChoreoEvent::GESTURE )
+ {
+ continue;
+ }
+
+ COLORREF clr = event == rampevent ? RGB( 0, 100, 250 ) : RGB( 100, 100, 100 );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, clr );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 12;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void RampTool::DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *rampevent, CChoreoEvent *event, float starttime, float endtime )
+{
+ if ( !event )
+ return;
+
+ //drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" );
+
+ for ( int i = 0; i < event->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() ) - rampevent->GetStartTime();
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ bool clipped = false;
+ int left = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ //float frac = ( tagtime - starttime ) / ( endtime - starttime );
+
+ //int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f );
+
+ COLORREF clr = event == rampevent ? RGB( 0, 100, 250 ) : RGB( 100, 100, 100 );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, clr );
+
+ RECT rcText;
+ rcText = rc;
+ rcText.bottom = rc.bottom - 10;
+ rcText.top = rcText.bottom - 10;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int RampTool::ComputeHPixelsNeeded( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return 0;
+
+ int pixels = 0;
+ float maxtime = event->GetDuration();
+ pixels = (int)( ( maxtime ) * GetPixelsPerSecond() + 10 );
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ if ( pixelsneeded <= w2() - 10 )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2() - 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( w2() - 10 );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float RampTool::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void RampTool::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = (float)x;
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ m_bLayoutIsValid = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : st -
+// ed -
+//-----------------------------------------------------------------------------
+void RampTool::GetStartAndEndTime( float& st, float& ed )
+{
+ st = m_flLeftOffset / GetPixelsPerSecond();
+ ed = st + (float)w2() / GetPixelsPerSecond();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : float
+//-----------------------------------------------------------------------------
+float RampTool::GetEventEndTime()
+{
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( !ev )
+ return 1.0f;
+
+ return ev->GetDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int RampTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ float frac = ( time - st ) / ( ed - st );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = ( int )( frac * w2() );
+ return pixel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float RampTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ if ( clip )
+ {
+ if ( mx < 0 )
+ {
+ return st;
+ }
+ if ( mx > w2() )
+ {
+ return ed;
+ }
+ }
+
+ float frac = (float)( mx ) / (float)( w2() );
+ return st + frac * ( ed - st );
+}
+
+void RampTool::OnChangeScale( void )
+{
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change Zoom" );
+ strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset= 0.0f;
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
+}
+
+void RampTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+ if ( !duration )
+ return;
+
+ int leftx = GetPixelForTimeValue( duration );
+ if ( leftx >= w2() )
+ return;
+
+ RECT rcSample;
+ GetSampleTrayRect( rcSample );
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, rcSample.top, leftx, rcSample.bottom );
+}
+
+void RampTool::GetSampleTrayRect( RECT& rc )
+{
+ rc.left = 0;
+ rc.right = w2();
+ rc.top = GetCaptionHeight() + 65;
+
+ rc.bottom = h2() - m_nScrollbarHeight-2;
+}
+
+void RampTool::DrawSamplesSimple( CChoreoWidgetDrawHelper& drawHelper, CChoreoEvent *e, bool clearbackground, COLORREF sampleColor, RECT &rcSamples )
+{
+ if ( clearbackground )
+ {
+ drawHelper.DrawFilledRect( RGB( 230, 230, 215 ), rcSamples );
+ }
+
+ if ( !e )
+ return;
+
+ float starttime = e->GetStartTime();
+
+ COLORREF lineColor = sampleColor;
+
+ int width = rcSamples.right - rcSamples.left;
+ if ( width <= 0.0f )
+ return;
+
+ int height = rcSamples.bottom - rcSamples.top;
+ int bottom = rcSamples.bottom;
+
+ float timestepperpixel = e->GetDuration() / (float)width;
+
+ float prev_value = e->GetIntensity( starttime );
+ int prev_x = rcSamples.left;
+ float prev_t = 0.0f;
+
+ for ( float x = rcSamples.left; x < rcSamples.right; x+=3 )
+ {
+ float t = (float)( x - rcSamples.left ) * timestepperpixel;
+
+ float value = e->GetIntensity( starttime + t );
+
+ // Draw segment
+ drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ prev_x, bottom - prev_value * height,
+ x, bottom - value * height );
+
+ prev_x = x;
+ prev_t = t;
+ prev_value = value;
+ }
+}
+
+void RampTool::DrawSamples( CChoreoWidgetDrawHelper& drawHelper, RECT &rcSamples )
+{
+ drawHelper.DrawFilledRect( RGB( 230, 230, 215 ), rcSamples );
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ int rampCount = e->GetRampCount();
+ if ( !rampCount )
+ return;
+
+ float starttime;
+ float endtime;
+
+ GetStartAndEndTime( starttime, endtime );
+
+ COLORREF lineColor = RGB( 0, 0, 255 );
+ COLORREF dotColor = RGB( 0, 0, 255 );
+ COLORREF dotColorSelected = RGB( 240, 80, 20 );
+ COLORREF shadowColor = RGB( 150, 150, 250 );
+
+ int height = rcSamples.bottom - rcSamples.top;
+ int bottom = rcSamples.bottom;
+ int top = rcSamples.top;
+
+ float timestepperpixel = 1.0f / GetPixelsPerSecond();
+
+ float stoptime = min( endtime, e->GetDuration() );
+
+ float prev_t = starttime;
+ float prev_value = e->GetIntensity( prev_t );
+
+ if ( 0 )
+ {
+ COLORREF shadowColor = RGB( 150, 150, 250 );
+
+ // draw hermite version of time step
+ float i0, i1, i2;
+ float time10hz = starttime;
+
+ i0 = e->GetIntensity( time10hz + e->GetStartTime() );
+ i1 = i0;
+ time10hz = starttime + 0.1;
+ i2 = e->GetIntensity( time10hz + e->GetStartTime() );;
+
+ for ( float t = starttime-timestepperpixel; t <= stoptime; t += timestepperpixel )
+ {
+ while (t >= time10hz)
+ {
+ time10hz += 0.1;
+ i0 = i1;
+ i1 = i2;
+ i2 = e->GetIntensity( time10hz + e->GetStartTime() );
+
+ bool clipped;
+ int x = GetPixelForTimeValue( time10hz, &clipped );
+ int y = bottom - i2 * height;
+ int dotsize = 4;
+
+ drawHelper.DrawCircle(
+ shadowColor,
+ x, y,
+ dotsize,
+ false );
+ }
+
+ float value = Hermite_Spline( i0, i1, i2, (t - time10hz + 0.1) / 0.1 );
+
+ int prevx, x;
+
+ bool clipped1, clipped2;
+ x = GetPixelForTimeValue( t, &clipped1 );
+ prevx = GetPixelForTimeValue( prev_t, &clipped2 );
+
+ if ( !clipped1 && !clipped2 )
+ {
+ // Draw segment
+ drawHelper.DrawColoredLine( shadowColor, PS_SOLID, 1,
+ prevx, clamp( bottom - prev_value * height, top, bottom ),
+ x, clamp( bottom - value * height, top, bottom ) );
+ }
+
+ prev_t = t;
+ prev_value = value;
+ }
+ }
+
+
+
+ for ( float t = starttime-timestepperpixel; t <= stoptime; t += timestepperpixel )
+ {
+ float value = e->GetIntensity( t + e->GetStartTime() );
+
+ int prevx, x;
+
+ bool clipped1, clipped2;
+ x = GetPixelForTimeValue( t, &clipped1 );
+ prevx = GetPixelForTimeValue( prev_t, &clipped2 );
+
+ if ( !clipped1 && !clipped2 )
+ {
+ // Draw segment
+ drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ prevx, bottom - prev_value * height,
+ x, bottom - value * height );
+ }
+
+ prev_t = t;
+ prev_value = value;
+
+ }
+
+ for ( int sample = 0; sample < rampCount; sample++ )
+ {
+ CExpressionSample *start = e->GetRamp( sample );
+
+ /*
+ int pixel = (int)( ( start->time / event_time ) * width + 0.5f);
+ int x = m_rcBounds.left + pixel;
+ float roundedfrac = (float)pixel / (float)width;
+ */
+ float value = start->value;
+ bool clipped = false;
+ int x = GetPixelForTimeValue( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+
+ if ( !start->selected )
+ continue;
+
+ if ( start->GetCurveType() == CURVE_DEFAULT )
+ continue;
+
+ // Draw curve type indicator...
+ char sz[ 128 ];
+ Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) );
+ RECT rc;
+ int fontSize = 9;
+ rc.top = clamp( y + 5, rcSamples.top + 2, rcSamples.bottom - 2 - fontSize );
+ rc.bottom = rc.top + fontSize + 1;
+ rc.left = x - 75;
+ rc.right = x + 175;
+ drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz );
+ }
+}
+
+void RampTool::DrawAutoHighlight( mxEvent *event )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f );
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcSamples, true );
+
+ RECT rcClient = rcSamples;
+
+ COLORREF dotColor = RGB( 0, 0, 255 );
+ COLORREF dotColorSelected = RGB( 240, 80, 20 );
+ COLORREF clrHighlighted = RGB( 0, 200, 0 );
+
+ int height = rcClient.bottom - rcClient.top;
+ int bottom = rcClient.bottom;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+ int dotSizeHighlighted = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+ COLORREF bgColor = RGB( 230, 230, 200 );
+
+ // Fixme, could look at 1st derivative and do more sampling at high rate of change?
+ // or near actual sample points!
+ int sampleCount = e->GetRampCount();
+ for ( int sample = 0; sample < sampleCount; sample++ )
+ {
+ CExpressionSample *start = e->GetRamp( sample );
+
+ float value = start->value;
+ bool clipped = false;
+ int x = GetPixelForTimeValue( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ if ( hover == start )
+ {
+ drawHelper.DrawCircle(
+ bgColor,
+ x, y,
+ dotSizeHighlighted,
+ true );
+
+ drawHelper.DrawCircle(
+ clrHighlighted,
+ x, y,
+ dotSizeHighlighted,
+ false );
+
+
+ }
+ else
+ {
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+ }
+ }
+}
+
+int RampTool::NumSamples()
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return 0;
+
+ return e->GetRampCount();
+}
+
+CExpressionSample *RampTool::GetSample( int idx )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return NULL;
+
+ return e->GetRamp( idx );
+}
+
+CExpressionSample *RampTool::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_RT_SELECTION_TOLERANCE*/ )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return NULL;
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ if ( !PtInRect( &rcSamples, pt ) )
+ return NULL;
+
+ pt.y -= rcSamples.top;
+
+ float closest_dist = 9999999.f;
+ CExpressionSample *bestsample = NULL;
+
+ int height = rcSamples.bottom - rcSamples.top;
+
+ for ( int i = 0; i < e->GetRampCount(); i++ )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+ Assert( sample );
+
+ bool clipped = false;
+ int px = GetPixelForTimeValue( sample->time, &clipped );
+ int py = height * ( 1.0f - sample->value );
+
+ int dx = px - pt.x;
+ int dy = py - pt.y;
+
+ float dist = sqrt( (float)(dx * dx + dy * dy) );
+
+ if ( dist < closest_dist )
+ {
+ bestsample = sample;
+ closest_dist = dist;
+ }
+
+ }
+
+ // Not close to any of them!!!
+ if ( ( tolerance != 0.0f ) &&
+ ( closest_dist > tolerance ) )
+ return NULL;
+
+ return bestsample;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::SelectPoints( void )
+{
+ RECT rcSelection;
+
+ rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ int selW = rcSelection.right - rcSelection.left;
+ int selH = rcSelection.bottom - rcSelection.top;
+
+ float tolerance = FP_RT_SELECTION_RECTANGLE_TOLERANCE;
+ // If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance
+ if ( selW <= 2 && selH <= 2 )
+ {
+ tolerance = FP_RT_SELECTION_TOLERANCE;
+
+ CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f );
+ if ( sample )
+ {
+ sample->selected = true;
+ return;
+ }
+ }
+ else
+ {
+ InflateRect( &rcSelection, 3, 3 );
+ }
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ int height = rcSamples.bottom - rcSamples.top;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+
+ float fleft = (float)GetTimeValueForMouse( rcSelection.left );
+ float fright = (float)GetTimeValueForMouse( rcSelection.right );
+
+ //fleft *= duration;
+ //fright *= duration;
+
+ float ftop = (float)( rcSelection.top - rcSamples.top ) / (float)height;
+ float fbottom = (float)( rcSelection.bottom - rcSamples.top ) / (float)height;
+
+ fleft = clamp( fleft, 0.0f, duration );
+ fright = clamp( fright, 0.0f, duration );
+ ftop = clamp( ftop, 0.0f, 1.0f );
+ fbottom = clamp( fbottom, 0.0f, 1.0f );
+
+ float timestepperpixel = 1.0f / GetPixelsPerSecond();
+ float yfracstepperpixel = 1.0f / (float)height;
+
+ float epsx = tolerance*timestepperpixel;
+ float epsy = tolerance*yfracstepperpixel;
+
+ for ( int i = 0; i < e->GetRampCount(); i++ )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+
+ if ( sample->time + epsx < fleft )
+ continue;
+
+ if ( sample->time - epsx > fright )
+ continue;
+
+ if ( (1.0f - sample->value ) + epsy < ftop )
+ continue;
+
+ if ( (1.0f - sample->value ) - epsy > fbottom )
+ continue;
+
+ sample->selected = true;
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int RampTool::CountSelected( void )
+{
+ return m_pHelper->CountSelected( false );
+}
+
+void RampTool::MoveSelectedSamples( float dfdx, float dfdy )
+{
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ int c = e->GetRampCount();
+
+ float duration = e->GetDuration();
+ //dfdx *= duration;
+
+ for ( int i = 0; i < c; i++ )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+ if ( !sample || !sample->selected )
+ continue;
+
+ sample->time += dfdx;
+ sample->time = clamp( sample->time, 0.0f, duration );
+
+ sample->value -= dfdy;
+ sample->value = clamp( sample->value, 0.0f, 1.0f );
+ }
+
+ e->ResortRamp();
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void RampTool::DeselectAll( void )
+{
+ int i;
+
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ CChoreoEvent *e = GetSafeEvent();
+ Assert( e );
+ if ( !e )
+ return;
+
+ for ( i = e->GetRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+ sample->selected = false;
+ }
+
+ redraw();
+}
+
+void RampTool::SelectAll( void )
+{
+ int i;
+
+ CChoreoEvent *e = GetSafeEvent();
+ Assert( e );
+ if ( !e )
+ return;
+
+ for ( i = e->GetRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+ sample->selected = true;
+ }
+
+ redraw();
+}
+
+void RampTool::Delete( void )
+{
+ int i;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ PreDataChanged( "Delete ramp points" );
+
+ for ( i = e->GetRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = e->GetRamp( i );
+ if ( !sample->selected )
+ continue;
+
+ e->DeleteRamp( i );
+ }
+
+ PostDataChanged( "Delete ramp points" );
+}
+
+void RampTool::OnModelChanged()
+{
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *undodescription -
+//-----------------------------------------------------------------------------
+void RampTool::PreDataChanged( char const *undodescription )
+{
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undodescription );
+ }
+ ++m_nUndoSetup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *redodescription -
+//-----------------------------------------------------------------------------
+void RampTool::PostDataChanged( char const *redodescription )
+{
+ --m_nUndoSetup;
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->PushRedo( redodescription );
+ redraw();
+ }
+}
+
+
+void RampTool::SetMousePositionForEvent( mxEvent *event )
+{
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ event->x = pt.x;
+ event->y = pt.y;
+}
+
+void RampTool::OnEdgeProperties()
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ CEdgePropertiesParams params;
+ Q_memset( &params, 0, sizeof( params ) );
+ Q_strcpy( params.m_szDialogTitle, "Edge Properties" );
+
+ params.SetFromCurve( e->GetRamp() );
+
+ if ( !EdgeProperties( &params ) )
+ {
+ return;
+ }
+
+ char *undotext = "Change Event Ramp Edge Properties";
+
+ PreDataChanged( undotext );
+
+ // Apply changes.
+ params.ApplyToCurve( e->GetRamp() );
+
+ PostDataChanged( undotext );
+}
+
+void RampTool::GetWorkList( bool reflect, CUtlVector< RampTool * >& list )
+{
+ NOTE_UNUSED( reflect );
+ list.AddToTail( this );
+}
diff --git a/utils/hlfaceposer/RampTool.h b/utils/hlfaceposer/RampTool.h
new file mode 100644
index 0000000..b4fc9de
--- /dev/null
+++ b/utils/hlfaceposer/RampTool.h
@@ -0,0 +1,205 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef RAMPTOOL_H
+#define RAMPTOOL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "studio.h"
+#include "utlvector.h"
+#include "faceposertoolwindow.h"
+
+class CChoreoEvent;
+class CChoreoWidgetDrawHelper;
+class CChoreoView;
+struct CExpressionSample;
+
+#define IDC_REDO_RT 1000
+#define IDC_UNDO_RT 1001
+
+#define IDC_RT_DELETE 1002
+#define IDC_RT_DESELECT 1003
+#define IDC_RT_SELECTALL 1004
+
+#define IDC_RT_CHANGESCALE 1005
+#define IDC_RAMPHSCROLL 1006
+#define IDC_RT_EDGEPROPERTIES 1007
+
+#define FP_RT_SELECTION_TOLERANCE 30.0f
+#define FP_RT_SELECTION_RECTANGLE_TOLERANCE 5.0f
+#define FP_RT_ADDSAMPLE_TOLERANCE 5.0f
+
+template< class T > class CCurveEditorHelper;
+
+class RampTool : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ // Construction
+ RampTool( mxWindow *parent );
+ ~RampTool( void );
+
+ virtual void Think( float dt );
+ void ScrubThink( float dt, bool scrubbing );
+ virtual bool IsScrubbing( void ) const;
+ virtual bool IsProcessing( void );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground();
+
+ void SetEvent( CChoreoEvent *event );
+
+ void GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped = false );
+
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference );
+ void DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right );
+ void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
+
+ void SetMouseOverPos( int x, int y );
+ void GetMouseOverPos( int &x, int& y );
+ void GetMouseOverPosRect( RECT& rcPos );
+ void DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos );
+ void DrawMouseOverPos();
+
+ void DrawScrubHandles();
+
+ CChoreoEvent *GetSafeEvent( void );
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ void ForceScrubPosition( float newtime );
+ void ForceScrubPositionFromSceneTime( float scenetime );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+
+ void DrawSamplesSimple( CChoreoWidgetDrawHelper& drawHelper, CChoreoEvent *e, bool clearbackground, COLORREF sampleColor, RECT &rcSamples );
+ virtual void OnModelChanged();
+
+ void SetMousePositionForEvent( mxEvent *event );
+
+ int NumSamples();
+ CExpressionSample *GetSample( int idx );
+ void PreDataChanged( char const *undodescription );
+ void PostDataChanged( char const *redodescription );
+ CExpressionSample *GetSampleUnderMouse( int mx, int my, float tolerance = FP_RT_SELECTION_TOLERANCE );
+ void GetWorkList( bool reflect, CUtlVector< RampTool * >& list );
+
+private:
+
+ void GetSampleTrayRect( RECT& rc );
+ void DrawSamples( CChoreoWidgetDrawHelper& drawHelper, RECT &rcSamples );
+
+ void SelectPoints( void );
+ void DeselectAll();
+ void SelectAll();
+ void Delete( void );
+
+ int CountSelected( void );
+ void MoveSelectedSamples( float dfdx, float dfdy );
+
+ void StartDragging( int dragtype, int startx, int starty, HCURSOR cursor );
+ void AddFocusRect( RECT& rc );
+ void OnMouseMove( mxEvent *event );
+ void DrawFocusRect( void );
+ void ShowContextMenu( mxEvent *event, bool include_track_menus );
+ void GetWorkspaceLeftRight( int& left, int& right );
+ void SetClickedPos( int x, int y );
+ float GetTimeForClickedPos( void );
+
+ void DrawAutoHighlight( mxEvent *event );
+
+ void ApplyBounds( int& mx, int& my );
+ void CalcBounds( int movetype );
+ void OnUndo( void );
+ void OnRedo( void );
+
+ //CEventAbsoluteTag *IsMouseOverTag( int mx, int my );
+ void OnRevert( void );
+
+ void OnEdgeProperties();
+
+ void DrawTimingTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+ void DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *rampevent, CChoreoEvent *event, float starttime, float endtime );
+ void DrawAbsoluteTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT &rc, CChoreoEvent *rampevent, CChoreoEvent *event, float starttime, float endtime );
+
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+ void OnChangeScale();
+ int ComputeHPixelsNeeded( void );
+ float GetPixelsPerSecond( void );
+ void InvalidateLayout( void );
+ void RepositionHSlider( void );
+ void GetStartAndEndTime( float& st, float& ed );
+ float GetEventEndTime();
+ float GetTimeValueForMouse( int mx, bool clip = false );
+ int GetPixelForTimeValue( float time, bool *clipped = NULL );
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_SCRUBBER,
+ DRAGTYPE_MOVEPOINTS_VALUE,
+ DRAGTYPE_MOVEPOINTS_TIME,
+ DRAGTYPE_SELECTION,
+ };
+
+ int m_nFocusEventGlobalID;
+
+ int m_nMousePos[ 2 ];
+
+ bool m_bUseBounds;
+ int m_nMinX;
+ int m_nMaxX;
+
+ HCURSOR m_hPrevCursor;
+ int m_nDragType;
+
+ int m_nStartX;
+ int m_nStartY;
+ int m_nLastX;
+ int m_nLastY;
+
+ int m_nClickedX;
+ int m_nClickedY;
+
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+ CChoreoEvent *m_pLastEvent;
+
+ bool m_bSuppressLayout;
+ // Height/width of scroll bars
+ int m_nScrollbarHeight;
+ float m_flLeftOffset;
+ mxScrollbar *m_pHorzScrollBar;
+ int m_nLastHPixelsNeeded;
+ // How many pixels per second we are showing in the UI
+ float m_flPixelsPerSecond;
+ // Do we need to move controls?
+ bool m_bLayoutIsValid;
+ float m_flLastDuration;
+ bool m_bInSetEvent;
+ float m_flScrubberTimeOffset;
+ int m_nUndoSetup;
+
+ CCurveEditorHelper< RampTool > *m_pHelper;
+ friend class CChoreoView;
+};
+
+extern RampTool *g_pRampTool;
+
+#endif // RAMPTOOL_H
diff --git a/utils/hlfaceposer/SceneRampTool.cpp b/utils/hlfaceposer/SceneRampTool.cpp
new file mode 100644
index 0000000..4cedbdb
--- /dev/null
+++ b/utils/hlfaceposer/SceneRampTool.cpp
@@ -0,0 +1,2244 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "SceneRampTool.h"
+#include "mdlviewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "TimelineItem.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "choreoevent.h"
+#include "StudioModel.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "ChoreoView.h"
+#include "InputProperties.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include "mxExpressionTray.h"
+#include "ExpressionProperties.h"
+#include "tier1/strtools.h"
+#include "faceposer_models.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "iscenetokenprocessor.h"
+#include "choreoviewcolors.h"
+#include "MatSysWin.h"
+#include "curveeditorhelpers.h"
+#include "EdgeProperties.h"
+
+SceneRampTool *g_pSceneRampTool = 0;
+
+#define TRAY_HEIGHT 20
+#define TRAY_ITEM_INSET 10
+
+#define TAG_TOP ( TRAY_HEIGHT + 12 )
+#define TAG_BOTTOM ( TAG_TOP + 20 )
+
+#define MAX_TIME_ZOOM 1000
+// 10% per step
+#define TIME_ZOOM_STEP 2
+
+SceneRampTool::SceneRampTool( mxWindow *parent )
+: IFacePoserToolWindow( "SceneRampTool", "Scene Ramp" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_pHelper = new CCurveEditorHelper< SceneRampTool >( this );
+
+ m_bSuppressLayout = false;
+
+ SetAutoProcess( true );
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+ m_nDragType = DRAGTYPE_NONE;
+
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+
+ m_hPrevCursor = 0;
+
+ m_nStartX = 0;
+ m_nStartY = 0;
+
+ m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 500.0f;
+
+ m_flLastDuration = 0.0f;
+ m_nScrollbarHeight = 12;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_SRT_RAMPHSCROLL, mxScrollbar::Horizontal );
+ m_pHorzScrollBar->setVisible( false );
+
+ m_flScrubberTimeOffset = 0.0f;
+
+ m_nUndoSetup = 0;
+}
+
+SceneRampTool::~SceneRampTool( void )
+{
+ delete m_pHelper;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoScene *SceneRampTool::GetSafeScene( void )
+{
+ if ( !g_pChoreoView )
+ return NULL;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return NULL;
+
+ return scene;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void SceneRampTool::GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped )
+{
+ float pixel = 0.0f;
+ if ( w2() > 0 )
+ {
+ pixel = GetPixelForTimeValue( scrub );
+
+ 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 : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference )
+{
+ HBRUSH br = CreateSolidBrush( reference ? RGB( 150, 0, 0 ) : RGB( 0, 150, 100 ) );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", scrub );
+
+ CChoreoScene *scene = GetSafeScene();
+ if ( scene )
+ {
+ float st, ed;
+ st = 0.0f;
+ ed = scene->FindStopTime();
+
+ float dt = ed - st;
+ if ( dt > 0.0f )
+ {
+ sprintf( sz, "%.3f", st + scrub );
+ }
+ }
+
+ 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 SceneRampTool::IsMouseOverScrubHandle( mxEvent *event )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, 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;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool SceneRampTool::IsProcessing( void )
+{
+ if ( !GetSafeScene() )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+bool SceneRampTool::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+void SceneRampTool::SetScrubTime( float t )
+{
+ m_flScrub = t;
+ CChoreoScene *scene = GetSafeScene();
+ if ( scene && scene->FindStopTime() )
+ {
+ float realtime = m_flScrub;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->DrawScrubHandle();
+ }
+}
+
+void SceneRampTool::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+ CChoreoScene *scene = GetSafeScene();
+ if ( scene && scene->FindStopTime() )
+ {
+ float realtime = m_flScrubTarget;
+
+ g_pChoreoView->SetScrubTargetTime( realtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void SceneRampTool::Think( float dt )
+{
+ if ( !GetSafeScene() )
+ return;
+
+ bool scrubbing = IsScrubbing();
+ ScrubThink( dt, scrubbing );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void SceneRampTool::ScrubThink( float dt, bool scrubbing )
+{
+ if ( !GetSafeScene() )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ return;
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub + maxmove );
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub - maxmove );
+ }
+ }
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+}
+
+void SceneRampTool::DrawScrubHandles()
+{
+ RECT rcTray;
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+
+ rcTray = rcHandle;
+ rcTray.left = 0;
+ rcTray.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcTray );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+}
+
+void SceneRampTool::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ CChoreoScene *scene = GetSafeScene();
+ if ( scene )
+ {
+ RECT rcText;
+ drawHelper.GetClientRect( rcText );
+ rcText.top += GetCaptionHeight()+1;
+ rcText.bottom = rcText.top + 13;
+ rcText.left += 5;
+ rcText.right -= 5;
+
+ OffsetRect( &rcText, 0, 12 );
+
+ int current, total;
+
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ RECT rcUndo = rcText;
+ OffsetRect( &rcUndo, 0, 2 );
+
+ drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
+ "Undo: %i/%i", current, total );
+ }
+
+ rcText.left += 60;
+
+ // Found it, write out description
+ //
+ RECT rcTextLine = rcText;
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 0, 0 ), rcTextLine,
+ "Scene: %s",
+ g_pChoreoView->GetChoreoFile() );
+
+ RECT rcTimeLine;
+ drawHelper.GetClientRect( rcTimeLine );
+ rcTimeLine.left = 0;
+ rcTimeLine.right = w2();
+ rcTimeLine.top += ( GetCaptionHeight() + 50 );
+
+ float lefttime = GetTimeValueForMouse( 0 );
+ float righttime = GetTimeValueForMouse( w2() );
+
+ DrawTimeLine( drawHelper, rcTimeLine, lefttime, righttime );
+
+ OffsetRect( &rcText, 0, 28 );
+
+ rcText.left = 5;
+
+ RECT timeRect = rcText;
+
+ timeRect.right = timeRect.left + 100;
+
+ char sz[ 32 ];
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", lefttime );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+
+ timeRect = rcText;
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", righttime );
+
+ int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ timeRect.right = w2() - 10;
+ timeRect.left = timeRect.right - textW;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+ }
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, m_flScrub, true );
+ DrawScrubHandle( drawHelper, rcHandle, m_flScrub, false );
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+ DrawSamples( drawHelper, rcSamples );
+
+ DrawSceneEnd( drawHelper );
+
+ RECT rcTags = rc;
+ rcTags.top = TAG_TOP + GetCaptionHeight();
+ rcTags.bottom = TAG_BOTTOM + GetCaptionHeight();
+
+ DrawTimingTags( drawHelper, rcTags );
+
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
+{
+ // Construct main menu
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ int current, total;
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ if ( current > 0 )
+ {
+ pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_SRT );
+ }
+
+ if ( current <= total - 1 )
+ {
+ pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_SRT );
+ }
+ pop->addSeparator();
+ }
+
+ CChoreoScene *scene = GetSafeScene();
+ if ( scene )
+ {
+ if ( CountSelected() > 0 )
+ {
+ pop->add( va( "Delete" ), IDC_SRT_DELETE );
+ pop->add( "Deselect all", IDC_SRT_DESELECT );
+ }
+ pop->add( "Select all", IDC_SRT_SELECTALL );
+ }
+
+ pop->add( va( "Change scale..." ), IDC_SRT_CHANGESCALE );
+ pop->addSeparator();
+ pop->add( "Edge Properties...", IDC_SRT_EDGEPROPERTIES );
+
+ pop->popup( this, (short)event->x, (short)event->y );
+}
+
+void SceneRampTool::GetWorkspaceLeftRight( int& left, int& right )
+{
+ left = 0;
+ right = w2();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::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 );
+}
+
+void SceneRampTool::SetClickedPos( int x, int y )
+{
+ m_nClickedX = x;
+ m_nClickedY = y;
+}
+
+float SceneRampTool::GetTimeForClickedPos( void )
+{
+ CChoreoScene *scene = GetSafeScene();
+ if ( !scene )
+ return 0.0f;
+
+ float t = GetTimeValueForMouse( m_nClickedX );
+ return t;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dragtype -
+// startx -
+// cursor -
+//-----------------------------------------------------------------------------
+void SceneRampTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
+{
+ m_nDragType = dragtype;
+ m_nStartX = startx;
+ m_nLastX = startx;
+ m_nStartY = starty;
+ m_nLastY = starty;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ m_hPrevCursor = SetCursor( cursor );
+
+ m_FocusRects.Purge();
+
+ RECT rcStart;
+ rcStart.left = startx;
+ rcStart.right = startx;
+
+ bool addrect = true;
+ switch ( dragtype )
+ {
+ case DRAGTYPE_SCRUBBER:
+ {
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, m_flScrub, true );
+
+ rcStart = rcScrub;
+ rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcStart.right = rcStart.left;
+ rcStart.top = rcScrub.bottom;
+
+ rcStart.bottom = h2();
+ }
+ break;
+ default:
+ {
+ rcStart.top = starty;
+ rcStart.bottom = starty;
+ }
+ break;
+ }
+
+
+ if ( addrect )
+ {
+ AddFocusRect( rcStart );
+ }
+
+ DrawFocusRect();
+}
+
+void SceneRampTool::OnMouseMove( mxEvent *event )
+{
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ event->x = (short)mx;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ {
+ OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), ( my - m_nStartY ) );
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ }
+
+ OffsetRect( &f->m_rcFocus, ( mx - m_nStartX ), 0 );
+ }
+ break;
+ case DRAGTYPE_MOVEPOINTS_TIME:
+ case DRAGTYPE_MOVEPOINTS_VALUE:
+ {
+ int dx = mx - m_nLastX;
+ int dy = my - m_nLastY;
+
+ if ( !( event->modifiers & mxEvent::KeyCtrl ) )
+ {
+ // Zero out motion on other axis
+ if ( m_nDragType == DRAGTYPE_MOVEPOINTS_VALUE )
+ {
+ dx = 0;
+ mx = m_nLastX;
+ }
+ else
+ {
+ dy = 0;
+ my = m_nLastY;
+ }
+ }
+ else
+ {
+ SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ int height = rcSamples.bottom - rcSamples.top;
+ Assert( height > 0 );
+
+ float dfdx = (float)dx / GetPixelsPerSecond();
+ float dfdy = (float)dy / (float)height;
+
+ MoveSelectedSamples( dfdx, dfdy );
+
+ // Update the scrubber
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ ForceScrubPosition( t );
+ g_pMatSysWindow->Frame();
+ }
+
+ OffsetRect( &f->m_rcFocus, dx, dy );
+ }
+ break;
+ case DRAGTYPE_SELECTION:
+ {
+ RECT rcFocus;
+
+ rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ f->m_rcFocus = rcFocus;
+ }
+ break;
+ }
+ }
+
+ DrawFocusRect();
+ }
+ else
+ {
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ /*
+ else if ( IsMouseOverTag( mx, my ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ */
+ // See if anything is selected
+ if ( CountSelected() <= 0 )
+ {
+ // Nothing selected
+ // Draw auto highlight
+ DrawAutoHighlight( event );
+ }
+ }
+
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+}
+
+int SceneRampTool::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ // Give helper a shot at the event
+ if ( m_pHelper->HelperHandleEvent( event ) )
+ {
+ return 1;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ int w, h;
+ w = event->width;
+ h = event->height;
+
+ m_nLastHPixelsNeeded = 0;
+ InvalidateLayout();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int tz = g_pChoreoView->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 );
+ }
+
+ g_pChoreoView->SetPreservedTimeZoom( this, tz );
+ }
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+ bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
+
+ iret = 1;
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetClickedPos( mx, my );
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ bool insamplearea = PtInRect( &rcSamples, pt ) ? true : false;
+
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+
+ CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_SRT_ADDSAMPLE_TOLERANCE : FP_SRT_SELECTION_TOLERANCE );
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ if ( w2() > 0 )
+ {
+ 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;
+ ForceScrubPosition( t );
+ }
+
+ StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( insamplearea )
+ {
+ if ( sample )
+ {
+ if ( shiftdown )
+ {
+ sample->selected = !sample->selected;
+ redraw();
+ }
+ else if ( sample->selected )
+ {
+ PreDataChanged( "move scene ramp points" );
+
+ StartDragging(
+ rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE,
+ m_nClickedX, m_nClickedY,
+ LoadCursor( NULL, rightbutton ? IDC_SIZEWE : IDC_SIZENS ) );
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ DeselectAll();
+ }
+
+ StartDragging( DRAGTYPE_SELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_ARROW ) );
+ }
+ }
+ else if ( ctrldown )
+ {
+ CChoreoScene *s = GetSafeScene();
+ if ( s )
+ {
+ // Add a sample point
+ float t = GetTimeValueForMouse( mx );
+
+ t = FacePoser_SnapTime( t );
+ float value = 1.0f - (float)( (short)event->y - rcSamples.top ) / (float)( rcSamples.bottom - rcSamples.top );
+ value = clamp( value, 0.0f, 1.0f );
+
+ PreDataChanged( "Add scene ramp point" );
+
+ s->AddSceneRamp( t, value, false );
+
+ s->ResortSceneRamp();
+
+ PostDataChanged( "Add scene ramp point" );
+ }
+ }
+ else
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ iret = 1;
+ return iret;
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ DeselectAll();
+ }
+
+ StartDragging( DRAGTYPE_SELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_ARROW ) );
+ }
+ }
+ }
+ else
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ iret = 1;
+ return iret;
+ }
+ else
+ {
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ SetScrubTargetTime( t );
+ }
+ }
+ }
+
+ CalcBounds( m_nDragType );
+ }
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ OnMouseMove( event );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ OnMouseMove( event );
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_NONE:
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ m_flScrubberTimeOffset = 0.0f;
+ }
+ }
+ break;
+ case DRAGTYPE_MOVEPOINTS_VALUE:
+ case DRAGTYPE_MOVEPOINTS_TIME:
+ {
+ PostDataChanged( "move ramp points" );
+ }
+ break;
+ case DRAGTYPE_SELECTION:
+ {
+ SelectPoints();
+ }
+ break;
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ redraw();
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_UNDO_SRT:
+ {
+ OnUndo();
+ }
+ break;
+ case IDC_REDO_SRT:
+ {
+ OnRedo();
+ }
+ break;
+ case IDC_SRT_DELETE:
+ {
+ Delete();
+ }
+ break;
+ case IDC_SRT_DESELECT:
+ {
+ DeselectAll();
+ }
+ break;
+ case IDC_SRT_SELECTALL:
+ {
+ SelectAll();
+ }
+ break;
+ case IDC_SRT_RAMPHSCROLL:
+ {
+ 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_SRT_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ case IDC_SRT_EDGEPROPERTIES:
+ {
+ OnEdgeProperties();
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ iret = 1;
+ switch ( event->key )
+ {
+ default:
+ iret = g_pChoreoView->HandleZoomKey( this, event->key );
+ break;
+ case VK_ESCAPE:
+ DeselectAll();
+ break;
+ case VK_DELETE:
+ Delete();
+ break;
+ }
+ }
+ }
+ return iret;
+}
+
+void SceneRampTool::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+void SceneRampTool::CalcBounds( int movetype )
+{
+ switch ( movetype )
+ {
+ default:
+ case DRAGTYPE_NONE:
+ {
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ m_bUseBounds = true;
+ m_nMinX = 0;
+ m_nMaxX = w2();
+ }
+ break;
+ }
+}
+
+bool SceneRampTool::PaintBackground()
+{
+ redraw();
+ return false;
+}
+
+void SceneRampTool::OnUndo( void )
+{
+ g_pChoreoView->Undo();
+}
+
+void SceneRampTool::OnRedo( void )
+{
+ g_pChoreoView->Redo();
+}
+
+void SceneRampTool::ForceScrubPositionFromSceneTime( float scenetime )
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s || !s->FindStopTime() )
+ return;
+
+ float t = scenetime;
+ m_flScrub = t;
+ m_flScrubTarget = t;
+ DrawScrubHandles();
+}
+
+void SceneRampTool::ForceScrubPosition( float t )
+{
+ m_flScrub = t;
+ m_flScrubTarget = t;
+
+ CChoreoScene *s = GetSafeScene();
+ if ( s && s->FindStopTime() )
+ {
+ float realtime = t;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->SetScrubTargetTime( realtime );
+
+ g_pChoreoView->DrawScrubHandle();
+ }
+
+ DrawScrubHandles();
+}
+
+void SceneRampTool::SetMouseOverPos( int x, int y )
+{
+ m_nMousePos[ 0 ] = x;
+ m_nMousePos[ 1 ] = y;
+}
+
+void SceneRampTool::GetMouseOverPos( int &x, int& y )
+{
+ x = m_nMousePos[ 0 ];
+ y = m_nMousePos[ 1 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcPos -
+//-----------------------------------------------------------------------------
+void SceneRampTool::GetMouseOverPosRect( RECT& rcPos )
+{
+ rcPos.top = GetCaptionHeight() + 12;
+ rcPos.left = w2() - 200;
+ rcPos.right = w2() - 5;
+ rcPos.bottom = rcPos.top + 13;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcPos -
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
+{
+ // Compute time for pixel x
+ float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ float snapped = FacePoser_SnapTime( t );
+
+ // Found it, write out description
+ //
+ char sz[ 128 ];
+ if ( t != snapped )
+ {
+ Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), "%.3f", t );
+ }
+
+ int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
+
+ RECT rcText = rcPos;
+ rcText.left = max( rcPos.left, rcPos.right - len );
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawMouseOverPos()
+{
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+void SceneRampTool::AddFocusRect( RECT& rc )
+{
+ RECT rcFocus = rc;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ // Convert to screen space?
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+// left -
+// right -
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right )
+{
+ RECT rcLabel;
+ float granularity = 0.5f;
+
+ drawHelper.DrawColoredLine( RGB( 150, 150, 200 ), PS_SOLID, 1, rc.left, rc.top + 2, rc.right, rc.top + 2 );
+
+ float f = SnapTime( left, granularity );
+ while ( f < right )
+ {
+ float frac = ( f - left ) / ( right - left );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ rcLabel.left = GetPixelForTimeValue( f );
+ rcLabel.top = rc.top + 5;
+ rcLabel.bottom = rcLabel.top + 10;
+
+ if ( f != left )
+ {
+ drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1,
+ rcLabel.left, rc.top, 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 );
+
+ RECT rcOut = rcLabel;
+ if ( rcOut.left <= 0 )
+ {
+ OffsetRect( &rcOut, -rcOut.left + 2, 0 );
+ }
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 50, 150 ), rcOut, sz );
+
+ }
+ f += granularity;
+ }
+}
+
+void SceneRampTool::DrawTimingTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ CChoreoScene *scene = GetSafeScene();
+ if ( !scene )
+ return;
+
+ float starttime = GetTimeValueForMouse( 0 );
+ float endtime = GetTimeValueForMouse( w2() );
+
+ if ( endtime - starttime <= 0.0f )
+ return;
+
+ RECT rcText = rc;
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, "Timing Tags:" );
+
+ // Loop through all events in scene
+
+ int c = scene->GetNumEvents();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e )
+ continue;
+
+ // See if time overlaps
+ if ( !e->HasEndTime() )
+ continue;
+
+ if ( e->GetEndTime() < starttime )
+ continue;
+
+ if ( e->GetStartTime() > endtime )
+ continue;
+
+ if ( e->GetNumRelativeTags() > 0 )
+ {
+ DrawRelativeTagsForEvent( drawHelper, rc, e, starttime, endtime );
+ }
+ if ( e->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ) > 0 )
+ {
+ DrawAbsoluteTagsForEvent( drawHelper, rc, e, starttime, endtime );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// &rc -
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawAbsoluteTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT &rc, CChoreoEvent *event, float starttime, float endtime )
+{
+ if ( !event )
+ return;
+
+ for ( int i = 0; i < event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+ if ( !tag )
+ continue;
+
+ float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() );
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ bool clipped = false;
+ int left = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ if ( event->GetType() == CChoreoEvent::GESTURE )
+ {
+ continue;
+ }
+
+ COLORREF clr = RGB( 0, 100, 250 );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, clr );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 12;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void SceneRampTool::DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *event, float starttime, float endtime )
+{
+ if ( !event )
+ return;
+
+ //drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" );
+
+ for ( int i = 0; i < event->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ float tagtime = ( event->GetStartTime() + tag->GetPercentage() * event->GetDuration() );
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ bool clipped = false;
+ int left = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ //float frac = ( tagtime - starttime ) / ( endtime - starttime );
+
+ //int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f );
+
+ COLORREF clr = RGB( 100, 100, 100 );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, clr );
+
+ RECT rcText;
+ rcText = rc;
+ rcText.bottom = rc.bottom - 10;
+ rcText.top = rcText.bottom - 10;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int SceneRampTool::ComputeHPixelsNeeded( void )
+{
+ CChoreoScene *scene = GetSafeScene();
+ if ( !scene )
+ return 0;
+
+ int pixels = 0;
+ float maxtime = scene->FindStopTime();
+ pixels = (int)( ( maxtime ) * GetPixelsPerSecond() + 10 );
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ if ( pixelsneeded <= w2() )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2() - 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( w2() );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float SceneRampTool::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() )/100.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void SceneRampTool::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = (float)x;
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ m_bLayoutIsValid = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : st -
+// ed -
+//-----------------------------------------------------------------------------
+void SceneRampTool::GetStartAndEndTime( float& st, float& ed )
+{
+ st = m_flLeftOffset / GetPixelsPerSecond();
+ ed = st + (float)w2() / GetPixelsPerSecond();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : float
+//-----------------------------------------------------------------------------
+float SceneRampTool::GetEventEndTime()
+{
+ CChoreoScene *scene = GetSafeScene();
+ if ( !scene )
+ return 1.0f;
+
+ return scene->FindStopTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int SceneRampTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ float frac = ( time - st ) / ( ed - st );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = ( int )( frac * w2() );
+ return pixel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float SceneRampTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ if ( clip )
+ {
+ if ( mx < 0 )
+ {
+ return st;
+ }
+ if ( mx > w2() )
+ {
+ return ed;
+ }
+ }
+
+ float frac = (float)( mx ) / (float)( w2() );
+ return st + frac * ( ed - st );
+}
+
+void SceneRampTool::OnChangeScale( void )
+{
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change Zoom" );
+ strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset= 0.0f;
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
+}
+
+void SceneRampTool::DrawSceneEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ float duration = s->FindStopTime();
+ if ( !duration )
+ return;
+
+ int leftx = GetPixelForTimeValue( duration );
+ if ( leftx >= w2() )
+ return;
+
+ RECT rcSample;
+ GetSampleTrayRect( rcSample );
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, rcSample.top, leftx, rcSample.bottom );
+}
+
+void SceneRampTool::GetSampleTrayRect( RECT& rc )
+{
+ rc.left = 0;
+ rc.right = w2();
+ rc.top = GetCaptionHeight() + 65;
+
+ rc.bottom = h2() - m_nScrollbarHeight-2;
+}
+
+void SceneRampTool::DrawSamplesSimple( CChoreoWidgetDrawHelper& drawHelper, CChoreoScene *scene, bool clearbackground, COLORREF sampleColor, RECT &rcSamples )
+{
+ if ( clearbackground )
+ {
+ drawHelper.DrawFilledRect( RGB( 230, 230, 215 ), rcSamples );
+ }
+
+ if ( !scene )
+ return;
+
+ float starttime = 0.0f;
+ float endtime = scene->FindStopTime();
+
+ COLORREF lineColor = sampleColor;
+
+ int width = rcSamples.right - rcSamples.left;
+ if ( width <= 0.0f )
+ return;
+
+ int height = rcSamples.bottom - rcSamples.top;
+ int bottom = rcSamples.bottom;
+
+ float timestepperpixel = endtime / (float)width;
+
+ float prev_value = scene->GetSceneRampIntensity( starttime );
+ int prev_x = rcSamples.left;
+ float prev_t = 0.0f;
+
+ for ( float x = rcSamples.left; x < rcSamples.right; x+=3 )
+ {
+ float t = (float)( x - rcSamples.left ) * timestepperpixel;
+
+ float value = scene->GetSceneRampIntensity( starttime + t );
+
+ // Draw segment
+ drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ prev_x, bottom - prev_value * height,
+ x, bottom - value * height );
+
+ prev_x = x;
+ prev_t = t;
+ prev_value = value;
+ }
+}
+
+void SceneRampTool::DrawSamples( CChoreoWidgetDrawHelper& drawHelper, RECT &rcSamples )
+{
+ drawHelper.DrawFilledRect( RGB( 230, 230, 215 ), rcSamples );
+
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ int rampCount = s->GetSceneRampCount();
+ if ( !rampCount )
+ return;
+
+ float starttime;
+ float endtime;
+
+ GetStartAndEndTime( starttime, endtime );
+
+ COLORREF lineColor = RGB( 0, 0, 255 );
+ COLORREF dotColor = RGB( 0, 0, 255 );
+ COLORREF dotColorSelected = RGB( 240, 80, 20 );
+ COLORREF shadowColor = RGB( 150, 150, 250 );
+
+ int height = rcSamples.bottom - rcSamples.top;
+ int bottom = rcSamples.bottom;
+ int top = rcSamples.top;
+
+ float timestepperpixel = 1.0f / GetPixelsPerSecond();
+
+ float stoptime = min( endtime, s->FindStopTime() );
+
+ float prev_t = starttime;
+ float prev_value = s->GetSceneRampIntensity( prev_t );
+
+ for ( float t = starttime-timestepperpixel; t <= stoptime; t += timestepperpixel )
+ {
+ float value = s->GetSceneRampIntensity( t );
+
+ int prevx, x;
+
+ bool clipped1, clipped2;
+ x = GetPixelForTimeValue( t, &clipped1 );
+ prevx = GetPixelForTimeValue( prev_t, &clipped2 );
+
+ if ( !clipped1 && !clipped2 )
+ {
+ // Draw segment
+ drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ prevx, clamp( bottom - prev_value * height, top, bottom ),
+ x, clamp( bottom - value * height, top, bottom ) );
+ }
+
+ prev_t = t;
+ prev_value = value;
+
+ }
+
+ for ( int sample = 0; sample < rampCount; sample++ )
+ {
+ CExpressionSample *start = s->GetSceneRamp( sample );
+
+ /*
+ int pixel = (int)( ( start->time / event_time ) * width + 0.5f);
+ int x = m_rcBounds.left + pixel;
+ float roundedfrac = (float)pixel / (float)width;
+ */
+ float value = start->value;
+ bool clipped = false;
+ int x = GetPixelForTimeValue( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+
+ if ( !start->selected )
+ continue;
+
+ if ( start->GetCurveType() == CURVE_DEFAULT )
+ continue;
+
+ // Draw curve type indicator...
+ char sz[ 128 ];
+ Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) );
+ RECT rc;
+ int fontSize = 9;
+ rc.top = clamp( y + 5, rcSamples.top + 2, rcSamples.bottom - 2 - fontSize );
+ rc.bottom = rc.top + fontSize + 1;
+ rc.left = x - 75;
+ rc.right = x + 175;
+ drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz );
+ }
+}
+
+void SceneRampTool::DrawAutoHighlight( mxEvent *event )
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f );
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcSamples, true );
+
+ RECT rcClient = rcSamples;
+
+ COLORREF dotColor = RGB( 0, 0, 255 );
+ COLORREF dotColorSelected = RGB( 240, 80, 20 );
+ COLORREF clrHighlighted = RGB( 0, 200, 0 );
+
+ int height = rcClient.bottom - rcClient.top;
+ int bottom = rcClient.bottom;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+ int dotSizeHighlighted = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+ COLORREF bgColor = RGB( 230, 230, 200 );
+
+ // Fixme, could look at 1st derivative and do more sampling at high rate of change?
+ // or near actual sample points!
+ int sampleCount = s->GetSceneRampCount();
+ for ( int sample = 0; sample < sampleCount; sample++ )
+ {
+ CExpressionSample *start = s->GetSceneRamp( sample );
+
+ float value = start->value;
+ bool clipped = false;
+ int x = GetPixelForTimeValue( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ if ( hover == start )
+ {
+ drawHelper.DrawCircle(
+ bgColor,
+ x, y,
+ dotSizeHighlighted,
+ true );
+
+ drawHelper.DrawCircle(
+ clrHighlighted,
+ x, y,
+ dotSizeHighlighted,
+ false );
+
+
+ }
+ else
+ {
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+ }
+ }
+}
+
+
+int SceneRampTool::NumSamples()
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return 0;
+
+ return s->GetSceneRampCount();
+}
+
+CExpressionSample *SceneRampTool::GetSample( int idx )
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return NULL;
+
+ return s->GetSceneRamp( idx );
+}
+
+CExpressionSample *SceneRampTool::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_SRT_SELECTION_TOLERANCE*/ )
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return NULL;
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ if ( !PtInRect( &rcSamples, pt ) )
+ return NULL;
+
+ pt.y -= rcSamples.top;
+
+ float closest_dist = 9999999.f;
+ CExpressionSample *bestsample = NULL;
+
+ int height = rcSamples.bottom - rcSamples.top;
+
+ for ( int i = 0; i < s->GetSceneRampCount(); i++ )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+ Assert( sample );
+
+ bool clipped = false;
+ int px = GetPixelForTimeValue( sample->time, &clipped );
+ int py = height * ( 1.0f - sample->value );
+
+ int dx = px - pt.x;
+ int dy = py - pt.y;
+
+ float dist = sqrt( (float)(dx * dx + dy * dy) );
+
+ if ( dist < closest_dist )
+ {
+ bestsample = sample;
+ closest_dist = dist;
+ }
+
+ }
+
+ // Not close to any of them!!!
+ if ( ( tolerance != 0.0f ) &&
+ ( closest_dist > tolerance ) )
+ return NULL;
+
+ return bestsample;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::SelectPoints( void )
+{
+ RECT rcSelection;
+
+ rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ int selW = rcSelection.right - rcSelection.left;
+ int selH = rcSelection.bottom - rcSelection.top;
+
+ float tolerance = FP_SRT_SELECTION_RECTANGLE_TOLERANCE;
+ // If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance
+ if ( selW <= 2 && selH <= 2 )
+ {
+ tolerance = FP_SRT_SELECTION_TOLERANCE;
+
+ CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f );
+ if ( sample )
+ {
+ sample->selected = true;
+ return;
+ }
+ }
+ else
+ {
+ InflateRect( &rcSelection, 3, 3 );
+ }
+
+ RECT rcSamples;
+ GetSampleTrayRect( rcSamples );
+
+ int height = rcSamples.bottom - rcSamples.top;
+
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ float duration = s->FindStopTime();
+ if ( !duration )
+ return;
+
+ float fleft = (float)GetTimeValueForMouse( rcSelection.left );
+ float fright = (float)GetTimeValueForMouse( rcSelection.right );
+
+ //fleft *= duration;
+ //fright *= duration;
+
+ float ftop = (float)( rcSelection.top - rcSamples.top ) / (float)height;
+ float fbottom = (float)( rcSelection.bottom - rcSamples.top ) / (float)height;
+
+ fleft = clamp( fleft, 0.0f, duration );
+ fright = clamp( fright, 0.0f, duration );
+ ftop = clamp( ftop, 0.0f, 1.0f );
+ fbottom = clamp( fbottom, 0.0f, 1.0f );
+
+ float timestepperpixel = 1.0f / GetPixelsPerSecond();
+ float yfracstepperpixel = 1.0f / (float)height;
+
+
+ float epsx = tolerance*timestepperpixel;
+ float epsy = tolerance*yfracstepperpixel;
+
+ for ( int i = 0; i < s->GetSceneRampCount(); i++ )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+
+ if ( sample->time + epsx < fleft )
+ continue;
+
+ if ( sample->time - epsx > fright )
+ continue;
+
+ if ( (1.0f - sample->value ) + epsy < ftop )
+ continue;
+
+ if ( (1.0f - sample->value ) - epsy > fbottom )
+ continue;
+
+ sample->selected = true;
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int SceneRampTool::CountSelected( void )
+{
+ return m_pHelper->CountSelected( false );
+}
+
+void SceneRampTool::MoveSelectedSamples( float dfdx, float dfdy )
+{
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ int c = s->GetSceneRampCount();
+
+ float duration = s->FindStopTime();
+ //dfdx *= duration;
+
+ for ( int i = 0; i < c; i++ )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+ if ( !sample || !sample->selected )
+ continue;
+
+ sample->time += dfdx;
+ sample->time = clamp( sample->time, 0.0f, duration );
+
+ sample->value -= dfdy;
+ sample->value = clamp( sample->value, 0.0f, 1.0f );
+ }
+
+ s->ResortSceneRamp();
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void SceneRampTool::DeselectAll( void )
+{
+ int i;
+
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ CChoreoScene *s = GetSafeScene();
+ Assert( s );
+ if ( !s )
+ return;
+
+ for ( i = s->GetSceneRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+ sample->selected = false;
+ }
+
+ redraw();
+}
+
+void SceneRampTool::SelectAll( void )
+{
+ int i;
+
+ CChoreoScene *s = GetSafeScene();
+ Assert( s );
+ if ( !s )
+ return;
+
+ for ( i = s->GetSceneRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+ sample->selected = true;
+ }
+
+ redraw();
+}
+
+void SceneRampTool::Delete( void )
+{
+ int i;
+
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ int selecteditems = CountSelected();
+ if ( !selecteditems )
+ return;
+
+ PreDataChanged( "Delete scene ramp points" );
+
+ for ( i = s->GetSceneRampCount() - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = s->GetSceneRamp( i );
+ if ( !sample->selected )
+ continue;
+
+ s->DeleteSceneRamp( i );
+ }
+
+ PostDataChanged( "Delete scene ramp points" );
+}
+
+void SceneRampTool::OnModelChanged()
+{
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *undodescription -
+//-----------------------------------------------------------------------------
+void SceneRampTool::PreDataChanged( char const *undodescription )
+{
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undodescription );
+ }
+ ++m_nUndoSetup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *redodescription -
+//-----------------------------------------------------------------------------
+void SceneRampTool::PostDataChanged( char const *redodescription )
+{
+ --m_nUndoSetup;
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->PushRedo( redodescription );
+ redraw();
+ }
+}
+
+void SceneRampTool::SetMousePositionForEvent( mxEvent *event )
+{
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ event->x = pt.x;
+ event->y = pt.y;
+}
+
+void SceneRampTool::OnEdgeProperties()
+{
+ CChoreoScene *s = GetSafeScene();
+ if ( !s )
+ return;
+
+ CEdgePropertiesParams params;
+ Q_memset( &params, 0, sizeof( params ) );
+ Q_strcpy( params.m_szDialogTitle, "Edge Properties" );
+
+ params.SetFromCurve( s->GetSceneRamp() );
+
+ if ( !EdgeProperties( &params ) )
+ {
+ return;
+ }
+
+ char *undotext = "Change Scene Ramp Edge Properties";
+
+ PreDataChanged( undotext );
+
+ // Apply changes.
+ params.ApplyToCurve( s->GetSceneRamp() );
+
+ PostDataChanged( undotext );
+}
+
+void SceneRampTool::GetWorkList( bool reflect, CUtlVector< SceneRampTool * >& list )
+{
+ NOTE_UNUSED( reflect );
+ list.AddToTail( this );
+}
diff --git a/utils/hlfaceposer/SceneRampTool.h b/utils/hlfaceposer/SceneRampTool.h
new file mode 100644
index 0000000..6e89c04
--- /dev/null
+++ b/utils/hlfaceposer/SceneRampTool.h
@@ -0,0 +1,201 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef SCENERAMPTOOL_H
+#define SCENERAMPTOOL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "studio.h"
+#include "utlvector.h"
+#include "faceposertoolwindow.h"
+
+class CChoreoEvent;
+class CChoreoScene;
+class CChoreoWidgetDrawHelper;
+class CChoreoView;
+struct CExpressionSample;
+
+#define IDC_REDO_SRT 1000
+#define IDC_UNDO_SRT 1001
+
+#define IDC_SRT_DELETE 1002
+#define IDC_SRT_DESELECT 1003
+#define IDC_SRT_SELECTALL 1004
+
+#define IDC_SRT_CHANGESCALE 1005
+#define IDC_SRT_RAMPHSCROLL 1006
+
+#define IDC_SRT_EDGEPROPERTIES 1007
+
+#define FP_SRT_SELECTION_TOLERANCE 30.0f
+#define FP_SRT_SELECTION_RECTANGLE_TOLERANCE 5.0f
+#define FP_SRT_ADDSAMPLE_TOLERANCE 5.0f
+
+template< class T > class CCurveEditorHelper;
+
+class SceneRampTool : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ // Construction
+ SceneRampTool( mxWindow *parent );
+ ~SceneRampTool( void );
+
+ virtual void Think( float dt );
+ void ScrubThink( float dt, bool scrubbing );
+ virtual bool IsScrubbing( void ) const;
+ virtual bool IsProcessing( void );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground();
+
+ void GetScrubHandleRect( RECT& rcHandle, float scrub, bool clipped = false );
+
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle, float scrub, bool reference );
+ void DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right );
+ void DrawSceneEnd( CChoreoWidgetDrawHelper& drawHelper );
+
+ void SetMouseOverPos( int x, int y );
+ void GetMouseOverPos( int &x, int& y );
+ void GetMouseOverPosRect( RECT& rcPos );
+ void DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos );
+ void DrawMouseOverPos();
+
+ void DrawScrubHandles();
+
+ CChoreoScene *GetSafeScene( void );
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ void ForceScrubPosition( float newtime );
+ void ForceScrubPositionFromSceneTime( float scenetime );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+
+ void DrawSamplesSimple( CChoreoWidgetDrawHelper& drawHelper, CChoreoScene *scene, bool clearbackground, COLORREF sampleColor, RECT &rcSamples );
+
+ virtual void OnModelChanged();
+
+ void SetMousePositionForEvent( mxEvent *event );
+
+ int NumSamples();
+ CExpressionSample *GetSample( int idx );
+ void PreDataChanged( char const *undodescription );
+ void PostDataChanged( char const *redodescription );
+ CExpressionSample *GetSampleUnderMouse( int mx, int my, float tolerance = FP_SRT_SELECTION_TOLERANCE );
+ void GetWorkList( bool reflect, CUtlVector< SceneRampTool * >& list );
+
+private:
+
+ void GetSampleTrayRect( RECT& rc );
+ void DrawSamples( CChoreoWidgetDrawHelper& drawHelper, RECT &rcSamples );
+
+ void SelectPoints( void );
+ void DeselectAll();
+ void SelectAll();
+ void Delete( void );
+
+ int CountSelected( void );
+ void MoveSelectedSamples( float dfdx, float dfdy );
+
+ void StartDragging( int dragtype, int startx, int starty, HCURSOR cursor );
+ void AddFocusRect( RECT& rc );
+ void OnMouseMove( mxEvent *event );
+ void DrawFocusRect( void );
+ void ShowContextMenu( mxEvent *event, bool include_track_menus );
+ void GetWorkspaceLeftRight( int& left, int& right );
+ void SetClickedPos( int x, int y );
+ float GetTimeForClickedPos( void );
+
+ void DrawAutoHighlight( mxEvent *event );
+
+ void ApplyBounds( int& mx, int& my );
+ void CalcBounds( int movetype );
+ void OnUndo( void );
+ void OnRedo( void );
+
+ void OnRevert( void );
+
+ void OnEdgeProperties();
+
+ void DrawTimingTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+ void DrawRelativeTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, CChoreoEvent *event, float starttime, float endtime );
+ void DrawAbsoluteTagsForEvent( CChoreoWidgetDrawHelper& drawHelper, RECT &rc, CChoreoEvent *event, float starttime, float endtime );
+
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+ void OnChangeScale();
+ int ComputeHPixelsNeeded( void );
+ float GetPixelsPerSecond( void );
+ void InvalidateLayout( void );
+ void RepositionHSlider( void );
+ void GetStartAndEndTime( float& st, float& ed );
+ float GetEventEndTime();
+ float GetTimeValueForMouse( int mx, bool clip = false );
+ int GetPixelForTimeValue( float time, bool *clipped = NULL );
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_SCRUBBER,
+ DRAGTYPE_MOVEPOINTS_VALUE,
+ DRAGTYPE_MOVEPOINTS_TIME,
+ DRAGTYPE_SELECTION,
+ };
+
+ int m_nMousePos[ 2 ];
+
+ bool m_bUseBounds;
+ int m_nMinX;
+ int m_nMaxX;
+
+ HCURSOR m_hPrevCursor;
+ int m_nDragType;
+
+ int m_nStartX;
+ int m_nStartY;
+ int m_nLastX;
+ int m_nLastY;
+
+ int m_nClickedX;
+ int m_nClickedY;
+
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+
+ bool m_bSuppressLayout;
+ // Height/width of scroll bars
+ int m_nScrollbarHeight;
+ float m_flLeftOffset;
+ mxScrollbar *m_pHorzScrollBar;
+ int m_nLastHPixelsNeeded;
+ // How many pixels per second we are showing in the UI
+ float m_flPixelsPerSecond;
+ // Do we need to move controls?
+ bool m_bLayoutIsValid;
+ float m_flLastDuration;
+ float m_flScrubberTimeOffset;
+ int m_nUndoSetup;
+
+ CCurveEditorHelper< SceneRampTool > *m_pHelper;
+
+ friend class CChoreoView;
+};
+
+extern SceneRampTool *g_pSceneRampTool;
+
+#endif // SCENERAMPTOOL_H
diff --git a/utils/hlfaceposer/VGuiWnd.cpp b/utils/hlfaceposer/VGuiWnd.cpp
new file mode 100644
index 0000000..3e49c43
--- /dev/null
+++ b/utils/hlfaceposer/VGuiWnd.cpp
@@ -0,0 +1,324 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cbase.h"
+#include "vguiwnd.h"
+#include <vgui_controls/EditablePanel.h>
+#include "vgui/ISurface.h"
+#include "vgui/IVGui.h"
+#include "VGuiMatSurface/IMatSystemSurface.h"
+#include "FacePoser_VGui.h"
+// #include "material.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/imaterial.h"
+
+#define REPAINT_TIMER_ID 1042 //random value, hopfully no collisions
+
+inline MaterialSystem_Config_t& MaterialSystemConfig()
+{
+ extern MaterialSystem_Config_t g_materialSystemConfig;
+ return g_materialSystemConfig;
+}
+
+inline IMaterialSystemHardwareConfig* MaterialSystemHardwareConfig()
+{
+ extern IMaterialSystemHardwareConfig* g_pMaterialSystemHardwareConfig;
+ return g_pMaterialSystemHardwareConfig;
+}
+
+class CBaseMainPanel : public vgui::EditablePanel
+{
+public:
+
+ CBaseMainPanel(Panel *parent, const char *panelName) : vgui::EditablePanel( parent, panelName ) {};
+
+ virtual void OnSizeChanged(int newWide, int newTall)
+ {
+ // call Panel and not EditablePanel OnSizeChanged.
+ Panel::OnSizeChanged(newWide, newTall);
+ }
+};
+
+int CVGuiPanelWnd::handleEvent( mxEvent *event )
+{
+ if ( !HandeEventVGui( event ) )
+ {
+ return BaseClass::handleEvent( event );
+ }
+
+ return 1;
+}
+
+CVGuiWnd::CVGuiWnd(void)
+{
+ m_pMainPanel = NULL;
+ m_pParentWnd = NULL;
+ m_hVGuiContext = vgui::DEFAULT_VGUI_CONTEXT;
+ m_bIsDrawing = false;
+ m_ClearColor.SetColor( 0,0,0,255 );
+ m_bClearZBuffer = true;
+}
+
+CVGuiWnd::~CVGuiWnd(void)
+{
+ if ( FaceposerVGui()->HasFocus( this ) )
+ {
+ FaceposerVGui()->SetFocus( NULL );
+ }
+
+ if ( m_hVGuiContext != vgui::DEFAULT_VGUI_CONTEXT )
+ {
+ vgui::ivgui()->DestroyContext( m_hVGuiContext );
+ m_hVGuiContext = vgui::DEFAULT_VGUI_CONTEXT;
+ }
+
+ // kill the timer if any
+ ::KillTimer( (HWND)m_pParentWnd->getHandle(), REPAINT_TIMER_ID );
+
+
+ if ( m_pMainPanel )
+ m_pMainPanel->MarkForDeletion();
+}
+
+void CVGuiWnd::SetParentWindow(mxWindow *pParent)
+{
+ m_pParentWnd = pParent;
+
+ /*
+ m_pParentWnd->EnableWindow( true );
+ m_pParentWnd->SetFocus();
+ */
+
+ HWND h = (HWND)m_pParentWnd->getHandle();
+ EnableWindow( h, TRUE );
+ SetFocus( h );
+}
+
+int CVGuiWnd::GetVGuiContext()
+{
+ return m_hVGuiContext;
+}
+
+void CVGuiWnd::SetCursor(vgui::HCursor cursor)
+{
+ if ( m_pMainPanel )
+ {
+ m_pMainPanel->SetCursor( cursor );
+ }
+}
+
+void CVGuiWnd::SetCursor(const char *filename)
+{
+ vgui::HCursor hCursor = vgui::surface()->CreateCursorFromFile( filename );
+ m_pMainPanel->SetCursor( hCursor );
+}
+
+void CVGuiWnd::SetMainPanel( vgui::EditablePanel * pPanel )
+{
+ SetRepaintInterval( 75 );
+
+ Assert( m_pMainPanel == NULL );
+ Assert( m_hVGuiContext == vgui::DEFAULT_VGUI_CONTEXT );
+
+ m_pMainPanel = pPanel;
+
+ m_pMainPanel->SetParent( vgui::surface()->GetEmbeddedPanel() );
+ m_pMainPanel->SetVisible( true );
+ m_pMainPanel->SetPaintBackgroundEnabled( false );
+ m_pMainPanel->SetCursor( vgui::dc_arrow );
+
+ m_hVGuiContext = vgui::ivgui()->CreateContext();
+ vgui::ivgui()->AssociatePanelWithContext( m_hVGuiContext, m_pMainPanel->GetVPanel() );
+}
+
+vgui::EditablePanel *CVGuiWnd::CreateDefaultPanel()
+{
+ return new CBaseMainPanel( NULL, "mainpanel" );
+}
+
+vgui::EditablePanel *CVGuiWnd::GetMainPanel()
+{
+ return m_pMainPanel;
+}
+
+mxWindow *CVGuiWnd::GetParentWnd()
+{
+ return m_pParentWnd;
+}
+
+void CVGuiWnd::SetRepaintInterval( int msecs )
+{
+ m_pParentWnd->setTimer( msecs );
+}
+
+void CVGuiWnd::DrawVGuiPanel()
+{
+ if ( !m_pMainPanel || !m_pParentWnd || m_bIsDrawing )
+ return;
+
+
+ m_bIsDrawing = true; // avoid recursion
+
+ HWND hWnd = (HWND)m_pParentWnd->getHandle();
+
+ int w,h;
+ RECT rect;
+ ::GetClientRect(hWnd, &rect);
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ g_pMaterialSystem->SetView( hWnd );
+
+ pRenderContext->Viewport( rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top );
+
+ pRenderContext->ClearColor4ub( m_ClearColor.r(), m_ClearColor.g(), m_ClearColor.b(), m_ClearColor.a() );
+
+ pRenderContext->ClearBuffers( true, m_bClearZBuffer );
+
+ g_pMaterialSystem->BeginFrame( 0 );
+
+ // draw from the main panel down
+
+ m_pMainPanel->GetSize( w , h );
+
+ if ( w != rect.right || h != rect.bottom )
+ {
+ m_pMainPanel->SetBounds( 2 + rect.left, 2 + rect.top, rect.right - rect.left - 4, rect.bottom - rect.top - 4 );
+ m_pMainPanel->Repaint();
+ }
+
+ FaceposerVGui()->Simulate();
+
+ vgui::surface()->PaintTraverseEx( m_pMainPanel->GetVPanel(), true );
+
+ g_pMaterialSystem->EndFrame();
+
+ g_pMaterialSystem->SwapBuffers();
+
+ m_bIsDrawing = false;
+}
+
+CVGuiPanelWnd::CVGuiPanelWnd( mxWindow *parent, int x, int y, int w, int h )
+: BaseClass( parent, x, y, w, h )
+{
+}
+
+void CVGuiPanelWnd::redraw()
+{
+ DrawVGuiPanel();
+}
+
+int CVGuiWnd::HandeEventVGui( mxEvent *event )
+{
+ if ( !m_pParentWnd )
+ return 0;
+
+ HWND hWnd = (HWND)m_pParentWnd->getHandle();
+
+// switch( uMsg )
+// {
+
+// case WM_GETDLGCODE :
+// {
+// // forward all keyboard into to vgui panel
+// return DLGC_WANTALLKEYS|DLGC_WANTCHARS;
+// }
+
+// case WM_PAINT :
+// {
+// // draw the VGUI panel now
+// DrawVGuiPanel();
+// break;
+// }
+
+// case WM_TIMER :
+// {
+// if ( wParam == REPAINT_TIMER_ID )
+// {
+// m_pParentWnd->Invalidate();
+// }
+// break;
+// }
+
+// case WM_SETCURSOR:
+// return 1; // don't pass WM_SETCURSOR
+
+/*
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_MOUSEMOVE:
+ {
+ // switch vgui focus to this panel
+ FaceposerVGui()->SetFocus( this );
+
+ // request keyboard focus too on mouse down
+ if ( uMsg != WM_MOUSEMOVE)
+ {
+ m_pParentWnd->Invalidate();
+ m_pParentWnd->SetFocus();
+ }
+ break;
+ }
+ case WM_KILLFOCUS:
+ {
+ // restore normal arrow cursor when mouse leaves VGUI panel
+ SetCursor( vgui::dc_arrow );
+ break;
+ }
+
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_MOUSEWHEEL:
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_SYSCHAR:
+ case WM_CHAR:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ // redraw window
+ m_pParentWnd->Invalidate();
+ break;
+ }
+ }
+*/
+
+ switch ( event->event )
+ {
+ case mxEvent::KeyUp:
+ case mxEvent::KeyDown:
+ case mxEvent::MouseUp:
+ case mxEvent::MouseWheeled:
+ case mxEvent::Char:
+ case mxEvent::MouseMove:
+ {
+ InvalidateRect( hWnd, NULL, FALSE );
+ }
+ break;
+
+ case mxEvent::Timer:
+ {
+ InvalidateRect( hWnd, NULL, FALSE );
+ }
+ break;
+ case mxEvent::Focus:
+ break;
+ case mxEvent::MouseDown:
+ {
+ // switch vgui focus to this panel
+ FaceposerVGui()->SetFocus( this );
+
+ // request keyboard focus too on mouse down
+ if ( event->event != mxEvent::MouseMove )
+ {
+ InvalidateRect( hWnd, NULL, FALSE );
+ SetFocus( hWnd );
+ }
+ }
+ break;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/VGuiWnd.h b/utils/hlfaceposer/VGuiWnd.h
new file mode 100644
index 0000000..0c8964a
--- /dev/null
+++ b/utils/hlfaceposer/VGuiWnd.h
@@ -0,0 +1,69 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef VGUIWND_H
+#define VGUIWND_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxtk/mx.h"
+#include "color.h"
+
+namespace vgui
+{
+ class EditablePanel;
+ typedef unsigned long HCursor;
+}
+
+class CVGuiWnd
+{
+
+public:
+ CVGuiWnd(void);
+ ~CVGuiWnd(void);
+
+public:
+
+ void SetMainPanel( vgui::EditablePanel * pPanel );
+ vgui::EditablePanel *GetMainPanel(); // returns VGUI main panel
+ vgui::EditablePanel *CreateDefaultPanel();
+
+ void SetParentWindow(mxWindow *pParent);
+ mxWindow *GetParentWnd(); // return mxWindow handle
+
+ void SetCursor(vgui::HCursor cursor);
+ void SetCursor(const char *filename);
+
+ void SetRepaintInterval( int msecs );
+ int GetVGuiContext();
+
+protected:
+ void DrawVGuiPanel(); // overridden to draw this view
+ int HandeEventVGui( mxEvent *event );
+
+ vgui::EditablePanel *m_pMainPanel;
+ mxWindow *m_pParentWnd;
+ int m_hVGuiContext;
+ bool m_bIsDrawing;
+ Color m_ClearColor;
+ bool m_bClearZBuffer;
+};
+
+class CVGuiPanelWnd: public mxWindow, public CVGuiWnd
+{
+ typedef mxWindow BaseClass;
+
+public:
+
+ CVGuiPanelWnd( mxWindow *parent, int x, int y, int w, int h );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw();
+};
+
+
+#endif // VGUIWND_H
diff --git a/utils/hlfaceposer/actorproperties.cpp b/utils/hlfaceposer/actorproperties.cpp
new file mode 100644
index 0000000..dfd6248
--- /dev/null
+++ b/utils/hlfaceposer/actorproperties.cpp
@@ -0,0 +1,77 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "resource.h"
+#include "ActorProperties.h"
+#include "ChoreoView.h"
+#include "choreoactor.h"
+#include "mdlviewer.h"
+
+static CActorParams g_Params;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK ActorPropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_ACTORNAME, g_Params.m_szName );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_ACTORNAME ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ g_Params.m_szName[ 0 ] = 0;
+ GetDlgItemText( hwndDlg, IDC_ACTORNAME, g_Params.m_szName, 256 );
+ EndDialog( hwndDlg, 1 );
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int ActorProperties( CActorParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_ACTORPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)ActorPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/actorproperties.h b/utils/hlfaceposer/actorproperties.h
new file mode 100644
index 0000000..83ff364
--- /dev/null
+++ b/utils/hlfaceposer/actorproperties.h
@@ -0,0 +1,28 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ACTORPROPERTIES_H
+#define ACTORPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CActorParams : public CBaseDialogParams
+{
+ // i/o actor name
+ char m_szName[ 256 ];
+};
+
+// Display/create actor info
+int ActorProperties( CActorParams *params );
+
+#endif // ACTORPROPERTIES_H
diff --git a/utils/hlfaceposer/addsoundentry.cpp b/utils/hlfaceposer/addsoundentry.cpp
new file mode 100644
index 0000000..979af56
--- /dev/null
+++ b/utils/hlfaceposer/addsoundentry.cpp
@@ -0,0 +1,164 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#include "cbase.h"
+#include "mxtk/mx.h"
+#include "resource.h"
+#include "AddSoundEntry.h"
+#include "mdlviewer.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "filesystem.h"
+
+static CAddSoundParams g_Params;
+
+static void PopulateScriptList( HWND wnd )
+{
+ HWND control = GetDlgItem( wnd, IDC_SOUNDSCRIPT );
+ if ( !control )
+ {
+ return;
+ }
+
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ int c = soundemitter->GetNumSoundScripts();
+ for ( int i = 0; i < c; i++ )
+ {
+ // add text to combo box
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)soundemitter->GetSoundScriptName( i ) );
+
+ if ( i == 0 && !g_Params.m_szScriptName[ 0 ] )
+ {
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)soundemitter->GetSoundScriptName( i ) );
+ }
+ }
+
+ if ( g_Params.m_szScriptName[ 0 ] )
+ {
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)g_Params.m_szScriptName );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK AddSoundPropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ PopulateScriptList( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_SOUNDNAME, g_Params.m_szSoundName );
+ if ( g_Params.m_bReadOnlySoundName )
+ {
+ HWND ctrl = GetDlgItem( hwndDlg, IDC_SOUNDNAME );
+ if ( ctrl )
+ {
+ SendMessage( ctrl, EM_SETREADONLY, (WPARAM)TRUE, 0 );
+ }
+ }
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_SOUNDNAME ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ g_Params.m_szSoundName[ 0 ] = 0;
+ g_Params.m_szScriptName[ 0 ] = 0;
+ GetDlgItemText( hwndDlg, IDC_SOUNDNAME, g_Params.m_szSoundName, sizeof( g_Params.m_szSoundName ) );
+ GetDlgItemText( hwndDlg, IDC_SOUNDSCRIPT, g_Params.m_szScriptName, sizeof( g_Params.m_szScriptName ) );
+
+ // Don't exit...
+ if ( !g_Params.m_szSoundName[ 0 ] || !g_Params.m_szScriptName[ 0 ] )
+ return TRUE;
+
+ // Don't stompt existing sounds
+ int idx = soundemitter->GetSoundIndex( g_Params.m_szSoundName );
+ if ( soundemitter->IsValidIndex( idx ) )
+ {
+ if ( !g_Params.m_bAllowExistingSound )
+ {
+ mxMessageBox( NULL, va( "Sound '%s' already exists",
+ g_Params.m_szSoundName ), g_appTitle, MX_MB_OK );
+ }
+ else
+ {
+ EndDialog( hwndDlg, 1 );
+ }
+ return TRUE;
+ }
+
+ // Check out script
+ if ( !filesystem->FileExists( g_Params.m_szScriptName ) )
+ {
+ mxMessageBox( NULL, va( "Script '%s' does not exist",
+ g_Params.m_szScriptName ), g_appTitle, MX_MB_OK );
+ return TRUE;
+ }
+
+ if ( !filesystem->IsFileWritable( g_Params.m_szScriptName ) )
+ {
+ mxMessageBox( NULL, va( "Script '%s' is read-only, you need to check it out of VSS",
+ g_Params.m_szScriptName ), g_appTitle, MX_MB_OK );
+ return TRUE;
+ }
+
+ // Add the entry
+ CSoundParametersInternal params;
+ params.SetChannel( CHAN_VOICE );
+ params.SetSoundLevel( SNDLVL_TALKING );
+
+ soundemitter->ExpandSoundNameMacros( params, g_Params.m_szWaveFile );
+ soundemitter->AddSound( g_Params.m_szSoundName, g_Params.m_szScriptName, params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int AddSound( CAddSoundParams *params, HWND parent )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_ADDSOUNDENTRY ),
+ parent,
+ (DLGPROC)AddSoundPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/addsoundentry.h b/utils/hlfaceposer/addsoundentry.h
new file mode 100644
index 0000000..b5945bd
--- /dev/null
+++ b/utils/hlfaceposer/addsoundentry.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef ADDSOUNDENTRY_H
+#define ADDSOUNDENTRY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CAddSoundParams : public CBaseDialogParams
+{
+ CAddSoundParams()
+ {
+ m_szWaveFile[ 0 ] = 0;
+ m_szSoundName[ 0 ] = 0;
+ m_szScriptName[ 0 ] = 0;
+ m_bAllowExistingSound = false;
+ m_bReadOnlySoundName = false;
+ }
+
+ char m_szWaveFile[ 256 ];
+
+ // i/o input text
+ char m_szSoundName[ 256 ];
+ char m_szScriptName[ 256 ];
+ bool m_bAllowExistingSound;
+ bool m_bReadOnlySoundName;
+};
+
+// Display/create dialog
+int AddSound( CAddSoundParams *params, HWND parent );
+
+#endif // ADDSOUNDENTRY_H
diff --git a/utils/hlfaceposer/audiowaveoutput.h b/utils/hlfaceposer/audiowaveoutput.h
new file mode 100644
index 0000000..c169c16
--- /dev/null
+++ b/utils/hlfaceposer/audiowaveoutput.h
@@ -0,0 +1,127 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef AUDIOWAVEOUTPUT_H
+#define AUDIOWAVEOUTPUT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "sound.h"
+#include "utlvector.h"
+
+#define OUTPUT_BUFFER_COUNT 32
+#define MAX_CHANNELS 16
+
+class CAudioMixer;
+
+class CAudioMixerState
+{
+public:
+ CAudioMixer *mixer;
+ int submit_mixer_sample;
+};
+
+class CAudioBuffer
+{
+public:
+ WAVEHDR *hdr;
+ bool submitted;
+ int submit_sample_count;
+
+ CUtlVector< CAudioMixerState > m_Referenced;
+};
+
+#define OUTPUT_SAMPLE_RATE 44100
+#define PAINTBUFFER_SIZE 1024
+
+typedef struct
+{
+ int left;
+ int right;
+} portable_samplepair_t;
+
+class CAudioDeviceSWMix : public IAudioDevice
+{
+public:
+ virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true );
+ virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true );
+ virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true );
+ virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true );
+ virtual int MaxSampleCount( void );
+ virtual void MixBegin( void );
+
+ void TransferBufferStereo16( short *pOutput, int sampleCount );
+
+private:
+ portable_samplepair_t m_paintbuffer[ PAINTBUFFER_SIZE ];
+};
+
+class CAudioWaveOutput : public CAudioOutput
+{
+public:
+ CAudioWaveOutput( void );
+ ~CAudioWaveOutput( void );
+
+ // returns the size of each sample in bytes
+ virtual int SampleSize( void ) { return 2; }
+
+ // returns the sampling rate of the data
+ virtual int SampleRate( void ) { return OUTPUT_SAMPLE_RATE; }
+
+ // returns the mono/stereo status of this device (true if stereo)
+ virtual bool IsStereo( void ) { return true; }
+
+ // mix a buffer up to time (time is absolute)
+ virtual void Update( float time );
+
+ virtual void Flush( void );
+
+ virtual void AddSource( CAudioMixer *pSource );
+ virtual void StopSounds( void );
+ virtual int FindSourceIndex( CAudioMixer *pSource );
+
+ virtual int GetOutputPosition( void );
+ virtual float GetAmountofTimeAhead( void );
+ virtual int GetNumberofSamplesAhead( void );
+
+ virtual CAudioMixer *GetMixerForSource( CAudioSource *pSource );
+
+
+private:
+ void OpenDevice( void );
+ bool ValidDevice( void ) { return m_deviceHandle != 0; }
+ void ClearDevice( void ) { m_deviceHandle = NULL; }
+ CAudioBuffer *GetEmptyBuffer( void );
+ void SilenceBuffer( short *pSamples, int sampleCount );
+
+ void SetChannel( int channelIndex, CAudioMixer *pSource );
+ void FreeChannel( int channelIndex );
+
+ void RemoveMixerChannelReferences( CAudioMixer *mixer );
+ void AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
+ void RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
+ bool IsSourceReferencedByActiveBuffer( CAudioMixer *mixer );
+ bool IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer );
+
+ // Compute how many samples we've mixed since most recent buffer submission
+ void ComputeSampleAheadAmount( void );
+
+ HWAVEOUT m_deviceHandle;
+
+ float m_mixTime;
+ float m_baseTime;
+ int m_sampleIndex;
+ CAudioBuffer m_buffers[ OUTPUT_BUFFER_COUNT ];
+
+ CAudioMixer *m_sourceList[MAX_CHANNELS];
+ int m_nEstimatedSamplesAhead;
+public:
+ CAudioDeviceSWMix m_audioDevice;
+
+};
+#endif // AUDIOWAVEOUTPUT_H
diff --git a/utils/hlfaceposer/basedialogparams.cpp b/utils/hlfaceposer/basedialogparams.cpp
new file mode 100644
index 0000000..dba7bd9
--- /dev/null
+++ b/utils/hlfaceposer/basedialogparams.cpp
@@ -0,0 +1,65 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <mxtk/mx.h>
+#include "tier0/dbg.h"
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : self -
+//-----------------------------------------------------------------------------
+void CBaseDialogParams::PositionSelf( void *self )
+{
+ RECT rcDlg;
+ HWND dlgWindow = (HWND)self;
+ GetWindowRect( dlgWindow, &rcDlg );
+
+ // Get relative to primary monitor instead of actual window parent
+ RECT rcParent;
+ rcParent.left = 0;
+ rcParent.right = rcParent.left + GetSystemMetrics( SM_CXFULLSCREEN );
+ rcParent.top = 0;
+ rcParent.bottom = rcParent.top + GetSystemMetrics( SM_CYFULLSCREEN );
+
+ int dialogw, dialogh;
+ int parentw, parenth;
+
+ parentw = rcParent.right - rcParent.left;
+ parenth = rcParent.bottom - rcParent.top;
+ dialogw = rcDlg.right - rcDlg.left;
+ dialogh = rcDlg.bottom - rcDlg.top;
+
+ int dlgleft, dlgtop;
+ dlgleft = ( parentw - dialogw ) / 2;
+ dlgtop = ( parenth - dialogh ) / 2;
+
+ if ( m_bPositionDialog )
+ {
+ int top = m_nTop - dialogh - 5;
+ int left = m_nLeft;
+
+ MoveWindow( dlgWindow,
+ left,
+ top,
+ dialogw,
+ dialogh,
+ TRUE );
+ }
+ else
+ {
+
+ MoveWindow( dlgWindow,
+ dlgleft,
+ dlgtop,
+ dialogw,
+ dialogh,
+ TRUE
+ );
+ }
+
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/basedialogparams.h b/utils/hlfaceposer/basedialogparams.h
new file mode 100644
index 0000000..9fb3463
--- /dev/null
+++ b/utils/hlfaceposer/basedialogparams.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BASEDIALOGPARAMS_H
+#define BASEDIALOGPARAMS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier0/dbg.h"
+#include "tier1/strtools.h"
+
+struct CBaseDialogParams
+{
+ // i dialog title
+ char m_szDialogTitle[ 128 ];
+
+ bool m_bPositionDialog;
+ int m_nLeft;
+ int m_nTop;
+
+ void PositionSelf( void * self );
+};
+
+#endif // BASEDIALOGPARAMS_H
diff --git a/utils/hlfaceposer/cbase.h b/utils/hlfaceposer/cbase.h
new file mode 100644
index 0000000..7657527
--- /dev/null
+++ b/utils/hlfaceposer/cbase.h
@@ -0,0 +1,21 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CBASE_H
+#define CBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "hlfaceposer.h"
+#include "tier1/strtools.h"
+#include "vstdlib/random.h"
+#include "sharedInterface.h"
+
+extern class ISoundEmitterSystemBase *soundemitter;
+
+#endif // CBASE_H
diff --git a/utils/hlfaceposer/cclookup.cpp b/utils/hlfaceposer/cclookup.cpp
new file mode 100644
index 0000000..37f3fc3
--- /dev/null
+++ b/utils/hlfaceposer/cclookup.cpp
@@ -0,0 +1,240 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <commctrl.h>
+#include "mxtk/mx.h"
+#include "resource.h"
+#include "CCLookup.h"
+#include "mdlviewer.h"
+#include "addsoundentry.h"
+#include <vgui/ILocalize.h>
+
+using namespace vgui;
+
+extern vgui::ILocalize *g_pLocalize;
+
+static CCloseCaptionLookupParams g_Params;
+
+static HFONT g_UnicodeFont = NULL;
+
+static void InsertTextColumn( HWND listcontrol, int column, int width, char const *label )
+{
+ LVCOLUMN col;
+ memset( &col, 0, sizeof( col ) );
+
+ col.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_ORDER;
+ col.iOrder = column;
+ col.pszText = (char *)label;
+ col.cchTextMax = 256;
+ col.iSubItem = column;
+ col.cx = width;
+
+ ListView_InsertColumn( listcontrol, column, &col );
+}
+
+static void PopulateCloseCaptionTokenList( HWND wnd, CCloseCaptionLookupParams *params )
+{
+ HWND control = GetDlgItem( wnd, IDC_CCTOKENLIST );
+ if ( !control )
+ return;
+
+ InsertTextColumn( control, 0, 200, "CloseCaption Token" );
+ InsertTextColumn( control, 1, 800, "Text" );
+
+
+ ListView_DeleteAllItems( control );
+
+ SendMessage( control, WM_SETFONT, (WPARAM)g_UnicodeFont, MAKELPARAM (TRUE, 0) );
+
+ StringIndex_t i = g_pLocalize->GetFirstStringIndex();
+ int saveSelected = -1;
+
+ while ( INVALID_LOCALIZE_STRING_INDEX != i )
+ {
+ char const *name = g_pLocalize->GetNameByIndex( i );
+
+ LV_ITEMW lvItem;
+ memset( &lvItem, 0, sizeof( lvItem ) );
+ lvItem.iItem = ListView_GetItemCount( control );
+ lvItem.mask = LVIF_TEXT | LVIF_PARAM;
+ lvItem.lParam = (LPARAM)i;
+
+ wchar_t label[ 256 ];
+ g_pLocalize->ConvertANSIToUnicode( name, label, sizeof( label ) );
+
+ lvItem.pszText = label;
+ lvItem.cchTextMax = 256;
+
+ SendMessage( control, LVM_INSERTITEMW, 0, (LPARAM)(const LV_ITEMW FAR*)(&lvItem));
+
+ lvItem.mask = LVIF_TEXT;
+ lvItem.iSubItem = 1;
+
+ wchar_t *value = g_pLocalize->GetValueByIndex( i );
+
+ lvItem.pszText = (wchar_t *)value;
+ lvItem.cchTextMax = 1024;
+
+ SendMessage( control, LVM_SETITEMW, 0, (LPARAM)(const LV_ITEMW FAR*)(&lvItem));
+
+ if ( !Q_stricmp( name, params->m_szCCToken ) )
+ {
+ ListView_SetItemState( control, lvItem.iItem, LVIS_SELECTED, LVIS_STATEIMAGEMASK );
+ saveSelected = lvItem.iItem;
+ }
+ i = g_pLocalize->GetNextStringIndex( i );
+ }
+
+ if ( saveSelected != -1 )
+ {
+ ListView_EnsureVisible(control, saveSelected, FALSE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK CloseCaptionLookupDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ HWND control = GetDlgItem( hwndDlg, IDC_CCTOKENLIST );
+ DWORD exStyle = GetWindowLong( control, GWL_EXSTYLE );
+ exStyle |= LVS_EX_FULLROWSELECT;
+ SetWindowLong( control, GWL_EXSTYLE, exStyle );
+
+ PopulateCloseCaptionTokenList( hwndDlg, &g_Params );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetDlgItemText( hwndDlg, IDC_CCTOKEN, g_Params.m_szCCToken );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_CCTOKENLIST ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ /*
+ //int selindex = SendMessage( GetDlgItem( hwndDlg, IDC_CCTOKENLIST ), LB_GETCURSEL, 0, 0 );
+ if ( selindex == LB_ERR )
+ {
+ mxMessageBox( NULL, "You must select an entry from the list", g_appTitle, MB_OK );
+ return TRUE;
+ }
+ */
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_CCTOKEN ), WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szCCToken ), (LPARAM)g_Params.m_szCCToken );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ case WM_NOTIFY:
+ {
+ if ( wParam == IDC_CCTOKENLIST )
+ {
+ NMHDR *hdr = ( NMHDR * )lParam;
+ if ( hdr->code == LVN_ITEMCHANGED )
+ {
+ HWND control = GetDlgItem( hwndDlg, IDC_CCTOKENLIST );
+
+ NM_LISTVIEW *nmlv = ( NM_LISTVIEW * )lParam;
+
+ int item = nmlv->iItem;
+
+ if ( item >= 0 )
+ {
+ // look up the lparam value
+ LVITEM lvi;
+ memset( &lvi, 0, sizeof( lvi ) );
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = item;
+
+ if ( ListView_GetItem( control, &lvi ) )
+ {
+ char const *name = g_pLocalize->GetNameByIndex( lvi.lParam );
+ if ( name )
+ {
+ Q_strncpy( g_Params.m_szCCToken, name, sizeof( g_Params.m_szCCToken ) );
+ SendMessage( GetDlgItem( hwndDlg, IDC_CCTOKEN ), WM_SETTEXT, (WPARAM)sizeof( g_Params.m_szCCToken ), (LPARAM)g_Params.m_szCCToken );
+ }
+ }
+ }
+ return FALSE;
+ }
+ if ( hdr->code == NM_DBLCLK )
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_CCTOKEN ), WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szCCToken ), (LPARAM)g_Params.m_szCCToken );
+ EndDialog( hwndDlg, 1 );
+ return FALSE;
+ }
+ }
+
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int CloseCaptionLookup( CCloseCaptionLookupParams *params )
+{
+ g_Params = *params;
+
+ g_UnicodeFont = CreateFont(
+ -10,
+ 0,
+ 0,
+ 0,
+ FW_NORMAL,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ "Tahoma" );
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_CCLOOKUP ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)CloseCaptionLookupDialogProc );
+
+ DeleteObject( g_UnicodeFont );
+ g_UnicodeFont = NULL;
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/cclookup.h b/utils/hlfaceposer/cclookup.h
new file mode 100644
index 0000000..047ac7f
--- /dev/null
+++ b/utils/hlfaceposer/cclookup.h
@@ -0,0 +1,27 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CCLOOKUP_H
+#define CCLOOKUP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+#include "utlvector.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CCloseCaptionLookupParams : public CBaseDialogParams
+{
+ char m_szCCToken[ 1024 ];
+};
+
+// Display/create dialog
+int CloseCaptionLookup( CCloseCaptionLookupParams *params );
+
+#endif // CCLOOKUP_H
diff --git a/utils/hlfaceposer/channelproperties.cpp b/utils/hlfaceposer/channelproperties.cpp
new file mode 100644
index 0000000..2161081
--- /dev/null
+++ b/utils/hlfaceposer/channelproperties.cpp
@@ -0,0 +1,124 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include "resource.h"
+#include "ChannelProperties.h"
+#include "ChoreoView.h"
+#include "choreoactor.h"
+#include "choreoscene.h"
+#include "mdlviewer.h"
+
+static CChannelParams g_Params;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK ChannelPropertiesDialogProc
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK ChannelPropertiesDialogProc ( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_CHANNELNAME, g_Params.m_szName );
+
+ HWND control = GetDlgItem( hwndDlg, IDC_ACTORCHOICE );
+
+ if ( !g_Params.m_bShowActors )
+ {
+ // Hide the combo box
+ if ( control )
+ {
+ ShowWindow( control, SW_HIDE );
+ }
+ control = GetDlgItem( hwndDlg, IDC_STATIC_ACTOR );
+ if ( control )
+ {
+ ShowWindow( control, SW_HIDE );
+ }
+ }
+ else
+ {
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ if ( g_Params.m_pScene )
+ {
+ for ( int i = 0 ; i < g_Params.m_pScene->GetNumActors() ; i++ )
+ {
+ CChoreoActor *actor = g_Params.m_pScene->GetActor( i );
+ if ( actor )
+ {
+ // add text to combo box
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)actor->GetName() );
+ }
+ }
+ }
+
+ SendMessage( control, CB_SETCURSEL, (WPARAM)0, (LPARAM)0 );
+ }
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_CHANNELNAME ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ g_Params.m_szName[ 0 ] = 0;
+ GetDlgItemText( hwndDlg, IDC_CHANNELNAME, g_Params.m_szName, 256 );
+
+ if ( g_Params.m_bShowActors )
+ {
+ HWND control = GetDlgItem( hwndDlg, IDC_ACTORCHOICE );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szSelectedActor ), (LPARAM)g_Params.m_szSelectedActor );
+ }
+ }
+
+ EndDialog( hwndDlg, 1 );
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int ChannelProperties( CChannelParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_CHANNELPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)ChannelPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/channelproperties.h b/utils/hlfaceposer/channelproperties.h
new file mode 100644
index 0000000..9a4402f
--- /dev/null
+++ b/utils/hlfaceposer/channelproperties.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CHANNELPROPERTIES_H
+#define CHANNELPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CChoreoScene;
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CChannelParams : public CBaseDialogParams
+{
+ // i/o channel name
+ char m_szName[ 256 ];
+
+ // For creating a new channel:
+ // i
+ bool m_bShowActors;
+ // i/o
+ char m_szSelectedActor[ 256 ];
+ // i
+ CChoreoScene *m_pScene;
+};
+
+// set/create channel properties
+int ChannelProperties( CChannelParams *params );
+
+#endif // CHANNELPROPERTIES_H
diff --git a/utils/hlfaceposer/choiceproperties.cpp b/utils/hlfaceposer/choiceproperties.cpp
new file mode 100644
index 0000000..30956c1
--- /dev/null
+++ b/utils/hlfaceposer/choiceproperties.cpp
@@ -0,0 +1,125 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "resource.h"
+#include "ChoiceProperties.h"
+#include <mxtk/mx.h>
+#include "mdlviewer.h"
+
+static CChoiceParams g_Params;
+
+static void PopulateChoiceList( HWND wnd, CChoiceParams *params )
+{
+ HWND control = GetDlgItem( wnd, IDC_CHOICE );
+ if ( !control )
+ return;
+
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ int c = params->m_Choices.Count();
+
+ if ( params->m_nSelected == -1 )
+ params->m_nSelected = 0;
+
+ if ( params->m_nSelected >= 0 && params->m_nSelected < c )
+ {
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)params->m_Choices[ params->m_nSelected ].choice );
+ }
+
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *text = params->m_Choices[ i ].choice;
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)text );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK ChoicePropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ PopulateChoiceList( hwndDlg, &g_Params );
+
+ SetDlgItemText( hwndDlg, IDC_STATIC_PROMPT, g_Params.m_szPrompt );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ char selected[ MAX_CHOICE_TEXT_SIZE ];
+ selected[ 0 ] = 0;
+ HWND control = GetDlgItem( hwndDlg, IDC_CHOICE );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( selected ), (LPARAM)selected );
+ }
+
+ g_Params.m_nSelected = -1;
+ int c = g_Params.m_Choices.Count();
+
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *text = g_Params.m_Choices[ i ].choice;
+ if ( stricmp( text, selected ) )
+ {
+ continue;
+ }
+
+ g_Params.m_nSelected = i;
+ break;
+ }
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int ChoiceProperties( CChoiceParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_CHOICEDIALOG ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)ChoicePropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/choiceproperties.h b/utils/hlfaceposer/choiceproperties.h
new file mode 100644
index 0000000..d13211b
--- /dev/null
+++ b/utils/hlfaceposer/choiceproperties.h
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CHOICEPROPERTIES_H
+#define CHOICEPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+
+#define MAX_CHOICE_TEXT_SIZE 128
+
+struct ChoiceText
+{
+ char choice[ MAX_CHOICE_TEXT_SIZE ];
+};
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CChoiceParams : public CBaseDialogParams
+{
+
+ char m_szPrompt[ 256 ];
+
+ CUtlVector< ChoiceText > m_Choices;
+
+ // i/o active choice and output choice
+ int m_nSelected; // -1 for none
+};
+
+// Display/create dialog
+int ChoiceProperties( CChoiceParams *params );
+
+#endif // CHOICEPROPERTIES_H
diff --git a/utils/hlfaceposer/choreoactorwidget.cpp b/utils/hlfaceposer/choreoactorwidget.cpp
new file mode 100644
index 0000000..aefe8e0
--- /dev/null
+++ b/utils/hlfaceposer/choreoactorwidget.cpp
@@ -0,0 +1,415 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "choreoactorwidget.h"
+#include "choreochannelwidget.h"
+#include "choreoactor.h"
+#include "choreoview.h"
+#include "choreowidgetdrawhelper.h"
+#include "mxBitmapButton.h"
+#include "choreoviewcolors.h"
+#include "choreochannel.h"
+#include "filesystem.h"
+#include "StudioModel.h"
+
+#define ACTOR_NAME_HEIGHT 26
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parent -
+// x -
+// y -
+// w -
+// h -
+// 0 -
+// 0 -
+//-----------------------------------------------------------------------------
+CActorBitmapButton::CActorBitmapButton( CChoreoActorWidget *actor, mxWindow *parent, int x, int y, int w, int h, int id /*= 0*/, const char *bitmap /*= 0*/ )
+: mxBitmapButton( parent, x, y, w, h, id, bitmap )
+{
+ m_pActor = actor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoActorWidget *CActorBitmapButton::GetActor( void )
+{
+ return m_pActor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CActorActiveCheckBox::CActorActiveCheckBox( CChoreoActorWidget *actor, mxWindow *parent, int x, int y, int w, int h, const char *label /*= 0*/, int id /*= 0*/ )
+ : mxCheckBox( parent, x, y, w, h, label, id )
+{
+ m_pActor = actor;
+}
+
+CChoreoActorWidget *CActorActiveCheckBox::GetActor( void )
+{
+ return m_pActor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CChoreoActorWidget::CChoreoActorWidget( CChoreoWidget *parent )
+: CChoreoWidget( parent )
+{
+ m_pParent = parent;
+
+ m_pActor = NULL;
+
+ m_bShowChannels = true;
+
+ m_btnOpen = new CActorBitmapButton( this, m_pView, 0, 0, 0, 0, IDC_CHANNELOPEN, "gfx/hlfaceposer/channelopen.bmp" );
+ m_btnClose = new CActorBitmapButton( this, m_pView, 0, 0, 0, 0, IDC_CHANNELCLOSE, "gfx/hlfaceposer/channelclose.bmp" );
+
+ ShowChannels( m_bShowChannels );
+
+ memset( m_rgCurrentSetting, 0, sizeof( m_rgCurrentSetting ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoActorWidget::~CChoreoActorWidget( void )
+{
+ for ( int i = 0 ; i < m_Channels.Size(); i++ )
+ {
+ CChoreoChannelWidget *c = m_Channels[ i ];
+ delete c;
+ }
+ m_Channels.RemoveAll();
+
+ delete m_btnOpen;
+ delete m_btnClose;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoActorWidget::GetShowChannels( void )
+{
+ return m_bShowChannels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Switch modes
+// Input : show -
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::ShowChannels( bool show )
+{
+ m_bShowChannels = show;
+
+ m_btnOpen->setVisible( !m_bShowChannels );
+ m_btnClose->setVisible( m_bShowChannels );
+
+ m_pView->InvalidateLayout();
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::Create( void )
+{
+ // Create objects for children
+ Assert( m_pActor );
+
+ // Create objects for children
+ for ( int i = 0; i < m_pActor->GetNumChannels(); i++ )
+ {
+ CChoreoChannel *channel = m_pActor->GetChannel( i );
+ Assert( channel );
+ if ( !channel )
+ {
+ continue;
+ }
+
+ CChoreoChannelWidget *channelWidget = new CChoreoChannelWidget( this );
+ channelWidget->SetChannel( channel );
+ channelWidget->Create();
+
+ AddChannel( channelWidget );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::Layout( RECT& rc )
+{
+ setBounds( rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top );
+
+ int buttonSize = 16;
+ int ypos = rc.top + ( ACTOR_NAME_HEIGHT - buttonSize )/ 2;
+
+ m_btnOpen->setBounds( rc.left + 2, ypos, buttonSize, buttonSize );
+ m_btnClose->setBounds( rc.left + 2, ypos, buttonSize, buttonSize );
+
+ bool buttonsVisible = ( ypos > m_pView->GetStartRow() && ( ypos + buttonSize ) < m_pView->GetEndRow() ) ? true : false;
+
+ if ( !buttonsVisible )
+ {
+ m_btnOpen->setVisible( false );
+ m_btnClose->setVisible( false );
+ }
+ else
+ {
+ m_btnOpen->setVisible( !m_bShowChannels );
+ m_btnClose->setVisible( m_bShowChannels );
+ }
+
+ RECT rcChannels;
+ rcChannels = rc;
+ rcChannels.top += ACTOR_NAME_HEIGHT;
+
+ // Create objects for children
+ for ( int i = 0; i < m_Channels.Size(); i++ )
+ {
+ CChoreoChannelWidget *channel = m_Channels[ i ];
+ Assert( channel );
+ if ( !channel )
+ {
+ continue;
+ }
+
+ rcChannels.bottom = rcChannels.top + channel->GetItemHeight();
+
+ channel->Layout( rcChannels );
+
+ OffsetRect( &rcChannels, 0, channel->GetItemHeight() );
+
+ channel->setVisible( m_bShowChannels );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CChoreoActorWidget::GetItemHeight( void )
+{
+ int itemHeight = ACTOR_NAME_HEIGHT + 2;
+ if ( m_bShowChannels )
+ {
+ for ( int i = 0; i < m_Channels.Size(); i++ )
+ {
+ CChoreoChannelWidget *channel = m_Channels[ i ];
+ itemHeight += channel->GetItemHeight();
+ }
+ }
+ return itemHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::redraw( CChoreoWidgetDrawHelper& drawHelper )
+{
+ if ( !getVisible() )
+ return;
+
+ CChoreoActor *actor = GetActor();
+ if ( !actor )
+ return;
+
+ RECT rcClient = getBounds();
+
+ if ( !actor->GetActive() )
+ {
+ RECT rcBg = rcClient;
+ rcBg.right = rcBg.left + m_pView->GetLabelWidth() ;
+ InflateRect( &rcBg, -3, -5 );
+
+ drawHelper.DrawFilledRect( RGB( 220, 220, 220 ), rcBg );
+ }
+
+ RECT rcText;
+
+ rcText.left = rcClient.left;
+ rcText.right = rcClient.left + m_pView->GetLabelWidth();
+ rcText.top = rcClient.top;
+ rcText.bottom = rcClient.top + ACTOR_NAME_HEIGHT;
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ACTORLINE, PS_SOLID, 1, 0, rcText.top,
+ rcClient.right, rcText.top );
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ACTORLINE, PS_SOLID, 1, 0, rcClient.bottom-2,
+ rcClient.right, rcClient.bottom-2 );
+ drawHelper.DrawColoredLine( RGB(200,206,255), PS_SOLID, 1, 0, rcClient.bottom-1,
+ rcClient.right, rcClient.bottom-1 );
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_DIVIDER, PS_SOLID, 1, rcText.right, rcClient.top,
+ rcText.right, rcClient.bottom-1 );
+
+ RECT rcName = rcText;
+
+ rcName.left += 18;
+ char n[ 512 ];
+ V_strcpy_safe( n, actor->GetName() );
+
+ drawHelper.DrawColoredText( "Arial",
+ m_pView->GetFontSize() + 5,
+ 1000,
+ actor->GetActive() ? COLOR_CHOREO_ACTORNAME : COLOR_CHOREO_ACTORNAME_INACTIVE,
+ rcName, n );
+
+ if ( !actor->GetActive() )
+ {
+ strcpy( n, "(inactive)" );
+
+ RECT rcInactive = rcName;
+ int len = drawHelper.CalcTextWidth( "Arial", m_pView->GetFontSize() - 2, 500, n );
+ rcInactive.left = rcInactive.right - len - 5;
+ rcInactive.top += 3;
+ rcInactive.bottom = rcInactive.top + m_pView->GetFontSize() - 2;
+
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ COLOR_CHOREO_ACTORNAME_INACTIVE, rcInactive, n );
+ }
+
+ rcName.left -= 18;
+
+
+ if ( actor->GetFacePoserModelName()[0] )
+ {
+ int textWidth = drawHelper.CalcTextWidth( "Arial", m_pView->GetFontSize() + 5, 1000, actor->GetName() );
+ RECT rcModelName = rcName;
+ rcModelName.left += ( 14 + textWidth + 2 );
+
+ int fontsize = m_pView->GetFontSize() - 2;
+
+ char shortname[ 512 ];
+ Q_FileBase (actor->GetFacePoserModelName(), shortname, sizeof( shortname ) );
+ strcat( shortname, ".mdl" );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, shortname );
+
+ rcModelName.left = rcModelName.right - len - 5;
+
+ rcModelName.top = rcModelName.bottom - fontsize;
+ OffsetRect( &rcModelName, 0, -3 );
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, COLOR_CHOREO_LIGHTTEXT, rcModelName, shortname );
+ }
+ if ( m_bShowChannels )
+ {
+ for ( int j = 0; j < GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = GetChannel( j );
+ if ( channel )
+ {
+ channel->redraw( drawHelper );
+ }
+
+ RECT rcChannel = channel->getBounds();
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ACTORLINE, PS_SOLID, 1, rcText.right+1, rcChannel.top,
+ rcChannel.right, rcChannel.top );
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ACTORLINE, PS_SOLID, 1, rcText.right+1, rcChannel.bottom,
+ rcChannel.right, rcChannel.bottom );
+
+ }
+ return;
+ }
+
+ OffsetRect( &rcName, m_pView->GetLabelWidth() + 10, 0 );
+ rcName.right = w();
+
+ char sz[ 256 ];
+ // count channels and events
+ int ev = 0;
+ for ( int i = 0; i < actor->GetNumChannels(); i++ )
+ {
+ CChoreoChannel *ch = actor->GetChannel( i );
+ if ( ch )
+ {
+ ev += ch->GetNumEvents();
+ }
+ }
+ sprintf( sz, "%i channels with %i events hidden", actor->GetNumChannels(), ev );
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize(), FW_NORMAL, COLOR_CHOREO_ACTORNAME, rcName, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoActor
+//-----------------------------------------------------------------------------
+CChoreoActor *CChoreoActorWidget::GetActor( void )
+{
+ return m_pActor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::SetActor( CChoreoActor *actor )
+{
+ m_pActor = actor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::AddChannel( CChoreoChannelWidget *channel )
+{
+ m_Channels.AddToTail( channel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::RemoveChannel( CChoreoChannelWidget *channel )
+{
+ m_Channels.FindAndRemove( channel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+// Output : CChoreoChannelWidget
+//-----------------------------------------------------------------------------
+CChoreoChannelWidget *CChoreoActorWidget::GetChannel( int num )
+{
+ return m_Channels[ num ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoActorWidget::GetNumChannels( void )
+{
+ return m_Channels.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float *CChoreoActorWidget::GetSettings( void )
+{
+ return m_rgCurrentSetting;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoActorWidget::ResetSettings( void )
+{
+ memset( m_rgCurrentSetting, 0, sizeof( m_rgCurrentSetting ) );
+}
diff --git a/utils/hlfaceposer/choreoactorwidget.h b/utils/hlfaceposer/choreoactorwidget.h
new file mode 100644
index 0000000..b3cfb72
--- /dev/null
+++ b/utils/hlfaceposer/choreoactorwidget.h
@@ -0,0 +1,118 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOACTORWIDGET_H
+#define CHOREOACTORWIDGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "studio.h"
+#include "choreowidget.h"
+#include "utlvector.h"
+#include "mxBitmapButton.h"
+#include "expressions.h"
+
+class CChoreoActor;
+class CChoreoChannelWidget;
+class mxCheckBox;
+class CChoreoActorWidget;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CActorBitmapButton : public mxBitmapButton
+{
+public:
+ CActorBitmapButton( CChoreoActorWidget *actor, mxWindow *parent, int x, int y, int w, int h, int id = 0, const char *bitmap = 0 );
+
+ CChoreoActorWidget *GetActor( void );
+private:
+
+ CChoreoActorWidget *m_pActor;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CActorActiveCheckBox : public mxCheckBox
+{
+public:
+ CActorActiveCheckBox( CChoreoActorWidget *actor, mxWindow *parent, int x, int y, int w, int h, const char *label = 0, int id = 0);
+
+ CChoreoActorWidget *GetActor( void );
+private:
+
+ CChoreoActorWidget *m_pActor;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: The base actor ui widget. Owns the channels
+//-----------------------------------------------------------------------------
+class CChoreoActorWidget : public CChoreoWidget
+{
+public:
+ typedef CChoreoWidget BaseClass;
+
+ // Construction / destruction
+ CChoreoActorWidget( CChoreoWidget *parent );
+ virtual ~CChoreoActorWidget( void );
+
+ virtual void Create( void );
+ virtual void Layout( RECT& rc );
+
+ virtual void redraw(CChoreoWidgetDrawHelper& drawHelper);
+
+ // Accessors
+ CChoreoActor *GetActor( void );
+ void SetActor( CChoreoActor *actor );
+
+ // Manipulate channels
+ void AddChannel( CChoreoChannelWidget *channel );
+ void RemoveChannel( CChoreoChannelWidget *channel );
+ CChoreoChannelWidget *GetChannel( int num );
+ int GetNumChannels( void );
+
+ // Override height because we can be open/collapsed and we contain the channels
+ virtual int GetItemHeight( void );
+
+ // UI interactions
+ void DeleteChannel( void );
+ void NewChannel( void );
+ void MoveChannelUp( void );
+ void MoveChannelDown( void );
+
+ // Expanded view or contracted view
+ void ShowChannels( bool show );
+ bool GetShowChannels( void );
+
+ float *GetSettings( void );
+
+ void ResetSettings( void );
+
+private:
+ // Context menu handler
+ void ShowRightClickMenu( int mx, int my );
+
+ // The underlying actor
+ CChoreoActor *m_pActor;
+
+ // Children
+ CUtlVector < CChoreoChannelWidget * > m_Channels;
+
+ // Expanded mode?
+ bool m_bShowChannels;
+
+ // Expand/collapse buttons
+ CActorBitmapButton *m_btnOpen;
+ CActorBitmapButton *m_btnClose;
+
+ CActorActiveCheckBox *m_cbActive;
+
+ float m_rgCurrentSetting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+};
+
+#endif // CHOREOACTORWIDGET_H
diff --git a/utils/hlfaceposer/choreochannelwidget.cpp b/utils/hlfaceposer/choreochannelwidget.cpp
new file mode 100644
index 0000000..d41b209
--- /dev/null
+++ b/utils/hlfaceposer/choreochannelwidget.cpp
@@ -0,0 +1,1207 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <stdio.h>
+#include <mxtk/mxPopupMenu.h>
+#include "hlfaceposer.h"
+#include "choreochannelwidget.h"
+#include "choreoeventwidget.h"
+#include "choreoactorwidget.h"
+#include "choreochannel.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoview.h"
+#include "choreoevent.h"
+#include "choreoviewcolors.h"
+#include "utlrbtree.h"
+#include "utllinkedlist.h"
+#include "iclosecaptionmanager.h"
+#include "PhonemeEditor.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "filesystem.h"
+
+#define AUDIO_HEIGHT 18
+#define STREAM_FONT "Tahoma"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CChoreoChannelWidget::CChoreoChannelWidget( CChoreoActorWidget *parent )
+: CChoreoWidget( parent )
+{
+ m_pChannel = NULL;
+ m_pParent = parent;
+ m_bHasAudio = false;
+ m_nBaseHeight = 0;
+ m_nSelectorEventIndex = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoChannelWidget::~CChoreoChannelWidget( void )
+{
+ for ( int i = 0 ; i < m_Events.Size(); i++ )
+ {
+ CChoreoEventWidget *e = m_Events[ i ];
+ delete e;
+ }
+ m_Events.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create child windows
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::Create( void )
+{
+ Assert( m_pChannel );
+
+ // Create objects for children
+ for ( int i = 0; i < m_pChannel->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *e = m_pChannel->GetEvent( i );
+ Assert( e );
+ if ( !e )
+ {
+ continue;
+ }
+
+ CChoreoEventWidget *eventWidget = new CChoreoEventWidget( this );
+ eventWidget->SetEvent( e );
+ eventWidget->Create();
+
+ AddEvent( eventWidget );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoChannelWidget::GetTimeForMousePosition( int mx )
+{
+ int dx = mx - m_pView->GetLabelWidth();
+ float windowfrac = ( float ) dx / ( float ) ( w() - m_pView->GetLabelWidth() );
+ float time = m_pView->GetStartTime() + windowfrac * ( m_pView->GetEndTime() - m_pView->GetStartTime() );
+ return time;
+}
+
+static bool EventStartTimeLessFunc( CChoreoEventWidget * const &p1, CChoreoEventWidget * const &p2 )
+{
+ CChoreoEventWidget *w1;
+ CChoreoEventWidget *w2;
+
+ w1 = const_cast< CChoreoEventWidget * >( p1 );
+ w2 = const_cast< CChoreoEventWidget * >( p2 );
+
+ CChoreoEvent *e1;
+ CChoreoEvent *e2;
+
+ e1 = w1->GetEvent();
+ e2 = w2->GetEvent();
+
+ return e1->GetStartTime() < e2->GetStartTime();
+}
+
+void CChoreoChannelWidget::LayoutEventInRow( CChoreoEventWidget *event, int row, RECT& rc )
+{
+ int itemHeight = BaseClass::GetItemHeight();
+
+ RECT rcEvent;
+ rcEvent.left = m_pView->GetPixelForTimeValue( event->GetEvent()->GetStartTime() );
+ if ( event->GetEvent()->HasEndTime() )
+ {
+ rcEvent.right = m_pView->GetPixelForTimeValue( event->GetEvent()->GetEndTime() );
+ }
+ else
+ {
+ rcEvent.right = rcEvent.left + 8;
+ }
+ rcEvent.top = rc.top + ( row ) * itemHeight + 2;
+ rcEvent.bottom = rc.top + ( row + 1 ) * itemHeight - 2;
+ event->Layout( rcEvent );
+}
+
+static bool EventCollidesWithRows( CUtlLinkedList< CChoreoEventWidget *, int >& list, CChoreoEventWidget *event )
+{
+ float st = event->GetEvent()->GetStartTime();
+ float ed = event->GetEvent()->HasEndTime() ? event->GetEvent()->GetEndTime() : event->GetEvent()->GetStartTime();
+
+ for ( int i = list.Head(); i != list.InvalidIndex(); i = list.Next( i ) )
+ {
+ CChoreoEvent *test = list[ i ]->GetEvent();
+
+ float teststart = test->GetStartTime();
+ float testend = test->HasEndTime() ? test->GetEndTime() : test->GetStartTime();
+
+ // See if spans overlap
+ if ( teststart >= ed )
+ continue;
+ if ( testend <= st )
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+int CChoreoChannelWidget::GetVerticalStackingCount( bool layout, RECT *rc )
+{
+ CUtlRBTree< CChoreoEventWidget * > sorted( 0, 0, EventStartTimeLessFunc );
+
+ CUtlVector< CUtlLinkedList< CChoreoEventWidget *, int > > rows;
+
+ int i;
+ // Sort items
+ int c = m_Events.Size();
+ for ( i = 0; i < c; i++ )
+ {
+ sorted.Insert( m_Events[ i ] );
+ }
+
+ for ( i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) )
+ {
+ CChoreoEventWidget *event = sorted[ i ];
+ Assert( event );
+ if ( !rows.Count() )
+ {
+ rows.AddToTail();
+
+ CUtlLinkedList< CChoreoEventWidget *, int >& list = rows[ 0 ];
+ list.AddToHead( event );
+
+ if ( layout )
+ {
+ LayoutEventInRow( event, 0, *rc );
+ }
+ continue;
+ }
+
+ // Does it come totally after what's in rows[0]?
+ int rowCount = rows.Count();
+ bool addrow = true;
+
+ for ( int j = 0; j < rowCount; j++ )
+ {
+ CUtlLinkedList< CChoreoEventWidget *, int >& list = rows[ j ];
+
+ if ( !EventCollidesWithRows( list, event ) )
+ {
+ // Update row event list
+ list.AddToHead( event );
+ addrow = false;
+ if ( layout )
+ {
+ LayoutEventInRow( event, j, *rc );
+ }
+ break;
+ }
+ }
+
+ if ( addrow )
+ {
+ // Add a new row
+ int idx = rows.AddToTail();
+ CUtlLinkedList< CChoreoEventWidget *, int >& list = rows[ idx ];
+ list.AddToHead( event );
+ if ( layout )
+ {
+ LayoutEventInRow( event, rows.Count() - 1, *rc );
+ }
+ }
+ }
+
+ return max( 1, rows.Count() );
+}
+
+int CChoreoChannelWidget::GetItemHeight( void )
+{
+ int itemHeight = BaseClass::GetItemHeight();
+ int stackCount = GetVerticalStackingCount( false, NULL );
+
+ CheckHasAudio();
+
+ int h = stackCount * itemHeight;
+
+ // Remember the base height
+ m_nBaseHeight = h;
+
+ if ( m_bHasAudio && m_pView->GetShowCloseCaptionData() )
+ {
+ h += 2 * AUDIO_HEIGHT;
+ }
+
+ return h;
+}
+
+bool CChoreoChannelWidget::CheckHasAudio()
+{
+ m_bHasAudio = false;
+ // Create objects for children
+ for ( int i = 0; i < m_Events.Size(); i++ )
+ {
+ CChoreoEventWidget *event = m_Events[ i ];
+ if ( event->GetEvent()->GetType() == CChoreoEvent::SPEAK )
+ {
+ m_bHasAudio = true;
+ break;
+ }
+ }
+ return m_bHasAudio;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::Layout( RECT& rc )
+{
+ setBounds( rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top );
+
+ GetVerticalStackingCount( true, &rc );
+ CheckHasAudio();
+
+ /*
+ // Create objects for children
+ for ( int i = 0; i < m_Events.Size(); i++ )
+ {
+ CChoreoEventWidget *event = m_Events[ i ];
+ Assert( event );
+ if ( !event )
+ {
+ continue;
+ }
+
+ RECT rcEvent;
+ rcEvent.left = m_pView->GetPixelForTimeValue( event->GetEvent()->GetStartTime() );
+ if ( event->GetEvent()->HasEndTime() )
+ {
+ rcEvent.right = m_pView->GetPixelForTimeValue( event->GetEvent()->GetEndTime() );
+ }
+ else
+ {
+ rcEvent.right = rcEvent.left + 8;
+ }
+ rcEvent.top = rc.top + 2;
+ rcEvent.bottom = rc.bottom - 2;
+ event->Layout( rcEvent );
+ }
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::redraw( CChoreoWidgetDrawHelper& drawHelper )
+{
+ if ( !getVisible() )
+ return;
+
+ CChoreoChannel *channel = GetChannel();
+ if ( !channel )
+ return;
+
+ RECT rcText;
+ rcText = getBounds();
+
+ rcText.right = m_pView->GetLabelWidth();
+
+ if ( !channel->GetActive() )
+ {
+ RECT rcBg = rcText;
+ InflateRect( &rcBg, -5, -5 );
+
+ drawHelper.DrawFilledRect( RGB( 210, 210, 210 ), rcBg );
+ }
+
+ RECT rcName = rcText;
+
+ rcName.left += 20;
+ char n[ 512 ];
+ V_strcpy_safe( n, channel->GetName() );
+
+ drawHelper.DrawColoredText( "Arial",
+ m_pView->GetFontSize() + 2,
+ FW_HEAVY,
+ channel->GetActive() ? COLOR_CHOREO_CHANNELNAME : COLOR_CHOREO_ACTORNAME_INACTIVE,
+ rcName, n );
+
+ if ( !channel->GetActive() )
+ {
+ strcpy( n, "(inactive)" );
+
+ RECT rcInactive = rcName;
+ int len = drawHelper.CalcTextWidth( "Arial", m_pView->GetFontSize(), 500, n );
+ rcInactive.left = rcInactive.right - len;
+ //rcInactive.top += 3;
+ //rcInactive.bottom = rcInactive.top + m_pView->GetFontSize() - 2;
+
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ COLOR_CHOREO_ACTORNAME_INACTIVE, rcInactive, n );
+ }
+
+ rcName.left -= 20;
+
+ RECT rcEventArea = getBounds();
+ rcEventArea.left = m_pView->GetLabelWidth() + 1;
+ rcEventArea.top -= 20;
+
+ drawHelper.StartClipping( rcEventArea );
+
+ if ( m_bHasAudio )
+ {
+ RenderCloseCaptionExpandCollapseRect( drawHelper, rcEventArea );
+ if ( m_pView->GetShowCloseCaptionData() )
+ {
+ RenderCloseCaptionExpandCollapseRect( drawHelper, rcEventArea );
+ RenderCloseCaptionInfo( drawHelper, rcEventArea );
+ RenderCloseCaptions( drawHelper, rcEventArea );
+ RenderCloseCaptionSelectors( drawHelper, rcEventArea );
+ }
+ }
+
+ for ( int j = GetNumEvents()-1; j >= 0; j-- )
+ {
+ CChoreoEventWidget *event = GetEvent( j );
+ if ( event )
+ {
+ event->redraw( drawHelper );
+ }
+ }
+
+ drawHelper.StopClipping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcEventArea -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::RenderCloseCaptionInfo( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea )
+{
+ wchar_t wstr[ 1024 ];
+ COLORREF barColor = RGB( 100, 200, 255 );
+
+ {
+ RECT rcText = rcEventArea;
+ rcText.left += 2;
+ rcText.top = rcEventArea.bottom - 15;
+ rcText.bottom = rcText.top + 12;
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ COLOR_CHOREO_TEXT, rcText, "token/data:" );
+ }
+
+ // Walk the events looking for SPEAK events (esp if marked as MASTER with >= 1 slave)
+ for ( int j = GetNumEvents()-1; j >= 0; j-- )
+ {
+ CChoreoEventWidget *event = GetEvent( j );
+ CChoreoEvent *e = event->GetEvent();
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ continue;
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE )
+ continue;
+
+ char const *label = "";
+
+ bool showState = false;
+ bool stateValid = false;
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ showState = true;
+ if ( e->GetNumSlaves() >= 1 )
+ {
+ barColor = RGB( 100, 200, 255 );
+ label = e->GetCloseCaptionToken();
+ }
+ else
+ {
+ barColor = RGB( 100, 150, 100 );
+ label = e->GetParameters();
+ }
+
+ char cctoken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( e->GetPlaybackCloseCaptionToken( cctoken, sizeof( cctoken ) ) )
+ {
+ stateValid = closecaptionmanager->LookupUnicodeText( GetCloseCaptionLanguageId(), cctoken, wstr, sizeof( wstr ) / sizeof( wchar_t ) );
+ }
+ }
+ else
+ {
+ barColor = RGB( 150, 150, 150 );
+ label = "-disabled-";
+ }
+
+ // Found one!!!
+ RECT rcEvent = event->getBounds();
+
+ float bestEndTime = max( e->GetEndTime(), e->GetLastSlaveEndTime() );
+ int pixeloffset = (int)( ( bestEndTime - e->GetStartTime() ) * m_pView->GetPixelsPerSecond() + 0.5f );
+
+ rcEvent.right = rcEvent.left + pixeloffset;
+ rcEvent.top = rcEventArea.bottom - 3;
+ rcEvent.bottom = rcEventArea.bottom;
+
+
+ drawHelper.DrawFilledRect( barColor, rcEvent );
+
+ RECT rcTriangle;
+ rcTriangle = rcEvent;
+ rcTriangle.right = rcTriangle.left + 3;
+ rcTriangle.left -= 3;
+
+ OffsetRect( &rcTriangle, 0, -6 );
+ rcTriangle.bottom += 6;
+ drawHelper.DrawTriangleMarker( rcTriangle, barColor, true );
+
+ rcTriangle.left = rcEvent.right - 3;
+ rcTriangle.right = rcEvent.right + 3;
+
+ drawHelper.DrawTriangleMarker( rcTriangle, barColor, true );
+
+ RECT rcText = rcEvent;
+ rcText.bottom = rcText.top + 12;
+ OffsetRect( &rcText, 5, -12 );
+
+ if ( showState )
+ {
+ int stateMarkWidth = 12;
+ RECT rcState = rcText;
+ rcState.right = rcState.left + stateMarkWidth;
+ rcText.left += stateMarkWidth;
+
+ COLORREF symColor = stateValid ? RGB( 40, 100, 40 ) : RGB( 200, 40, 40 );
+
+ drawHelper.DrawColoredTextCharset(
+ "Marlett",
+ m_pView->GetFontSize() - 2,
+ 500,
+ SYMBOL_CHARSET,
+ symColor,
+ rcState,
+ stateValid ? "a" : "r" );
+
+ }
+
+ if ( e->IsSuppressingCaptionAttenuation() )
+ {
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ RGB( 80, 80, 100 ), rcText, "%s [no attenuate]", label );
+
+ }
+ else
+ {
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ RGB( 80, 80, 100 ), rcText, label );
+ }
+
+
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcEventArea -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::RenderCloseCaptions( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea )
+{
+ {
+ RECT rcText = rcEventArea;
+ rcText.top = rcEventArea.top + m_nBaseHeight + AUDIO_HEIGHT + 5;
+ rcText.bottom = rcText.top + 12;
+ rcText.left += 12;
+ drawHelper.DrawColoredText( "Arial", m_pView->GetFontSize() - 2, 500,
+ COLOR_CHOREO_TEXT, rcText, "%s", CSentence::NameForLanguage( GetCloseCaptionLanguageId() ) );
+
+ // Previous
+ GetCloseCaptionLanguageRect( rcText, true );
+ drawHelper.DrawColoredTextCharset(
+ "Marlett",
+ m_pView->GetFontSize(),
+ 500,
+ SYMBOL_CHARSET,
+ COLOR_CHOREO_TEXT,
+ rcText,
+ "3" );
+
+ // Next
+ GetCloseCaptionLanguageRect( rcText, false );
+ drawHelper.DrawColoredTextCharset(
+ "Marlett",
+ m_pView->GetFontSize(),
+ 500,
+ SYMBOL_CHARSET,
+ COLOR_CHOREO_TEXT,
+ rcText,
+ "4" );
+ }
+
+ // Walk the events looking for SPEAK events (esp if marked as MASTER with >= 1 slave)
+ for ( int j = GetNumEvents()-1; j >= 0; j-- )
+ {
+ CChoreoEventWidget *event = GetEvent( j );
+ CChoreoEvent *e = event->GetEvent();
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ continue;
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE ||
+ e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ continue;
+
+ char cctoken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+
+ bool valid = e->GetPlaybackCloseCaptionToken( cctoken, sizeof( cctoken ) );
+ if ( !valid )
+ continue;
+
+ wchar_t wstr[ 1024 ];
+
+ valid = closecaptionmanager->LookupStrippedUnicodeText( GetCloseCaptionLanguageId(), cctoken, wstr, sizeof( wstr ) / sizeof( wchar_t ) );
+
+ // Found one!!!
+ RECT rcEvent = event->getBounds();
+
+ float bestEndTime = max( e->GetEndTime(), e->GetLastSlaveEndTime() );
+ int pixeloffset = (int)( ( bestEndTime - e->GetStartTime() ) * m_pView->GetPixelsPerSecond() + 0.5f );
+
+ rcEvent.right = rcEvent.left + pixeloffset;
+ rcEvent.top = rcEventArea.top + m_nBaseHeight + AUDIO_HEIGHT + 5;
+ rcEvent.bottom = rcEvent.top + 12;
+ rcEvent.left += 5;
+
+ COLORREF textColor = valid ? RGB( 80, 80, 100 ) : RGB( 225, 40, 40 );
+
+ drawHelper.DrawColoredTextW( STREAM_FONT, m_pView->GetFontSize() - 2, 500,
+ textColor, rcEvent, wstr );
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoChannel
+//-----------------------------------------------------------------------------
+CChoreoChannel *CChoreoChannelWidget::GetChannel( void )
+{
+ return m_pChannel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::SetChannel( CChoreoChannel *channel )
+{
+ m_pChannel = channel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::AddEvent( CChoreoEventWidget *event )
+{
+ m_Events.AddToTail( event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::RemoveEvent( CChoreoEventWidget *event )
+{
+ m_Events.FindAndRemove( event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+// Output : CChoreoEventWidget
+//-----------------------------------------------------------------------------
+CChoreoEventWidget *CChoreoChannelWidget::GetEvent( int num )
+{
+ return m_Events[ num ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoChannelWidget::GetNumEvents( void )
+{
+ return m_Events.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::MoveEventToTail( CChoreoEventWidget *event )
+{
+ for ( int i = 0; i < GetNumEvents(); i++ )
+ {
+ CChoreoEventWidget *ew = GetEvent( i );
+ if ( ew == event )
+ {
+ m_Events.Remove( i );
+ m_Events.AddToTail( ew );
+ break;
+ }
+ }
+}
+
+int CChoreoChannelWidget::GetChannelItemUnderMouse( int mx, int my )
+{
+ m_nSelectorEventIndex = -1;
+
+ if ( !m_bHasAudio )
+ return CLOSECAPTION_NONE;
+
+ RECT rcCCArea;
+ GetCloseCaptionExpandCollapseRect( rcCCArea );
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ if ( PtInRect( &rcCCArea, pt ) )
+ {
+ return CLOSECAPTION_EXPANDCOLLAPSE;
+ }
+
+ // previous
+ GetCloseCaptionLanguageRect( rcCCArea, true );
+ if ( PtInRect( &rcCCArea, pt ) )
+ {
+ return CLOSECAPTION_PREVLANGUAGE;
+ }
+
+ // next language
+ GetCloseCaptionLanguageRect( rcCCArea, false );
+ if ( PtInRect( &rcCCArea, pt ) )
+ {
+ return CLOSECAPTION_NEXTLANGUAGE;
+ }
+
+ CUtlVector< CloseCaptionInfo > vecSelectors;
+ GetCloseCaptions( vecSelectors );
+ int c = vecSelectors.Count();
+ if ( vecSelectors.Count() > 0 )
+ {
+ int i;
+ for ( i = 0; i < c; ++i )
+ {
+ CloseCaptionInfo& check = vecSelectors[ i ];
+ if ( check.isSelector && PtInRect( &check.rcSelector, pt ) )
+ {
+ m_nSelectorEventIndex = check.eventindex;
+ return CLOSECAPTION_SELECTOR;
+ }
+ }
+
+ for ( i = 0; i < c; ++i )
+ {
+ CloseCaptionInfo& check = vecSelectors[ i ];
+ if ( PtInRect( &check.rcCaption, pt ) )
+ {
+ m_nSelectorEventIndex = check.eventindex;
+ return CLOSECAPTION_CAPTION;
+ }
+ }
+ }
+
+ return CLOSECAPTION_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::HandleSelectorClicked()
+{
+ if ( m_nSelectorEventIndex < 0 )
+ return;
+
+ if ( m_nSelectorEventIndex >= m_Events.Count() )
+ return;
+
+ CChoreoEvent *event = GetEvent( m_nSelectorEventIndex )->GetEvent();
+ SetUsingCombinedFieldByTokenName( event->GetCloseCaptionToken(), !event->IsUsingCombinedFile() );
+}
+
+void CChoreoChannelWidget::SetUsingCombinedFieldByTokenName( char const *token, bool usingcombinedfile )
+{
+ int c = GetNumEvents();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = GetEvent( i )->GetEvent();
+ if ( !Q_stricmp( e->GetCloseCaptionToken(), token ) )
+ {
+ e->SetUsingCombinedFile( usingcombinedfile );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoEvent
+//-----------------------------------------------------------------------------
+CChoreoEvent *CChoreoChannelWidget::GetCaptionClickedEvent()
+{
+ if ( m_nSelectorEventIndex < 0 )
+ return NULL;
+
+ if ( m_nSelectorEventIndex >= m_Events.Count() )
+ return NULL;
+
+ CChoreoEvent *event = GetEvent( m_nSelectorEventIndex )->GetEvent();
+ return event;
+}
+
+void CChoreoChannelWidget::GetCloseCaptionExpandCollapseRect( RECT& rc )
+{
+ Assert( m_bHasAudio );
+
+ rc = getBounds();
+ rc.left = m_pView->GetLabelWidth() + 2;
+ rc.right = rc.left + 12;
+
+ rc.top += 2;
+ rc.bottom = rc.top + 12;
+}
+
+void CChoreoChannelWidget::GetCloseCaptionLanguageRect( RECT& rc, bool previous )
+{
+ Assert( m_bHasAudio );
+
+ RECT rcEventArea = getBounds();
+ rcEventArea.left = m_pView->GetLabelWidth() + 1;
+ rcEventArea.top -= 20;
+
+ rc = rcEventArea;
+ rc.top = rcEventArea.top + m_nBaseHeight + AUDIO_HEIGHT + 5;
+ rc.bottom = rc.top + 12;
+ rc.left += 2;
+ rc.right = rc.left + 12;
+
+ if ( !previous )
+ {
+ int textlen = CChoreoWidgetDrawHelper::CalcTextWidth
+ (
+ "Arial",
+ m_pView->GetFontSize()-2,
+ 500,
+ CSentence::NameForLanguage( GetCloseCaptionLanguageId() )
+ );
+
+ OffsetRect( &rc, textlen + 10, 0 );
+ }
+}
+
+void CChoreoChannelWidget::RenderCloseCaptionSelectors( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea )
+{
+ CUtlVector< CloseCaptionInfo > vecSelectors;
+ GetCloseCaptions( vecSelectors );
+ int c = vecSelectors.Count();
+ if ( vecSelectors.Count() > 0 )
+ {
+ for ( int i = 0; i < c; ++i )
+ {
+ CloseCaptionInfo& check = vecSelectors[ i ];
+
+ if ( !check.isSelector )
+ continue;
+
+ CChoreoEventWidget *e = GetEvent( check.eventindex );
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+
+ bool upArrow = !event->IsUsingCombinedFile();
+ COLORREF clr = RGB( 63, 63, 63 ); // upArrow ? RGB( 255, 0, 0 ) : RGB( 0, 0, 255 );
+
+ RECT rc = check.rcSelector;
+
+ POINT endpt;
+ endpt.x = rc.right - 2;
+
+ if ( upArrow )
+ {
+ endpt.y = rc.top - 9;
+ }
+ else
+ {
+ endpt.y = rc.bottom + 9;
+ }
+
+ POINT startpt;
+ startpt.x = ( rc.left + rc.right ) * 0.5;
+ startpt.y = ( rc.top + rc.bottom ) * 0.5;
+
+ drawHelper.DrawCircle(
+ clr,
+ endpt.x,
+ endpt.y,
+ 3 , true );
+
+ drawHelper.DrawColoredLine( clr, PS_SOLID, 1, startpt.x, startpt.y, endpt.x, endpt.y );
+
+
+ drawHelper.DrawCircle(
+ clr,
+ startpt.x,
+ startpt.y,
+ 7, true );
+ }
+ }
+}
+
+void CChoreoChannelWidget::GetCloseCaptions( CUtlVector< CloseCaptionInfo >& selectors )
+{
+ selectors.RemoveAll();
+
+ // Walk the events looking for SPEAK events (esp if marked as MASTER with >= 1 slave)
+ for ( int j = GetNumEvents()-1; j >= 0; j-- )
+ {
+ CChoreoEventWidget *event = GetEvent( j );
+ CChoreoEvent *e = event->GetEvent();
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ continue;
+
+ CChoreoEvent::CLOSECAPTION capType = e->GetCloseCaptionType();
+
+ if ( capType == CChoreoEvent::CC_SLAVE )
+ continue;
+
+ bool isSelector = ( e->GetNumSlaves() >= 1 ) && capType == CChoreoEvent::CC_MASTER;
+
+ // Found one!!!
+ RECT rcEvent = event->getBounds();
+ RECT rcCaption = rcEvent;
+
+ rcEvent.right = rcEvent.left + 16;
+ OffsetRect( &rcEvent, -16, rcEvent.bottom - rcEvent.top );
+ rcEvent.bottom = rcEvent.top + 16;
+
+ CloseCaptionInfo ccs;
+ ccs.rcSelector = rcEvent;
+ ccs.isSelector = isSelector;
+
+ rcCaption.top += rcEvent.bottom - rcEvent.top;
+
+ RECT rcEventArea = getBounds();
+
+ rcCaption.bottom = rcEventArea.bottom;
+
+ // Now compute true right edge
+ float bestEndTime = max( e->GetEndTime(), e->GetLastSlaveEndTime() );
+ int pixeloffset = (int)( ( bestEndTime - e->GetStartTime() ) * m_pView->GetPixelsPerSecond() + 0.5f );
+ rcCaption.right = rcCaption.left + pixeloffset;
+
+ ccs.rcCaption = rcCaption;
+
+ ccs.eventindex = j;
+ selectors.AddToTail( ccs );
+ }
+}
+
+
+void CChoreoChannelWidget::RenderCloseCaptionExpandCollapseRect( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea )
+{
+ if ( !m_bHasAudio )
+ return;
+
+ RECT rcCCArea;
+ GetCloseCaptionExpandCollapseRect( rcCCArea );
+
+ COLORREF symColor = RGB( 100, 100, 100 );
+
+ drawHelper.DrawColoredTextCharset(
+ "Marlett",
+ m_pView->GetFontSize(),
+ 900,
+ SYMBOL_CHARSET,
+ symColor,
+ rcCCArea,
+ m_pView->GetShowCloseCaptionData() ? "6" : "4" );
+}
+
+void CChoreoChannelWidget::GetMasterAndSlaves( CChoreoEvent *master, CUtlVector< CChoreoEvent * >& fulllist )
+{
+ // Old
+ int c = GetNumEvents();
+ int i;
+ for ( i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = GetEvent( i )->GetEvent();
+ if ( !Q_stricmp( master->GetCloseCaptionToken(), e->GetCloseCaptionToken() ) )
+ {
+ if ( fulllist.Find( e ) == fulllist.InvalidIndex() )
+ {
+ fulllist.AddToTail( e );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcBounds -
+//-----------------------------------------------------------------------------
+void CChoreoChannelWidget::redrawStatus( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient, int areaUnderMouse )
+{
+ if ( !getVisible() )
+ return;
+
+ if ( areaUnderMouse != CLOSECAPTION_CAPTION )
+ return;
+
+ CChoreoEvent *e = GetCaptionClickedEvent();
+ if ( !e )
+ return;
+
+ int deflateborder = 1;
+ int fontsize = 9;
+
+ // Now draw the label
+ RECT rcEventLabel;
+ rcEventLabel = rcClient;
+
+ InflateRect( &rcEventLabel, 0, -deflateborder );
+
+ // rcEventLabel.top += 2;
+ rcEventLabel.left += 2;
+ //rcEventLabel.top = rcEventLabel.bottom - 2 * ( fontsize + 2 ) - 1;
+ //rcEventLabel.bottom = rcEventLabel.top + fontsize + 2;
+
+ /*
+ HDC dc = drawHelper.GrabDC();
+
+ int leftAdd = 16;
+
+ if ( CChoreoEventWidget::GetImage( event->GetType() ) )
+ {
+ mxbitmapdata_t *image = CChoreoEventWidget::GetImage( event->GetType() );
+ if ( image )
+ {
+ RECT rcFixed = rcEventLabel;
+ drawHelper.OffsetSubRect( rcFixed );
+ DrawBitmapToDC( dc, rcFixed.left, rcFixed.top, leftAdd, leftAdd,
+ *image );
+ }
+ }
+
+ // Draw Type Name:
+ //rcEventLabel.top -= 4;
+
+ rcEventLabel.left = rcClient.left + 32;
+ rcEventLabel.bottom = rcEventLabel.top + fontsize + 2;
+ // OffsetRect( &rcEventLabel, 0, 2 );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, "%s event \"%s\"",
+ event->NameForType( event->GetType() ), event->GetName() );
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, COLOR_INFO_TEXT, rcEventLabel, "%s event \"%s\"",
+ event->NameForType( event->GetType() ), event->GetName() );
+
+ OffsetRect( &rcEventLabel, 0, fontsize + 2 );
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, COLOR_INFO_TEXT,
+ rcEventLabel, "parameters \"%s\"", GetLabelText() );
+ */
+
+ char const *label = "";
+
+ bool showState = false;
+ bool stateValid = false;
+
+ wchar_t wstr[ 1024 ];
+ COLORREF labelColor = COLOR_INFO_TEXT;
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ showState = true;
+ if ( e->GetNumSlaves() >= 1 )
+ {
+ label = e->GetCloseCaptionToken();
+ }
+ else
+ {
+ label = e->GetParameters();
+ }
+ }
+ else if ( e->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE )
+ {
+ showState = true;
+ label = e->GetCloseCaptionToken();
+ }
+ else
+ {
+ label = "-disabled-";
+ }
+
+ char cctoken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( showState && e->GetPlaybackCloseCaptionToken( cctoken, sizeof( cctoken ) ) )
+ {
+ stateValid = closecaptionmanager->LookupUnicodeText( GetCloseCaptionLanguageId(), cctoken, wstr, sizeof( wstr ) / sizeof( wchar_t ) );
+ }
+
+ RECT rcText = rcEventLabel;
+
+ rcText.left += 250;
+ rcText.bottom = rcText.top + fontsize + 1;
+
+ if ( showState )
+ {
+ int stateMarkWidth = 12;
+ RECT rcState = rcText;
+ rcState.right = rcState.left + stateMarkWidth;
+ rcText.left += stateMarkWidth;
+
+ COLORREF symColor = stateValid ? RGB( 40, 100, 40 ) : RGB( 200, 40, 40 );
+
+ drawHelper.DrawColoredTextCharset(
+ "Marlett",
+ fontsize+2,
+ 500,
+ SYMBOL_CHARSET,
+ symColor,
+ rcState,
+ stateValid ? "a" : "r" );
+
+ }
+
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ labelColor, rcText, "closecaption token: %s", label );
+
+ RECT saveText = rcText;
+
+ COLORREF statusClr = RGB( 20, 150, 20 );
+
+ if ( e->GetCloseCaptionType() != CChoreoEvent::CC_DISABLED )
+ {
+ if ( e->GetNumSlaves() >= 1 ||
+ e->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE )
+ {
+
+ bool combinedValid = m_pView->ValidateCombinedSoundCheckSum( e );
+
+ OffsetRect( &rcText, 0, fontsize + 3 );
+
+ char cf[ 256 ];
+ Q_strncpy( cf, "(no file)", sizeof( cf ) );
+
+ // Get the filename, including expansion for gender
+ e->ComputeCombinedBaseFileName( cf, sizeof( cf ), e->IsCombinedUsingGenderToken() );
+ bool gendermacro = Q_stristr( cf, SOUNDGENDER_MACRO ) ? true : false;
+
+ char exist[ 256 ];
+
+ if ( gendermacro )
+ {
+ bool valid[2];
+ char actualfile[ 256 ];
+ soundemitter->GenderExpandString( GENDER_MALE, cf, actualfile, sizeof( actualfile ) );
+ valid[ 0 ] = filesystem->FileExists( actualfile );
+ soundemitter->GenderExpandString( GENDER_FEMALE, cf, actualfile, sizeof( actualfile ) );
+ valid[ 1 ] = filesystem->FileExists( actualfile );
+
+ if ( !valid[ 0 ] || !valid[ 1 ] )
+ {
+ statusClr = RGB( 255, 0, 0 );
+ }
+
+ Q_snprintf( exist, sizeof( exist ), "%s", valid ? "exist" : "missing!" );
+ }
+ else
+ {
+ bool valid = filesystem->FileExists( cf );
+ if ( !valid )
+ {
+ statusClr = RGB( 255, 0, 0 );
+ }
+
+ Q_snprintf( exist, sizeof( exist ), "%s", valid ? "exists" : "missing!" );
+ }
+
+ RECT rcPartial = rcText;
+
+ char sz[ 256 ];
+ Q_snprintf( sz, sizeof( sz ),
+ "combined file active [ %s ] gender[ %s ] up-to-date[ ",
+ e->IsUsingCombinedFile() ? "yes" : "no",
+ e->IsCombinedUsingGenderToken() ? "yes" : "no" );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, 500, sz );
+
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ labelColor, rcPartial, sz );
+
+ rcPartial.left += len;
+
+ Q_snprintf( sz, sizeof( sz ),
+ "%s",
+ combinedValid ? "yes" : "no" );
+
+ len = drawHelper.CalcTextWidth( "Arial", fontsize, 500, sz );
+
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ combinedValid ? RGB( 20, 150, 20 ) : RGB( 255, 0, 0 ),
+ rcPartial, sz );
+
+ rcPartial.left += len;
+
+ Q_snprintf( sz, sizeof( sz ),
+ " ]: %s, %s ",
+ cf,
+ gendermacro ? "files" : "file" );
+
+ len = drawHelper.CalcTextWidth( "Arial", fontsize, 500, sz );
+
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ labelColor, rcPartial, sz );
+
+ rcPartial.left += len;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ statusClr, rcPartial, exist );
+
+ }
+
+ rcText = saveText;
+
+ OffsetRect( &rcText, 400, 0 );
+
+ // Print out script file for sound
+ int soundindex = soundemitter->GetSoundIndex( cctoken );
+ if ( soundindex >= 0 )
+ {
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ Assert( scriptfile );
+ if ( scriptfile )
+ {
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ labelColor, rcText, "sound script: %s", scriptfile );
+ }
+ }
+ else
+ {
+ drawHelper.DrawColoredText( "Arial", fontsize, 500,
+ RGB( 255, 0, 0 ), rcText, "sound not in game_sounds script files!" );
+ }
+ }
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/choreochannelwidget.h b/utils/hlfaceposer/choreochannelwidget.h
new file mode 100644
index 0000000..7558993
--- /dev/null
+++ b/utils/hlfaceposer/choreochannelwidget.h
@@ -0,0 +1,120 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOCHANNELWIDGET_H
+#define CHOREOCHANNELWIDGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "choreowidget.h"
+#include "utlvector.h"
+
+class CChoreoEventWidget;
+class CChoreoActorWidget;
+class CChoreoChannel;
+class CChoreoChannelWidget;
+
+//-----------------------------------------------------------------------------
+// Purpose: The channel container
+//-----------------------------------------------------------------------------
+class CChoreoChannelWidget : public CChoreoWidget
+{
+public:
+ typedef CChoreoWidget BaseClass;
+
+ enum
+ {
+ FULLMENU = 0,
+ NEWEVENTMENU
+ };
+
+ enum
+ {
+ CLOSECAPTION_NONE = 0,
+ CLOSECAPTION_EXPANDCOLLAPSE,
+ CLOSECAPTION_PREVLANGUAGE,
+ CLOSECAPTION_NEXTLANGUAGE,
+ CLOSECAPTION_SELECTOR,
+ CLOSECAPTION_CAPTION,
+ };
+
+ // Construction
+ CChoreoChannelWidget( CChoreoActorWidget *parent );
+ virtual ~CChoreoChannelWidget( void );
+
+ virtual void Create( void );
+ virtual void Layout( RECT& rc );
+
+ virtual int GetItemHeight( void );
+
+ virtual void redraw(CChoreoWidgetDrawHelper& drawHelper);
+ virtual void redrawStatus( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient, int areaUnderMouse );
+
+ // Accessors
+ CChoreoChannel *GetChannel( void );
+ void SetChannel( CChoreoChannel *channel );
+
+ // Manipulate child events
+ void AddEvent( CChoreoEventWidget *event );
+ void RemoveEvent( CChoreoEventWidget *event );
+
+ void MoveEventToTail( CChoreoEventWidget *event );
+
+ CChoreoEventWidget *GetEvent( int num );
+ int GetNumEvents( void );
+
+ // Determine time for click position
+ float GetTimeForMousePosition( int mx );
+
+ int GetChannelItemUnderMouse( int mx, int my );
+
+ CChoreoEvent *GetCaptionClickedEvent();
+ void GetMasterAndSlaves( CChoreoEvent *master, CUtlVector< CChoreoEvent * >& fulllist );
+
+ void HandleSelectorClicked();
+
+private:
+
+ struct CloseCaptionInfo
+ {
+ bool isSelector;
+ RECT rcSelector;
+ RECT rcCaption;
+ int eventindex;
+ };
+
+ void GetCloseCaptionExpandCollapseRect( RECT& rc );
+ void GetCloseCaptionLanguageRect( RECT& rc, bool previous );
+ void GetCloseCaptions( CUtlVector< CloseCaptionInfo >& selectors );
+
+ int GetVerticalStackingCount( bool dolayout, RECT* rc );
+ void LayoutEventInRow( CChoreoEventWidget *event, int row, RECT& rc );
+
+ void RenderCloseCaptionInfo( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea );
+ void RenderCloseCaptions( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea );
+ void RenderCloseCaptionExpandCollapseRect( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea );
+ void RenderCloseCaptionSelectors( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventArea );
+
+ void SetUsingCombinedFieldByTokenName( char const *token, bool usingcombinedfile );
+
+ bool CheckHasAudio();
+
+ // The actor to whom we belong
+ CChoreoActorWidget *m_pParent;
+
+ // The underlying scene object
+ CChoreoChannel *m_pChannel;
+
+ // Children
+ CUtlVector < CChoreoEventWidget * > m_Events;
+ bool m_bHasAudio;
+ int m_nBaseHeight;
+
+ int m_nSelectorEventIndex;
+};
+
+#endif // CHOREOCHANNELWIDGET_H
diff --git a/utils/hlfaceposer/choreoeventwidget.cpp b/utils/hlfaceposer/choreoeventwidget.cpp
new file mode 100644
index 0000000..01affbb
--- /dev/null
+++ b/utils/hlfaceposer/choreoeventwidget.cpp
@@ -0,0 +1,784 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "choreoeventwidget.h"
+#include "choreochannelwidget.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoview.h"
+#include "choreoevent.h"
+#include "choreochannel.h"
+#include "choreoscene.h"
+#include "choreoviewcolors.h"
+#include "ifaceposersound.h"
+#include "snd_audio_source.h"
+#include "RampTool.h"
+
+// Static members
+mxbitmapdata_t CChoreoEventWidget::m_Bitmaps[ FP_NUM_BITMAPS ];
+mxbitmapdata_t CChoreoEventWidget::m_ResumeConditionBitmap;
+mxbitmapdata_t CChoreoEventWidget::m_LockBodyFacingBitmap;
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CChoreoEventWidget::CChoreoEventWidget( CChoreoWidget *parent )
+ : CChoreoWidget( parent )
+{
+ m_pEvent = NULL;
+ m_pParent = parent;
+ m_pWaveFile = NULL;
+ m_nDurationRightEdge= 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoEventWidget::~CChoreoEventWidget( void )
+{
+ delete m_pWaveFile;
+ m_pWaveFile = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::Create( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CChoreoEventWidget::GetLabelText( void )
+{
+ static char label[ 256 ];
+ if ( GetEvent()->GetType() == CChoreoEvent::EXPRESSION )
+ {
+ sprintf( label, "%s : %s", GetEvent()->GetParameters(), GetEvent()->GetParameters2() );
+ }
+ else
+ {
+ V_strcpy_safe( label, GetEvent()->GetParameters() );
+ }
+
+ return label;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CChoreoEventWidget::GetDurationRightEdge( void )
+{
+ return m_nDurationRightEdge;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::Layout( RECT& rc )
+{
+ int requestedW = rc.right - rc.left;
+
+ m_nDurationRightEdge = requestedW;
+
+ setBounds( rc.left, rc.top, requestedW, rc.bottom - rc.top );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// &rcWAV -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT &rcWAV, float length, CChoreoEvent *event )
+{
+ for ( int i = 0; i < event->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ int left = rcWAV.left + (int)( tag->GetPercentage() * ( float )( rcWAV.right - rcWAV.left ) + 0.5f );
+
+ RECT rcMark;
+ rcMark = rcWAV;
+ rcMark.top -= 2;
+ rcMark.bottom = rcMark.top + 6;
+ rcMark.left = left - 3;
+ rcMark.right = left + 3;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 250 ) );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 12;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// &rcWAV -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::DrawAbsoluteTags( CChoreoWidgetDrawHelper& drawHelper, RECT &rcWAV, float length, CChoreoEvent *event )
+{
+ for ( int i = 0; i < event->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+ if ( !tag )
+ continue;
+
+ //
+ int left = rcWAV.left + (int)( tag->GetPercentage() * ( float )( rcWAV.right - rcWAV.left ) + 0.5f );
+
+ RECT rcMark;
+ rcMark = rcWAV;
+ rcMark.top -= 2;
+ rcMark.bottom = rcMark.top + 6;
+ rcMark.left = left - 3;
+ rcMark.right = left + 3;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 250 ) );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 12;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcBounds -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::redrawStatus( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient )
+{
+ if ( !getVisible() )
+ return;
+
+ CChoreoEvent *event = GetEvent();
+ if ( !event )
+ return;
+
+ int deflateborder = 1;
+ int fontsize = 9;
+
+ HDC dc = drawHelper.GrabDC();
+
+ // Now draw the label
+ RECT rcEventLabel;
+ rcEventLabel = rcClient;
+
+ InflateRect( &rcEventLabel, 0, -deflateborder );
+
+ // rcEventLabel.top += 2;
+ rcEventLabel.left += 2;
+ //rcEventLabel.top = rcEventLabel.bottom - 2 * ( fontsize + 2 ) - 1;
+ //rcEventLabel.bottom = rcEventLabel.top + fontsize + 2;
+
+ int leftAdd = 16;
+
+ if ( CChoreoEventWidget::GetImage( event->GetType() ) )
+ {
+ mxbitmapdata_t *image = CChoreoEventWidget::GetImage( event->GetType() );
+ if ( image )
+ {
+ RECT rcFixed = rcEventLabel;
+ drawHelper.OffsetSubRect( rcFixed );
+ DrawBitmapToDC( dc, rcFixed.left, rcFixed.top, leftAdd, leftAdd,
+ *image );
+ }
+ }
+
+ OffsetRect( &rcEventLabel, leftAdd, 0 );
+
+ if ( event->IsResumeCondition() )
+ {
+ RECT rc = rcEventLabel;
+ OffsetRect( &rcEventLabel, 16, 0 );
+ rc.right = rc.left + leftAdd;
+ rc.bottom = rc.top + leftAdd;
+
+ RECT rcFixed = rc;
+ drawHelper.OffsetSubRect( rcFixed );
+ DrawBitmapToDC( dc, rcFixed.left, rcFixed.top,
+ rcFixed.right - rcFixed.left, rcFixed.bottom - rcFixed.top,
+ *CChoreoEventWidget::GetPauseImage() );
+ }
+
+ if ( event->IsLockBodyFacing() )
+ {
+ RECT rc = rcEventLabel;
+ OffsetRect( &rcEventLabel, 16, 0 );
+ rc.right = rc.left + leftAdd;
+ rc.bottom = rc.top + leftAdd;
+
+ RECT rcFixed = rc;
+ drawHelper.OffsetSubRect( rcFixed );
+ DrawBitmapToDC( dc, rcFixed.left, rcFixed.top,
+ rcFixed.right - rcFixed.left, rcFixed.bottom - rcFixed.top,
+ *CChoreoEventWidget::GetLockImage() );
+ }
+
+ // Draw Type Name:
+ OffsetRect( &rcEventLabel, 2, 1 );
+
+ rcEventLabel.left = rcClient.left + 32;
+ rcEventLabel.bottom = rcEventLabel.top + fontsize + 2;
+ // OffsetRect( &rcEventLabel, 0, 2 );
+
+ drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, "%s event \"%s\"", event->NameForType( event->GetType() ), event->GetName() );
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, COLOR_INFO_TEXT, rcEventLabel, "%s event \"%s\"", event->NameForType( event->GetType() ), event->GetName() );
+
+ OffsetRect( &rcEventLabel, 0, fontsize + 2 );
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, COLOR_INFO_TEXT, rcEventLabel, "parameters \"%s\"", GetLabelText() );
+
+}
+
+COLORREF CChoreoEventWidget::GrayOutColor( COLORREF clr )
+{
+ CChoreoEvent *event = GetEvent();
+ if ( !event )
+ return clr;
+ if ( event->GetActive() )
+ return clr;
+
+ int r, g, b;
+ r = GetRValue( clr );
+ g = GetGValue( clr );
+ b = GetBValue( clr );
+ int val = ( r + g + b ) / 3;
+ val += ( 255 - val ) * 0.25f;
+
+ clr = RGB( val, val, val );
+ return clr;
+}
+
+
+void CChoreoEventWidget::DrawSpeakEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ bool ramponly = m_pView->IsRampOnly();
+
+ CChoreoEvent *event = GetEvent();
+ Assert( event );
+
+ HDC dc = drawHelper.GrabDC();
+
+ HBRUSH brEvent = CreateSolidBrush( GrayOutColor( COLOR_CHOREO_EVENT ) );
+ HBRUSH brBackground = CreateSolidBrush( GrayOutColor( COLOR_CHOREO_DARKBACKGROUND ) );
+
+ if ( !ramponly )
+ {
+ FillRect( dc, &rcEventLine, brBackground );
+ }
+
+ // Only draw wav form here if selected
+ if ( IsSelected() )
+ {
+ sound->RenderWavToDC( dc, rcEventLine, GrayOutColor( IsSelected() ? COLOR_CHOREO_EVENT_SELECTED : COLOR_CHOREO_EVENT ), 0.0, m_pWaveFile->GetRunningLength(), m_pWaveFile );
+ }
+
+ //FrameRect( dc, &rcEventLine, brEvent );
+ drawHelper.DrawColoredLine( GrayOutColor( COLOR_CHOREO_EVENT ), PS_SOLID, 3,
+ rcEventLine.left, rcEventLine.top, rcEventLine.left, rcEventLine.bottom );
+ drawHelper.DrawColoredLine( GrayOutColor( COLOR_CHOREO_EVENT ), PS_SOLID, 3,
+ rcEventLine.right, rcEventLine.top, rcEventLine.right, rcEventLine.bottom );
+
+ DeleteObject( brBackground );
+ DeleteObject( brEvent );
+
+ //rcEventLine.top -= 3;
+ DrawRelativeTags( drawHelper, rcEventLine, m_pWaveFile->GetRunningLength(), event );
+}
+
+void CChoreoEventWidget::DrawGestureEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine )
+{
+ CChoreoEvent *event = GetEvent();
+ Assert( event );
+
+ RECT rcEventLine2 = rcEventLine;
+ /*
+ float duration = event->GetDuration();
+ // Crop eventline2
+ if ( duration > 0.0f )
+ {
+ float attack_frac = 0.3; // ( event->GetAttackTime() ) / duration;
+ float decay_frac = 0.7; // ( event->GetDecayTime() ) / duration;
+
+ float event_line_width = rcEventLine.right - rcEventLine.left;
+
+ rcEventLine2.left = rcEventLine.left + attack_frac * event_line_width;
+ rcEventLine2.right = rcEventLine.left + decay_frac * event_line_width;
+ }
+ */
+
+ bool ramponly = m_pView->IsRampOnly();
+
+
+ HDC dc = drawHelper.GrabDC();
+
+ bool nullevent = false;
+
+ COLORREF clrEvent = GrayOutColor( IsSelected() ? COLOR_CHOREO_EVENT_SELECTED : COLOR_CHOREO_EVENT );
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ clrEvent = GrayOutColor( RGB( 50, 50, 120 ) );
+ nullevent = true;
+ }
+
+ HBRUSH brEvent = CreateSolidBrush( clrEvent );
+
+ if ( !ramponly )
+ {
+ FillRect( dc, &rcEventLine2, brEvent );
+ }
+
+ DeleteObject( brEvent );
+
+ if ( ramponly && IsSelected() )
+ {
+ drawHelper.DrawOutlinedRect( GrayOutColor( RGB( 150, 180, 250 ) ), PS_SOLID, 1,
+ rcEventLine2 );
+ }
+ else
+ {
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 127, 127, 127 ) ), PS_SOLID, 1, rcEventLine2.left, rcEventLine2.bottom,
+ rcEventLine2.left, rcEventLine2.top );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 127, 127, 127 ) ), PS_SOLID, 1, rcEventLine2.left, rcEventLine2.top,
+ rcEventLine2.right, rcEventLine2.top );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 31, 31, 31 ) ), PS_SOLID, 1, rcEventLine2.right, rcEventLine2.top,
+ rcEventLine2.right, rcEventLine2.bottom );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 0, 0, 0 ) ), PS_SOLID, 1, rcEventLine2.right, rcEventLine2.bottom,
+ rcEventLine2.left, rcEventLine2.bottom );
+ }
+
+ int rampstart = m_pView->GetPixelForTimeValue( event->GetStartTime( ) );
+ int rampend = m_pView->GetPixelForTimeValue( event->GetEndTime( ) );
+
+// COLORREF clrBottom = RGB( 180, 180, 180 );
+
+// drawHelper.DrawColoredLine( clrBottom, PS_SOLID, 1, rampstart, rcEventLine2.bottom,
+// rcEventLine2.left, rcEventLine2.bottom );
+// drawHelper.DrawColoredLine( clrBottom, PS_SOLID, 1, rcEventLine2.right, rcEventLine2.bottom,
+// rampend, rcEventLine2.bottom );
+
+ if ( !nullevent )
+ {
+ drawHelper.DrawColoredRamp( clrEvent, PS_SOLID, 1,
+ rampstart,
+ rcEventLine2.bottom,
+ rcEventLine2.left,
+ rcEventLine2.top,
+ 0.0f,
+ 1.0f );
+ drawHelper.DrawColoredRamp( clrEvent, PS_SOLID, 1,
+ rcEventLine2.right,
+ rcEventLine2.top,
+ rampend,
+ rcEventLine2.bottom,
+ 0.0f,
+ 1.0f );
+ }
+
+ g_pRampTool->DrawSamplesSimple( drawHelper, event, false, GrayOutColor( RGB( 63, 63, 63 ) ), rcEventLine );
+
+ DrawRelativeTags( drawHelper, rcEventLine, event->GetDuration(), event );
+ DrawAbsoluteTags( drawHelper, rcEventLine, event->GetDuration(), event );
+}
+
+void CChoreoEventWidget::DrawGenericEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine )
+{
+ bool ramponly = m_pView->IsRampOnly();
+
+ CChoreoEvent *event = GetEvent();
+ Assert( event );
+
+ HDC dc = drawHelper.GrabDC();
+
+ COLORREF clrEvent = GrayOutColor( IsSelected() ? COLOR_CHOREO_EVENT_SELECTED : COLOR_CHOREO_EVENT );
+ if ( event->GetType() == CChoreoEvent::SUBSCENE )
+ {
+ clrEvent = GrayOutColor( RGB( 200, 180, 200 ) );
+ }
+
+ HBRUSH brEvent = CreateSolidBrush( clrEvent );
+
+ if ( !ramponly )
+ {
+ FillRect( dc, &rcEventLine, brEvent );
+ }
+
+ DeleteObject( brEvent );
+
+ if ( ramponly && IsSelected() )
+ {
+ drawHelper.DrawOutlinedRect( GrayOutColor( RGB( 150, 180, 250 ) ), PS_SOLID, 1,
+ rcEventLine );
+ }
+ else
+ {
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 127, 127, 127 ) ), PS_SOLID, 1, rcEventLine.left, rcEventLine.bottom,
+ rcEventLine.left, rcEventLine.top );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 127, 127, 127 ) ), PS_SOLID, 1, rcEventLine.left, rcEventLine.top,
+ rcEventLine.right, rcEventLine.top );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 31, 31, 31 ) ), PS_SOLID, 1, rcEventLine.right, rcEventLine.top,
+ rcEventLine.right, rcEventLine.bottom );
+ drawHelper.DrawColoredLine( GrayOutColor( RGB( 0, 0, 0 ) ), PS_SOLID, 1, rcEventLine.right, rcEventLine.bottom,
+ rcEventLine.left, rcEventLine.bottom );
+ }
+
+ g_pRampTool->DrawSamplesSimple( drawHelper, event, false, GrayOutColor( RGB( 63, 63, 63 ) ), rcEventLine );
+
+ DrawRelativeTags( drawHelper, rcEventLine, event->GetDuration(), event );
+ DrawAbsoluteTags( drawHelper, rcEventLine, event->GetDuration(), event );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: FIXME: This should either be embedded or we should draw the caption
+// here
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::redraw( CChoreoWidgetDrawHelper& drawHelper )
+{
+ if ( !getVisible() )
+ return;
+
+ CChoreoEvent *event = GetEvent();
+ if ( !event )
+ return;
+
+ int deflateborder = 1;
+ int fontsize = 9;
+
+ HDC dc = drawHelper.GrabDC();
+ RECT rcClient = getBounds();
+
+ RECT rcDC;
+ drawHelper.GetClientRect( rcDC );
+
+ RECT dummy;
+ if ( !IntersectRect( &dummy, &rcDC, &rcClient ) )
+ return;
+
+ bool ramponly = m_pView->IsRampOnly();
+
+ if ( IsSelected() && !ramponly )
+ {
+ InflateRect( &rcClient, 3, 1 );
+ //rcClient.bottom -= 1;
+ rcClient.right += 1;
+
+ RECT rcFrame = rcClient;
+ RECT rcBorder = rcClient;
+
+ rcFrame.bottom = rcFrame.top + 17;
+ rcBorder.bottom = rcFrame.top + 17;
+
+ COLORREF clrSelection = GrayOutColor( RGB( 0, 63, 63 ) );
+ COLORREF clrBorder = GrayOutColor( RGB( 100, 200, 255 ) );
+
+ HBRUSH brBorder = CreateSolidBrush( clrBorder );
+ HBRUSH brSelected = CreateHatchBrush( HS_FDIAGONAL, clrSelection );
+ for ( int i = 0; i < 2; i++ )
+ {
+ FrameRect( dc, &rcFrame, brSelected );
+ InflateRect( &rcFrame, -1, -1 );
+ }
+ FrameRect( dc, &rcBorder, brBorder );
+ FrameRect( dc, &rcFrame, brBorder );
+
+ DeleteObject( brSelected );
+ DeleteObject( brBorder );
+ rcClient.right -= 1;
+ //rcClient.bottom += 1;
+ InflateRect( &rcClient, -3, -1 );
+ }
+
+ RECT rcEvent;
+ rcEvent = rcClient;
+
+ InflateRect( &rcEvent, 0, -deflateborder );
+
+ rcEvent.bottom = rcEvent.top + 10;
+
+ if ( event->GetType() == CChoreoEvent::SPEAK && m_pWaveFile && !event->HasEndTime() )
+ {
+ event->SetEndTime( event->GetStartTime() + m_pWaveFile->GetRunningLength() );
+ rcEvent.right = ( int )( m_pWaveFile->GetRunningLength() * m_pView->GetPixelsPerSecond() );
+ }
+
+ if ( event->HasEndTime() )
+ {
+ RECT rcEventLine = rcEvent;
+ OffsetRect( &rcEventLine, 0, 1 );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SPEAK:
+ {
+ DrawSpeakEvent( drawHelper, rcEventLine );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ DrawGestureEvent( drawHelper, rcEventLine );
+ }
+ break;
+ default:
+ {
+ DrawGenericEvent( drawHelper, rcEventLine );
+ }
+ break;
+ }
+ }
+ else
+ {
+ RECT rcEventLine = rcEvent;
+ OffsetRect( &rcEventLine, 0, 1 );
+
+ drawHelper.DrawColoredLine( GrayOutColor( COLOR_CHOREO_EVENT ), PS_SOLID, 3,
+ rcEventLine.left - 1, rcEventLine.top, rcEventLine.left - 1, rcEventLine.bottom );
+ }
+
+ if ( event->IsUsingRelativeTag() )
+ {
+ RECT rcTagName;
+ rcTagName = rcClient;
+
+ int length = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, event->GetRelativeTagName() );
+
+ rcTagName.right = rcTagName.left;
+ rcTagName.left = rcTagName.right - length - 4;
+ rcTagName.top += 3;
+ rcTagName.bottom = rcTagName.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, GrayOutColor( RGB( 0, 100, 200 ) ), rcTagName, event->GetRelativeTagName() );
+
+ drawHelper.DrawFilledRect( GrayOutColor( RGB( 0, 100, 250 ) ), rcTagName.right-1, rcTagName.top-2,
+ rcTagName.right+2, rcTagName.bottom + 2 );
+
+ }
+
+ // Now draw the label
+ RECT rcEventLabel;
+ rcEventLabel = rcClient;
+
+ InflateRect( &rcEventLabel, 0, -deflateborder );
+
+ rcEventLabel.top += 15; // rcEventLabel.bottom - 2 * ( fontsize + 2 ) - 1;
+ rcEventLabel.bottom = rcEventLabel.top + fontsize + 2;
+ rcEventLabel.left += 1;
+
+ //rcEventLabel.left -= 8;
+
+ int leftAdd = 16;
+
+ if ( CChoreoEventWidget::GetImage( event->GetType() ) )
+ {
+ mxbitmapdata_t *image = CChoreoEventWidget::GetImage( event->GetType() );
+ if ( image )
+ {
+ DrawBitmapToDC( dc, rcEventLabel.left, rcEventLabel.top, leftAdd, leftAdd,
+ *image );
+ }
+ }
+
+ OffsetRect( &rcEventLabel, leftAdd, 1 );
+
+ if ( event->IsResumeCondition() )
+ {
+ RECT rc = rcEventLabel;
+ OffsetRect( &rcEventLabel, leftAdd, 0 );
+ rc.right = rc.left + leftAdd;
+ rc.bottom = rc.top + leftAdd;
+
+ DrawBitmapToDC( dc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+ *CChoreoEventWidget::GetPauseImage() );
+ }
+
+ if ( event->IsLockBodyFacing() )
+ {
+ RECT rc = rcEventLabel;
+ OffsetRect( &rcEventLabel, 16, 0 );
+ rc.right = rc.left + leftAdd;
+ rc.bottom = rc.top + leftAdd;
+
+ RECT rcFixed = rc;
+ drawHelper.OffsetSubRect( rcFixed );
+ DrawBitmapToDC( dc, rcFixed.left, rcFixed.top,
+ rcFixed.right - rcFixed.left, rcFixed.bottom - rcFixed.top,
+ *CChoreoEventWidget::GetLockImage() );
+ }
+
+ OffsetRect( &rcEventLabel, 2, 1 );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, event->GetName() );
+
+ rcEventLabel.right = rcEventLabel.left + len + 2;
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL, GrayOutColor( RGB( 0, 0, 120 ) ),
+ rcEventLabel, event->GetName() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoEvent
+//-----------------------------------------------------------------------------
+CChoreoEvent *CChoreoEventWidget::GetEvent( void )
+{
+ return m_pEvent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::SetEvent( CChoreoEvent *event )
+{
+ sound->StopAll();
+
+ delete m_pWaveFile;
+ m_pWaveFile = NULL;
+
+ m_pEvent = event;
+
+ if ( event->GetType() == CChoreoEvent::SPEAK )
+ {
+ m_pWaveFile = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the user changes the association of .mdls to actors, then the gender could change and we could need to access a different .wav file
+// Input : -
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::RecomputeWave()
+{
+ if ( m_pEvent->GetType() == CChoreoEvent::SPEAK )
+ {
+ delete m_pWaveFile;
+ m_pWaveFile = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( m_pEvent ) ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::LoadImages( void )
+{
+ for ( int i = 0; i < FP_NUM_BITMAPS; i++ )
+ {
+ m_Bitmaps[ i ].valid = false;
+ }
+
+ m_ResumeConditionBitmap.valid = false;
+ m_LockBodyFacingBitmap.valid = false;
+
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_expression.bmp", m_Bitmaps[ CChoreoEvent::EXPRESSION ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_lookat.bmp", m_Bitmaps[ CChoreoEvent::LOOKAT ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_moveto.bmp", m_Bitmaps[ CChoreoEvent::MOVETO ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_speak.bmp", m_Bitmaps[ CChoreoEvent::SPEAK ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_gesture.bmp", m_Bitmaps[ CChoreoEvent::GESTURE ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_face.bmp", m_Bitmaps[ CChoreoEvent::FACE ] );
+
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_firetrigger.bmp", m_Bitmaps[ CChoreoEvent::FIRETRIGGER ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_sequence.bmp", m_Bitmaps[ CChoreoEvent::SEQUENCE ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_flexanimation.bmp", m_Bitmaps[ CChoreoEvent::FLEXANIMATION ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_subscene.bmp", m_Bitmaps[ CChoreoEvent::SUBSCENE ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_loop.bmp", m_Bitmaps[ CChoreoEvent::LOOP ] );
+
+ LoadBitmapFromFile( "gfx/hlfaceposer/pause.bmp", m_ResumeConditionBitmap );
+
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_interrupt.bmp", m_Bitmaps[ CChoreoEvent::INTERRUPT ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_stoppoint.bmp", m_Bitmaps[ CChoreoEvent::STOPPOINT ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_permit_response.bmp", m_Bitmaps[ CChoreoEvent::PERMIT_RESPONSES ] );
+ LoadBitmapFromFile( "gfx/hlfaceposer/ev_generic.bmp", m_Bitmaps[ CChoreoEvent::GENERIC ] );
+
+ LoadBitmapFromFile( "gfx/hlfaceposer/lock.bmp", m_LockBodyFacingBitmap );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoEventWidget::DestroyImages( void )
+{
+ for ( int i = 0; i < FP_NUM_BITMAPS; i++ )
+ {
+ if ( m_Bitmaps[ i ].valid )
+ {
+ m_Bitmaps[ i ].valid = false;
+ DeleteObject( m_Bitmaps[ i ].image );
+ m_Bitmaps[ i ].image = NULL;
+ }
+ }
+
+ if ( m_ResumeConditionBitmap.valid )
+ {
+ m_ResumeConditionBitmap.valid = false;
+ DeleteObject( m_ResumeConditionBitmap.image );
+ m_ResumeConditionBitmap.image = NULL;
+ }
+
+ if ( m_LockBodyFacingBitmap.valid )
+ {
+ m_LockBodyFacingBitmap.valid = false;
+ DeleteObject( m_LockBodyFacingBitmap.image );
+ m_LockBodyFacingBitmap.image = NULL;
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+// Output : mxbitmapdata_t
+//-----------------------------------------------------------------------------
+mxbitmapdata_t *CChoreoEventWidget::GetImage( int type )
+{
+ return &m_Bitmaps[ type ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : mxbitmapdata_t
+//-----------------------------------------------------------------------------
+mxbitmapdata_t *CChoreoEventWidget::GetPauseImage( void )
+{
+ return &m_ResumeConditionBitmap;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : mxbitmapdata_t
+//-----------------------------------------------------------------------------
+mxbitmapdata_t *CChoreoEventWidget::GetLockImage( void )
+{
+ return &m_LockBodyFacingBitmap;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/choreoeventwidget.h b/utils/hlfaceposer/choreoeventwidget.h
new file mode 100644
index 0000000..3f7774a
--- /dev/null
+++ b/utils/hlfaceposer/choreoeventwidget.h
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOEVENTWIDGET_H
+#define CHOREOEVENTWIDGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxbitmaptools.h"
+#include "choreowidget.h"
+
+class CChoreoEvent;
+class CChoreoChannelWidget;
+class CAudioSource;
+
+#define FP_NUM_BITMAPS 20
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw event UI element and handle mouse interactions
+//-----------------------------------------------------------------------------
+class CChoreoEventWidget : public CChoreoWidget
+{
+public:
+ typedef CChoreoWidget BaseClass;
+
+ // Construction/destruction
+ CChoreoEventWidget( CChoreoWidget *parent );
+ virtual ~CChoreoEventWidget( void );
+
+ // Create children
+ virtual void Create( void );
+ // Redo layout
+ virtual void Layout( RECT& rc );
+ // Screen refresh
+ virtual void redraw(CChoreoWidgetDrawHelper& drawHelper);
+ virtual void redrawStatus( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient );
+
+ virtual int GetDurationRightEdge( void );
+
+ // Access underlying object
+ CChoreoEvent *GetEvent( void );
+ void SetEvent( CChoreoEvent *event );
+
+ // If the user changes the association of .mdls to actors, then the gender could change and we could need to access a different .wav file
+ // Call this to reconcile things
+ void RecomputeWave();
+
+ // System wide icons for various event types ( indexed by CChoreEvent::m_fType )
+ static void LoadImages( void );
+ static void DestroyImages( void );
+ static mxbitmapdata_t *GetImage( int type );
+ static mxbitmapdata_t *GetPauseImage( void );
+ static mxbitmapdata_t *GetLockImage( void );
+
+private:
+
+ COLORREF GrayOutColor( COLORREF clr );
+
+ void DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT &rcWAV, float length, CChoreoEvent *event );
+ void DrawAbsoluteTags( CChoreoWidgetDrawHelper& drawHelper, RECT &rcWAV, float length, CChoreoEvent *event );
+
+ const char *GetLabelText( void );
+
+ void DrawSpeakEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine );
+ void DrawGestureEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine );
+ void DrawGenericEvent( CChoreoWidgetDrawHelper& drawHelper, RECT& rcEventLine );
+ // Parent widget
+ CChoreoWidget *m_pParent;
+
+ // Underlying event
+ CChoreoEvent *m_pEvent;
+
+ int m_nDurationRightEdge;
+
+ // For speak events
+ CAudioSource *m_pWaveFile;
+
+ // Bitmaps for drawing event widgets
+ static mxbitmapdata_t m_Bitmaps[ FP_NUM_BITMAPS ];
+ static mxbitmapdata_t m_ResumeConditionBitmap;
+ static mxbitmapdata_t m_LockBodyFacingBitmap;
+};
+
+#endif // CHOREOEVENTWIDGET_H
diff --git a/utils/hlfaceposer/choreoglobaleventwidget.cpp b/utils/hlfaceposer/choreoglobaleventwidget.cpp
new file mode 100644
index 0000000..d9af31b
--- /dev/null
+++ b/utils/hlfaceposer/choreoglobaleventwidget.cpp
@@ -0,0 +1,216 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "choreoviewcolors.h"
+#include "choreoglobaleventwidget.h"
+#include "choreowidgetdrawhelper.h"
+#include "ChoreoView.h"
+#include "choreoevent.h"
+#include "choreoeventwidget.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CChoreoGlobalEventWidget::CChoreoGlobalEventWidget( CChoreoWidget *parent )
+: CChoreoWidget( parent )
+{
+ m_pEvent = NULL;
+
+ m_bDragging = false;
+ m_xStart = 0;
+ m_hPrevCursor = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoGlobalEventWidget::~CChoreoGlobalEventWidget( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoGlobalEventWidget::Create( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoGlobalEventWidget::Layout( RECT& rc )
+{
+ setBounds( rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top );
+}
+
+//-----------------------------------------------------------------------------
+// Redraw to screen
+//-----------------------------------------------------------------------------
+void CChoreoGlobalEventWidget::redraw( CChoreoWidgetDrawHelper& drawHelper )
+{
+ if ( !getVisible() )
+ return;
+
+ CChoreoEvent *event = GetEvent();
+ if ( !event )
+ return;
+
+ RECT rcTab;
+ rcTab = getBounds();
+
+ bool isLoop = false;
+ COLORREF pointColor = COLOR_CHOREO_SEGMENTDIVIDER;
+ COLORREF clr = COLOR_CHOREO_SEGMENTDIVIDER_BG;
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ clr = COLOR_CHOREO_LOOPPOINT_BG;
+ pointColor = COLOR_CHOREO_LOOPPOINT;
+ isLoop = true;
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ clr = COLOR_CHOREO_STOPPOINT_BG;
+ pointColor = COLOR_CHOREO_STOPPOINT;
+ }
+ break;
+ }
+
+ if ( IsSelected() )
+ {
+ InflateRect( &rcTab, 2, 2 );
+
+ drawHelper.DrawTriangleMarker( rcTab, pointColor );
+
+ InflateRect( &rcTab, -2, -2 );
+
+ drawHelper.DrawTriangleMarker( rcTab, RGB( 240, 240, 220 ) );
+
+ }
+ else
+ {
+ drawHelper.DrawTriangleMarker( rcTab, pointColor );
+ }
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ RECT rcLine = rcTab;
+ rcLine.top = rcTab.bottom + 2;
+ rcLine.bottom = rcClient.bottom;
+ rcLine.left = ( rcLine.left + rcLine.right ) / 2;
+ rcLine.right = rcLine.left;
+
+ if ( IsSelected() )
+ {
+ drawHelper.DrawColoredLine( clr, PS_DOT, 2, rcLine.left, rcLine.top, rcLine.right, rcLine.bottom );
+ }
+ else
+ {
+ drawHelper.DrawColoredLine( clr, PS_DOT, 1, rcLine.left, rcLine.top, rcLine.right, rcLine.bottom );
+ }
+
+ if ( event->GetType() == CChoreoEvent::STOPPOINT )
+ {
+ OffsetRect( &rcTab, -4, 15 );
+
+ mxbitmapdata_t *image = CChoreoEventWidget::GetImage( event->GetType() );
+ if ( image )
+ {
+ drawHelper.OffsetSubRect( rcTab );
+ DrawBitmapToDC( drawHelper.GrabDC(), rcTab.left, rcTab.top, 16, 16, *image );
+ }
+ }
+
+ if ( !isLoop )
+ return;
+
+ COLORREF labelText = COLOR_INFO_TEXT;
+ DrawLabel( drawHelper, labelText, rcLine.left, rcLine.top + 2, false );
+
+ // Figure out loop spot
+ float looptime = (float)atof( event->GetParameters() );
+
+ // Find pixel for that
+ bool clipped = false;
+ int x = m_pView->GetPixelForTimeValue( looptime, &clipped );
+ if ( clipped )
+ return;
+
+ rcLine.left = x;
+ rcLine.right = x;
+
+ clr = COLOR_CHOREO_LOOPPOINT_START_BG;
+ drawHelper.DrawColoredLine( clr, PS_SOLID, 1, rcLine.left, rcLine.top, rcLine.right, rcLine.top + 28);
+
+ DrawLabel( drawHelper, labelText, rcLine.left, rcLine.top + 2, true );
+}
+
+void CChoreoGlobalEventWidget::DrawLabel( CChoreoWidgetDrawHelper& drawHelper, COLORREF clr, int x, int y, bool right )
+{
+ CChoreoEvent *event = GetEvent();
+ if ( !event )
+ return;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, va( "%s", event->GetName() ) );
+
+ RECT rcText;
+ rcText.top = y;
+ rcText.bottom = y + 10;
+ rcText.left = x - len / 2;
+ rcText.right = rcText.left + len;
+
+ if ( !right )
+ {
+ drawHelper.DrawColoredTextCharset( "Marlett", 9, FW_NORMAL, SYMBOL_CHARSET, clr, rcText, "3" );
+ OffsetRect( &rcText, 8, 0 );
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, va( "%s", event->GetName() ) );
+ }
+ else
+ {
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, clr, rcText, va( "%s", event->GetName() ) );
+ OffsetRect( &rcText, len, 0 );
+ drawHelper.DrawColoredTextCharset( "Marlett", 9, FW_NORMAL, SYMBOL_CHARSET, clr, rcText, "4" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoGlobalEventWidget::DrawFocusRect( void )
+{
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &m_rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoEvent
+//-----------------------------------------------------------------------------
+CChoreoEvent *CChoreoGlobalEventWidget::GetEvent( void )
+{
+ return m_pEvent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoGlobalEventWidget::SetEvent( CChoreoEvent *event )
+{
+ m_pEvent = event;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/choreoglobaleventwidget.h b/utils/hlfaceposer/choreoglobaleventwidget.h
new file mode 100644
index 0000000..e1881ca
--- /dev/null
+++ b/utils/hlfaceposer/choreoglobaleventwidget.h
@@ -0,0 +1,59 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOGLOBALEVENTWIDGET_H
+#define CHOREOGLOBALEVENTWIDGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "choreowidget.h"
+
+class CChoreoEvent;
+
+//-----------------------------------------------------------------------------
+// Purpose: For section start/end
+// FIXME: Finish this
+//-----------------------------------------------------------------------------
+class CChoreoGlobalEventWidget : public CChoreoWidget
+{
+public:
+ typedef CChoreoWidget BaseClass;
+
+ // Construction/destruction
+ CChoreoGlobalEventWidget( CChoreoWidget *parent );
+ virtual ~CChoreoGlobalEventWidget( void );
+
+ // Create children
+ virtual void Create( void );
+ // Redo layout
+ virtual void Layout( RECT& rc );
+
+ // Screen refresh
+ virtual void redraw(CChoreoWidgetDrawHelper& drawHelper);
+
+ // Access underlying scene object
+ CChoreoEvent *GetEvent( void );
+ void SetEvent( CChoreoEvent *event );
+
+ // Draw focus rect while mouse dragging is going on
+ void DrawFocusRect( void );
+private:
+
+ void DrawLabel( CChoreoWidgetDrawHelper& drawHelper, COLORREF clr, int x, int y, bool right );
+
+ // The underlying scene object
+ CChoreoEvent *m_pEvent;
+
+ // For updating focus rect
+ bool m_bDragging;
+ int m_xStart;
+ RECT m_rcFocus;
+ RECT m_rcOrig;
+ HCURSOR m_hPrevCursor;
+};
+
+#endif // CHOREOGLOBALEVENTWIDGET_H
diff --git a/utils/hlfaceposer/choreoview.cpp b/utils/hlfaceposer/choreoview.cpp
new file mode 100644
index 0000000..440e69b
--- /dev/null
+++ b/utils/hlfaceposer/choreoview.cpp
@@ -0,0 +1,11647 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+#include <stdio.h>
+#include <mxtk/mxWindow.h>
+#include "mdlviewer.h"
+#include "hlfaceposer.h"
+#include "StudioModel.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "ChoreoView.h"
+#include "choreoevent.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoscene.h"
+#include "choreowidget.h"
+#include "choreoactorwidget.h"
+#include "choreochannelwidget.h"
+#include "choreoglobaleventwidget.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoeventwidget.h"
+#include "viewerSettings.h"
+#include "filesystem.h"
+#include "choreoviewcolors.h"
+#include "ActorProperties.h"
+#include "ChannelProperties.h"
+#include "EventProperties.h"
+#include "GlobalEventProperties.h"
+#include "ifaceposersound.h"
+#include "snd_wave_source.h"
+#include "ifaceposerworkspace.h"
+#include "PhonemeEditor.h"
+#include "iscenetokenprocessor.h"
+#include "InputProperties.h"
+#include "filesystem.h"
+#include "ExpressionTool.h"
+#include "ControlPanel.h"
+#include "faceposer_models.h"
+#include "choiceproperties.h"
+#include "MatSysWin.h"
+#include "tier1/strtools.h"
+#include "GestureTool.h"
+#include "npcevent.h"
+#include "RampTool.h"
+#include "SceneRampTool.h"
+#include "KeyValues.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "cclookup.h"
+#include "iclosecaptionmanager.h"
+#include "AddSoundEntry.h"
+#include "isoundcombiner.h"
+#include <vgui/ILocalize.h>
+#include "scriplib.h"
+#include "WaveBrowser.h"
+#include "filesystem_init.h"
+#include "flexpanel.h"
+#include "tier3/choreoutils.h"
+#include "tier2/p4helpers.h"
+
+
+using namespace vgui;
+
+extern vgui::ILocalize *g_pLocalize;
+
+// 10x magnification
+#define MAX_TIME_ZOOM 1000
+#define TIME_ZOOM_STEP 4
+
+#define PHONEME_FILTER 0.08f
+#define PHONEME_DELAY 0.0f
+
+#define SCRUBBER_HEIGHT 15
+#define TIMELINE_NUMBERS_HEIGHT 11
+
+#define COPYPASTE_FILENAME "scenes/copydatavcd.txt"
+
+extern double realtime;
+extern bool NameLessFunc( const char *const& name1, const char *const& name2 );
+
+// Try to keep shifted times at same absolute time
+static void RescaleExpressionTimes( CChoreoEvent *event, float newstart, float newend )
+{
+ if ( !event || event->GetType() != CChoreoEvent::FLEXANIMATION )
+ return;
+
+ // Did it actually change
+ if ( newstart == event->GetStartTime() &&
+ newend == event->GetEndTime() )
+ {
+ return;
+ }
+
+ float newduration = newend - newstart;
+
+ float dt = 0.0f;
+ //If the end is moving, leave tags stay where they are (dt == 0.0f)
+ if ( newstart != event->GetStartTime() )
+ {
+ // Otherwise, if the new start is later, then tags need to be shifted backwards
+ dt -= ( newstart - event->GetStartTime() );
+ }
+
+ int count = event->GetNumFlexAnimationTracks();
+ int i;
+
+ for ( i = 0; i < count; i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ for ( int type = 0; type < 2; type++ )
+ {
+ int sampleCount = track->GetNumSamples( type );
+ for ( int sample = sampleCount - 1; sample >= 0 ; sample-- )
+ {
+ CExpressionSample *s = track->GetSample( sample, type );
+ if ( !s )
+ continue;
+
+ s->time += dt;
+
+ if ( s->time > newduration || s->time < 0.0f )
+ {
+ track->RemoveSample( sample, type );
+ }
+ }
+ }
+ }
+}
+
+static void RescaleRamp( CChoreoEvent *event, float newduration )
+{
+ float oldduration = event->GetDuration();
+
+ if ( fabs( oldduration - newduration ) < 0.000001f )
+ return;
+
+ if ( newduration <= 0.0f )
+ return;
+
+ float midpointtime = oldduration * 0.5f;
+ float newmidpointtime = newduration * 0.5f;
+
+ int count = event->GetRampCount();
+ int i;
+
+ for ( i = 0; i < count; i++ )
+ {
+ CExpressionSample *sample = event->GetRamp( i );
+ if ( !sample )
+ continue;
+
+ float t = sample->time;
+ if ( t < midpointtime )
+ continue;
+
+ float timefromend = oldduration - t;
+
+ // There's room to just shift it
+ if ( timefromend <= newmidpointtime )
+ {
+ t = newduration - timefromend;
+ }
+ else
+ {
+ // No room, rescale them instead
+ float frac = ( t - midpointtime ) / midpointtime;
+ t = newmidpointtime + frac * newmidpointtime;
+ }
+
+ sample->time = t;
+ }
+}
+
+bool DoesAnyActorHaveAssociatedModelLoaded( CChoreoScene *scene )
+{
+ if ( !scene )
+ return false;
+
+ int c = scene->GetNumActors();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ if ( !a )
+ continue;
+
+ char const *modelname = a->GetFacePoserModelName();
+ if ( !modelname )
+ continue;
+
+ if ( !modelname[ 0 ] )
+ continue;
+
+ char mdlname[ 256 ];
+ Q_strncpy( mdlname, modelname, sizeof( mdlname ) );
+ Q_FixSlashes( mdlname );
+
+ int idx = models->FindModelByFilename( mdlname );
+ if ( idx >= 0 )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *a -
+// Output : StudioModel
+//-----------------------------------------------------------------------------
+StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a )
+{
+ if ( !a || !scene )
+ return NULL;
+
+ Assert( models->GetActiveStudioModel() );
+
+ StudioModel *model = NULL;
+ if ( a->GetFacePoserModelName()[ 0 ] )
+ {
+ int idx = models->FindModelByFilename( a->GetFacePoserModelName() );
+ if ( idx >= 0 )
+ {
+ model = models->GetStudioModel( idx );
+ return model;
+ }
+ }
+
+ // Is there any loaded model with the actorname in it?
+ int c = models->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *modelname = models->GetModelName( i );
+ if ( !Q_stricmp( modelname, a->GetName() ) )
+ {
+ return models->GetStudioModel( i );
+ }
+ }
+
+ // Does any actor have an associated model which is loaded
+ if ( DoesAnyActorHaveAssociatedModelLoaded( scene ) )
+ {
+ // Then return NULL here so we don't override with the default an actor who has a valid model going
+ return NULL;
+ }
+
+ // Couldn't find it and nobody else has a loaded associated model, so just use the default model
+ if ( !model )
+ {
+ model = models->GetActiveStudioModel();
+ }
+ return model;
+}
+
+
+CChoreoView *g_pChoreoView = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+// x -
+// y -
+// w -
+// h -
+// id -
+//-----------------------------------------------------------------------------
+CChoreoView::CChoreoView( mxWindow *parent, int x, int y, int w, int h, int id )
+: IFacePoserToolWindow( "CChoreoView", "Choreography" ), mxWindow( parent, x, y, w, h )
+{
+ m_bRampOnly = false;
+
+ m_bForceProcess = false;
+
+ m_bSuppressLayout = true;
+
+ SetAutoProcess( true );
+
+ m_flLastMouseClickTime = -1.0f;
+ m_bProcessSequences = true;
+
+ m_flPlaybackRate = 1.0f;
+
+ m_pScene = NULL;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+
+ m_bCanDraw = false;
+
+ m_bRedoPending = false;
+ m_nUndoLevel = 0;
+
+ CChoreoEventWidget::LoadImages();
+
+ CChoreoWidget::m_pView = this;
+
+ setId( id );
+
+ m_flLastSpeedScale = 0.0f;
+ m_bResetSpeedScale = false;
+
+ m_nTopOffset = 0;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_nLastVPixelsNeeded = -1;
+
+
+ m_nStartRow = 45;
+ m_nLabelWidth = 140;
+ m_nRowHeight = 35;
+
+ m_bSimulating = false;
+ m_bPaused = false;
+
+ m_bForward = true;
+
+ m_flStartTime = 0.0f;
+ m_flEndTime = 0.0f;
+ m_flFrameTime = 0.0f;
+
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ m_pVertScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOVSCROLL, mxScrollbar::Vertical );
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_CHOREOHSCROLL, mxScrollbar::Horizontal );
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 150.0f;
+
+ m_btnPlay = new mxBitmapButton( this, 2, 4, 16, 16, IDC_PLAYSCENE, "gfx/hlfaceposer/play.bmp" );
+ m_btnPause = new mxBitmapButton( this, 18, 4, 16, 16, IDC_PAUSESCENE, "gfx/hlfaceposer/pause.bmp" );
+ m_btnStop = new mxBitmapButton( this, 34, 4, 16, 16, IDC_STOPSCENE, "gfx/hlfaceposer/stop.bmp" );
+
+ m_pPlaybackRate = new mxSlider( this, 0, 0, 16, 16, IDC_CHOREO_PLAYBACKRATE );
+ m_pPlaybackRate->setRange( 0.0, 2.0, 40 );
+ m_pPlaybackRate->setValue( m_flPlaybackRate );
+
+ ShowButtons( false );
+
+ m_nFontSize = 12;
+
+ for ( int i = 0; i < MAX_ACTORS; i++ )
+ {
+ m_ActorExpanded[ i ].expanded = true;
+ }
+
+ SetChoreoFile( "" );
+
+ //SetFocus( (HWND)getHandle() );
+ if ( workspacefiles->GetNumStoredFiles( IWorkspaceFiles::CHOREODATA ) >= 1 )
+ {
+ LoadSceneFromFile( workspacefiles->GetStoredFile( IWorkspaceFiles::CHOREODATA, 0 ) );
+ }
+
+ ClearABPoints();
+
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+ m_nSelectedEvents = 0;
+ m_nClickedTag = -1;
+ m_nClickedChannelCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE;
+
+ // Mouse dragging
+ m_bDragging = false;
+ m_xStart = 0;
+ m_yStart = 0;
+ m_nDragType = DRAGTYPE_NONE;
+ m_hPrevCursor = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_nScrollbarHeight = 12;
+ m_nInfoHeight = 30;
+
+ ClearStatusArea();
+
+ SetDirty( false );
+
+ m_bCanDraw = true;
+ m_bSuppressLayout = false;
+ m_flScrubberTimeOffset = 0.0f;
+
+ m_bShowCloseCaptionData = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : closing -
+//-----------------------------------------------------------------------------
+bool CChoreoView::Close( void )
+{
+ if ( m_pScene && m_bDirty )
+ {
+ int retval = mxMessageBox( NULL, va( "Save changes to scene '%s'?", GetChoreoFile() ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return false;
+ }
+ if ( retval == 0 )
+ {
+ Save();
+ }
+ }
+
+ if ( m_pScene )
+ {
+ UnloadScene();
+ }
+ return true;
+}
+
+bool CChoreoView::CanClose()
+{
+ if ( m_pScene )
+ {
+ workspacefiles->StartStoringFiles( IWorkspaceFiles::CHOREODATA );
+ workspacefiles->StoreFile( IWorkspaceFiles::CHOREODATA, GetChoreoFile() );
+ workspacefiles->FinishStoringFiles( IWorkspaceFiles::CHOREODATA );
+ }
+
+ if ( m_pScene && m_bDirty && !Close() )
+ {
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called just before window is destroyed
+//-----------------------------------------------------------------------------
+void CChoreoView::OnDelete()
+{
+ if ( m_pScene )
+ {
+ UnloadScene();
+ }
+
+ CChoreoWidget::m_pView = NULL;
+
+ CChoreoEventWidget::DestroyImages();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoView::~CChoreoView()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ReportSceneClearToTools( void )
+{
+ if ( m_pScene )
+ {
+ m_pScene->ResetSimulation();
+ }
+
+ g_pPhonemeEditor->ClearEvent();
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find a time that's less than input on the granularity:
+// e.g., 3.01 granularity 0.05 will be 3.00, 3.05 will be 3.05
+// Input : input -
+// granularity -
+// Output : float
+//-----------------------------------------------------------------------------
+float SnapTime( float input, float granularity )
+{
+ float base = (float)(int)input;
+ float multiplier = (float)(int)( 1.0f / granularity );
+ float fracpart = input - (int)input;
+
+ fracpart *= multiplier;
+
+ fracpart = (float)(int)fracpart;
+ fracpart *= granularity;
+
+ return base + fracpart;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+// left -
+// right -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right )
+{
+ RECT rcFill = m_rcTimeLine;
+ rcFill.bottom -= TIMELINE_NUMBERS_HEIGHT;
+ drawHelper.DrawFilledRect( COLOR_CHOREO_DARKBACKGROUND, rcFill );
+
+ RECT rcLabel;
+ float granularity = 0.5f / ((float)GetTimeZoom( GetToolName() ) / 100.0f);
+
+ drawHelper.DrawColoredLine( COLOR_CHOREO_TIMELINE, PS_SOLID, 1, rc.left, GetStartRow() - 1, rc.right, GetStartRow() - 1 );
+
+ float f = SnapTime( left, granularity );
+ while ( f < right )
+ {
+ float frac = ( f - left ) / ( right - left );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ rcLabel.left = GetLabelWidth() + (int)( frac * ( rc.right - GetLabelWidth() ) );
+ rcLabel.bottom = GetStartRow() - 1;
+ rcLabel.top = rcLabel.bottom - 10;
+
+ if ( f != left )
+ {
+ drawHelper.DrawColoredLine( RGB( 220, 220, 240 ), PS_DOT, 1,
+ rcLabel.left, GetStartRow(), rcLabel.left, h2() );
+ }
+
+ char sz[ 32 ];
+ sprintf( sz, "%.2f", f );
+
+ int textWidth = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ rcLabel.right = rcLabel.left + textWidth;
+
+ OffsetRect( &rcLabel, -textWidth / 2, 0 );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_TEXT, rcLabel, sz );
+
+ }
+ f += granularity;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawSceneABTicks( CChoreoWidgetDrawHelper& drawHelper )
+{
+ RECT rcThumb;
+
+ float scenestart = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : 0.0f;
+ float sceneend = m_rgABPoints[ 1 ].active ? m_rgABPoints[ 1 ].time : 0.0f;
+
+ if ( scenestart )
+ {
+ int markerstart = GetPixelForTimeValue( scenestart );
+
+ rcThumb.left = markerstart - 4;
+ rcThumb.right = markerstart + 4;
+ rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcThumb.bottom = rcThumb.top + 8;
+
+ drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB );
+ }
+
+ if ( sceneend )
+ {
+ int markerend = GetPixelForTimeValue( sceneend );
+
+ rcThumb.left = markerend - 4;
+ rcThumb.right = markerend + 4;
+ rcThumb.top = 2 + GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcThumb.bottom = rcThumb.top + 8;
+
+ drawHelper.DrawTriangleMarker( rcThumb, COLOR_CHOREO_TICKAB );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawRelativeTagLines( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ if ( !m_pScene )
+ return;
+
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.top = GetStartRow();
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+ rcClip.right -= m_nScrollbarHeight;
+
+ drawHelper.StartClipping( rcClip );
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !event->IsUsingRelativeTag() )
+ continue;
+
+ // Using it, find the tag and figure out the time for it
+ CEventRelativeTag *tag = m_pScene->FindTagByName(
+ event->GetRelativeWavName(),
+ event->GetRelativeTagName() );
+
+ if ( !tag )
+ continue;
+
+ // Found it, draw a vertical line
+ //
+ float tagtime = tag->GetStartTime();
+
+ // Convert to pixel value
+ bool clipped = false;
+ int pixel = GetPixelForTimeValue( tagtime, &clipped );
+ if ( clipped )
+ continue;
+
+ drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1,
+ pixel, rcClip.top, pixel, rcClip.bottom );
+ }
+ }
+ }
+
+ drawHelper.StopClipping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the background markings (actor names, etc)
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawBackground( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.top = GetStartRow();
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+ rcClip.right -= m_nScrollbarHeight;
+
+ int i;
+
+ for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( event )
+ {
+ event->redraw( drawHelper );
+ }
+ }
+
+ drawHelper.StartClipping( rcClip );
+
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actorW = m_SceneActors[ i ];
+ if ( !actorW )
+ continue;
+
+ actorW->redraw( drawHelper );
+ }
+
+ drawHelper.StopClipping();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ if ( m_bSuppressLayout )
+ return;
+
+ LayoutScene();
+
+ CChoreoWidgetDrawHelper drawHelper( this, COLOR_CHOREO_BACKGROUND );
+ HandleToolRedraw( drawHelper );
+
+ if ( !m_bCanDraw )
+ return;
+
+ RECT rc;
+ rc.left = 0;
+ rc.top = GetCaptionHeight();
+ rc.right = drawHelper.GetWidth();
+ rc.bottom = drawHelper.GetHeight();
+
+ RECT rcInfo;
+ rcInfo.left = rc.left;
+ rcInfo.right = rc.right - m_nScrollbarHeight;
+ rcInfo.bottom = rc.bottom - m_nScrollbarHeight;
+ rcInfo.top = rcInfo.bottom - m_nInfoHeight;
+
+ drawHelper.StartClipping( rcInfo );
+
+ RedrawStatusArea( drawHelper, rcInfo );
+
+ drawHelper.StopClipping();
+
+ RECT rcClip = rc;
+ rcClip.bottom -= ( m_nInfoHeight + m_nScrollbarHeight );
+
+ drawHelper.StartClipping( rcClip );
+
+ if ( !m_pScene )
+ {
+ char sz[ 256 ];
+ sprintf( sz, "No choreography scene file (.vcd) loaded" );
+
+ int pointsize = 18;
+ int textlen = drawHelper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz );
+
+ RECT rcText;
+ rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2;
+ rcText.bottom = rcText.top + pointsize + 10;
+ rcText.left = rc.right / 2 - textlen / 2;
+ rcText.right = rcText.left + textlen;
+
+ drawHelper.DrawColoredText( "Arial", pointsize, FW_NORMAL, COLOR_CHOREO_LIGHTTEXT, rcText, sz );
+
+ drawHelper.StopClipping();
+ return;
+ }
+
+ DrawTimeLine( drawHelper, rc, m_flStartTime, m_flEndTime );
+
+ bool clipped = false;
+ int finishx = GetPixelForTimeValue( m_pScene->FindStopTime(), &clipped );
+ if ( !clipped )
+ {
+ drawHelper.DrawColoredLine( COLOR_CHOREO_ENDTIME, PS_DOT, 1, finishx, rc.top + GetStartRow(), finishx, rc.bottom );
+ }
+
+ DrawRelativeTagLines( drawHelper, rc );
+ DrawBackground( drawHelper, rc );
+
+ DrawSceneABTicks( drawHelper );
+
+ drawHelper.StopClipping();
+
+ if ( m_UndoStack.Size() > 0 )
+ {
+ int length = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL,
+ "undo %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+ RECT rcText = rc;
+ rcText.top = rc.top + 48;
+ rcText.bottom = rcText.top + 10;
+ rcText.left = GetLabelWidth() - length - 20;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 100, 180, 100 ), rcText,
+ "undo %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+ }
+
+ DrawScrubHandle( drawHelper );
+
+ char sz[ 48 ];
+ sprintf( sz, "Speed: %.2fx", m_flPlaybackRate );
+
+ int fontsize = 9;
+
+ int length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz);
+
+ RECT rcText = rc;
+ rcText.top = rc.top + 25;
+ rcText.bottom = rcText.top + 10;
+ rcText.left = GetLabelWidth() + 20;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL,
+ RGB( 50, 50, 50 ), rcText, sz );
+
+ sprintf( sz, "Zoom: %.2fx", (float)GetTimeZoom( GetToolName() ) / 100.0f );
+
+ length = drawHelper.CalcTextWidth( "Arial", fontsize, FW_NORMAL, sz);
+
+ rcText = rc;
+ rcText.left = 5;
+ rcText.top = rc.top + 48;
+ rcText.bottom = rcText.top + 10;
+ rcText.right = rcText.left + length;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, FW_NORMAL,
+ RGB( 50, 50, 50 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : current -
+// number -
+// Output : int
+//-----------------------------------------------------------------------------
+void CChoreoView::GetUndoLevels( int& current, int& number )
+{
+ current = m_nUndoLevel;
+ number = m_UndoStack.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float frac = ( time - m_flStartTime ) / ( m_flEndTime - m_flStartTime );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = GetLabelWidth() + (int)( frac * ( w2() - GetLabelWidth() ) );
+ return pixel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ RECT rc = m_rcTimeLine;
+ rc.left = GetLabelWidth();
+
+ if ( clip )
+ {
+ if ( mx < rc.left )
+ {
+ return m_flStartTime;
+ }
+ if ( mx > rc.right )
+ {
+ return m_flEndTime;
+ }
+ }
+
+ float frac = (float)( mx - rc.left ) / (float)( rc.right - rc.left );
+
+ return m_flStartTime + frac * ( m_flEndTime - m_flStartTime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetStartTime( float time )
+{
+ m_flStartTime = time;
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetStartTime( void )
+{
+ return m_flStartTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetEndTime( void )
+{
+ return m_flEndTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)GetTimeZoom( GetToolName() ) / 100.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// origmx -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::GetTimeDeltaForMouseDelta( int mx, int origmx )
+{
+ float t1, t2;
+
+ t2 = GetTimeValueForMouse( mx );
+ t1 = GetTimeValueForMouse( origmx );
+
+ return t2 - t1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+//-----------------------------------------------------------------------------
+void CChoreoView::PlaceABPoint( int mx )
+{
+ m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].time = GetTimeValueForMouse( mx );
+ m_rgABPoints[ ( m_nCurrentABPoint) & 0x01 ].active = true;
+ m_nCurrentABPoint++;
+
+ if ( m_rgABPoints[ 0 ].active && m_rgABPoints [ 1 ].active &&
+ m_rgABPoints[ 0 ].time > m_rgABPoints[ 1 ].time )
+ {
+ float temp = m_rgABPoints[ 0 ].time;
+ m_rgABPoints[ 0 ].time = m_rgABPoints[ 1 ].time;
+ m_rgABPoints[ 1 ].time = temp;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ClearABPoints( void )
+{
+ memset( m_rgABPoints, 0, sizeof( m_rgABPoints ) );
+ m_nCurrentABPoint = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsMouseOverTimeline( int mx, int my )
+{
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ RECT rcCheck = m_rcTimeLine;
+ rcCheck.bottom -= TIMELINE_NUMBERS_HEIGHT;
+
+ if ( PtInRect( &rcCheck, pt ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::ShowContextMenu( int mx, int my )
+{
+ CChoreoActorWidget *a = NULL;
+ CChoreoChannelWidget *c = NULL;
+ CChoreoEventWidget *e = NULL;
+ CChoreoGlobalEventWidget *ge = NULL;
+ int ct = -1;
+ CEventAbsoluteTag *at = NULL;
+ int clickedCloseCaptionButton = CChoreoChannelWidget::CLOSECAPTION_NONE;
+
+ GetObjectsUnderMouse( mx, my, &a, &c, &e, &ge, &ct, &at, &clickedCloseCaptionButton );
+
+ m_pClickedActor = a;
+ m_pClickedChannel = c;
+ m_pClickedEvent = e;
+ m_pClickedGlobalEvent = ge;
+ m_nClickedX = mx;
+ m_nClickedY = my;
+ m_nClickedTag = ct;
+ m_pClickedAbsoluteTag = at;
+ m_nClickedChannelCloseCaptionButton = clickedCloseCaptionButton;
+
+ // Construct main
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( a && c )
+ {
+ if (!e)
+ {
+ pop->add( "Expression...", IDC_ADDEVENT_EXPRESSION );
+ pop->add( "WAV File...", IDC_ADDEVENT_SPEAK );
+ pop->add( "Gesture...", IDC_ADDEVENT_GESTURE );
+ pop->add( "NULL Gesture...", IDC_ADDEVENT_NULLGESTURE );
+ pop->add( "Look at actor...", IDC_ADDEVENT_LOOKAT );
+ pop->add( "Move to actor...", IDC_ADDEVENT_MOVETO );
+ pop->add( "Face actor...", IDC_ADDEVENT_FACE );
+ pop->add( "Fire Trigger...", IDC_ADDEVENT_FIRETRIGGER );
+ pop->add( "Generic(AI)...", IDC_ADDEVENT_GENERIC );
+ pop->add( "Sequence...", IDC_ADDEVENT_SEQUENCE );
+ pop->add( "Flex animation...", IDC_ADDEVENT_FLEXANIMATION );
+ pop->add( "Sub-scene...", IDC_ADDEVENT_SUBSCENE );
+ pop->add( "Interrupt...", IDC_ADDEVENT_INTERRUPT );
+ pop->add( "Permit Responses...", IDC_ADDEVENT_PERMITRESPONSES );
+
+ pop->addSeparator();
+ }
+ else
+ {
+ pop->add( va( "Edit Event '%s'...", e->GetEvent()->GetName() ), IDC_EDITEVENT );
+ switch ( e->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ pop->add( va( "Edit Event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ pop->add( va( "Edit Event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL );
+ }
+ break;
+ }
+
+ if ( e->GetEvent()->HasEndTime() )
+ {
+ pop->add( "Timing Tag...", IDC_ADDTIMINGTAG );
+ }
+
+ pop->addSeparator();
+ }
+ }
+
+ // Construct "New..."
+ mxPopupMenu *newMenu = new mxPopupMenu();
+ {
+ newMenu->add( "Actor...", IDC_ADDACTOR );
+ if ( a )
+ {
+ newMenu->add( "Channel...", IDC_ADDCHANNEL );
+ }
+ newMenu->add( "Section Pause...", IDC_ADDEVENT_PAUSE );
+ newMenu->add( "Loop...", IDC_ADDEVENT_LOOP );
+ newMenu->add( "Fire Completion...", IDC_ADDEVENT_STOPPOINT );
+ }
+ pop->addMenu( "New", newMenu );
+
+ // Now construct "Edit..."
+ if ( a || c || e || ge )
+ {
+ mxPopupMenu *editMenu = new mxPopupMenu();
+ {
+ if ( a )
+ {
+ editMenu->add( va( "Actor '%s'...", a->GetActor()->GetName() ), IDC_EDITACTOR );
+ }
+ if ( c )
+ {
+ editMenu->add( va( "Channel '%s'...", c->GetChannel()->GetName() ), IDC_EDITCHANNEL );
+ }
+ if ( ge )
+ {
+ switch ( ge->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ editMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ editMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ editMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_EDITGLOBALEVENT );
+ }
+ break;
+ }
+ }
+ }
+
+ pop->addMenu( "Edit", editMenu );
+
+ }
+
+ // Move up/down
+ if ( a || c )
+ {
+ mxPopupMenu *moveUpMenu = new mxPopupMenu();
+ mxPopupMenu *moveDownMenu = new mxPopupMenu();
+
+ if ( a )
+ {
+ moveUpMenu->add( va( "Move '%s' up", a->GetActor()->GetName() ), IDC_MOVEACTORUP );
+ moveDownMenu->add( va( "Move '%s' down", a->GetActor()->GetName() ), IDC_MOVEACTORDOWN );
+ }
+ if ( c )
+ {
+ moveUpMenu->add( va( "Move '%s' up", c->GetChannel()->GetName() ), IDC_MOVECHANNELUP );
+ moveDownMenu->add( va( "Move '%s' down", c->GetChannel()->GetName() ), IDC_MOVECHANNELDOWN );
+ }
+
+ pop->addMenu( "Move Up", moveUpMenu );
+ pop->addMenu( "Move Down", moveDownMenu );
+ }
+
+ // Delete
+ if ( a || c || e || ge || (ct != -1) )
+ {
+ mxPopupMenu *deleteMenu = new mxPopupMenu();
+ if ( a )
+ {
+ deleteMenu->add( va( "Actor '%s'", a->GetActor()->GetName() ), IDC_DELETEACTOR );
+ }
+ if ( c )
+ {
+ deleteMenu->add( va( "Channel '%s'", c->GetChannel()->GetName() ), IDC_DELETECHANNEL );
+ }
+ if ( e )
+ {
+ deleteMenu->add( va( "Event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT );
+ }
+ if ( ge )
+ {
+ switch ( ge->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ deleteMenu->add( va( "Section Pause '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ deleteMenu->add( va( "Loop Point '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ deleteMenu->add( va( "Fire Completion '%s'...", ge->GetEvent()->GetName() ), IDC_DELETEGLOBALEVENT );
+ }
+ break;
+ }
+ }
+ if ( e && ct != -1 )
+ {
+ CEventRelativeTag *tag = e->GetEvent()->GetRelativeTag( ct );
+ if ( tag )
+ {
+ deleteMenu->add( va( "Relative Tag '%s'...", tag->GetName() ), IDC_DELETERELATIVETAG );
+ }
+ }
+ pop->addMenu( "Delete", deleteMenu );
+ }
+
+ // Select
+ {
+ mxPopupMenu *selectMenu = new mxPopupMenu();
+ selectMenu->add( "Select All", IDC_SELECTALL );
+ selectMenu->add( "Deselect All", IDC_DESELECTALL );
+ selectMenu->addSeparator();
+
+ selectMenu->add( "All events before", IDC_SELECTEVENTS_ALL_BEFORE );
+ selectMenu->add( "All events after", IDC_SELECTEVENTS_ALL_AFTER );
+ selectMenu->add( "Active events before", IDC_SELECTEVENTS_ACTIVE_BEFORE );
+ selectMenu->add( "Active events after", IDC_SELECTEVENTS_ACTIVE_AFTER );
+ selectMenu->add( "Channel events before", IDC_SELECTEVENTS_CHANNEL_BEFORE );
+ selectMenu->add( "Channel events after", IDC_SELECTEVENTS_CHANNEL_AFTER );
+
+ if ( a || c )
+ {
+ selectMenu->addSeparator();
+ if ( a )
+ {
+ selectMenu->add( va( "All events in actor '%s'", a->GetActor()->GetName() ), IDC_CV_ALLEVENTS_ACTOR );
+ }
+ if ( c )
+ {
+ selectMenu->add( va( "All events in channel '%s'", c->GetChannel()->GetName() ), IDC_CV_ALLEVENTS_CHANNEL );
+ }
+ }
+ pop->addMenu( "Select/Deselect", selectMenu );
+ }
+
+ // Quick delete for events
+ if ( e )
+ {
+ pop->addSeparator();
+
+ switch ( e->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ pop->add( va( "Edit event '%s' in expression tool", e->GetEvent()->GetName() ), IDC_EXPRESSIONTOOL );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ pop->add( va( "Edit event '%s' in gesture tool", e->GetEvent()->GetName() ), IDC_GESTURETOOL );
+ }
+ break;
+ }
+
+ pop->add( va( "Move event '%s' to back", e->GetEvent()->GetName() ), IDC_MOVETOBACK );
+ if ( CountSelectedEvents() > 1 )
+ {
+ pop->add( va( "Delete events" ), IDC_DELETEEVENT );
+ pop->addSeparator();
+ pop->add( "Enable events", IDC_CV_ENABLEEVENTS );
+ pop->add( "Disable events", IDC_CV_DISABLEEVENTS );
+ }
+ else
+ {
+ pop->add( va( "Delete event '%s'", e->GetEvent()->GetName() ), IDC_DELETEEVENT );
+ pop->addSeparator();
+ if ( e->GetEvent()->GetActive() )
+ {
+ pop->add( va( "Disable event '%s'", e->GetEvent()->GetName() ), IDC_CV_DISABLEEVENTS );
+ }
+ else
+ {
+ pop->add( va( "Enable event '%s'", e->GetEvent()->GetName() ), IDC_CV_ENABLEEVENTS );
+ }
+ }
+ }
+
+ if ( m_rgABPoints[ 0 ].active && m_rgABPoints[ 1 ].active )
+ {
+ pop->addSeparator();
+ mxPopupMenu *timeMenu = new mxPopupMenu();
+ timeMenu->add( "Insert empty space between marks (shifts events right)", IDC_INSERT_TIME );
+ timeMenu->add( "Delete events between marks (shifts remaining events left)", IDC_DELETE_TIME );
+ pop->addMenu( "Time Marks", timeMenu );
+ }
+
+
+ // Copy/paste
+ if ( CanPaste() || e )
+ {
+ pop->addSeparator();
+
+ if ( CountSelectedEvents() > 1 )
+ {
+ pop->add( va( "Copy events to clipboard" ), IDC_COPYEVENTS );
+ }
+ else if ( e )
+ {
+ pop->add( va( "Copy event '%s' to clipboard", e->GetEvent()->GetName() ), IDC_COPYEVENTS );
+ }
+
+ if ( CanPaste() )
+ {
+ pop->add( va( "Paste events" ), IDC_PASTEEVENTS );
+ }
+ }
+
+ // Export / import
+ pop->addSeparator();
+
+ if ( e )
+ {
+ mxPopupMenu *exportMenu = new mxPopupMenu();
+ if ( CountSelectedEvents() > 1 )
+ {
+ exportMenu->add( va( "Export events to .vce..." ), IDC_EXPORTEVENTS );
+ }
+ else if ( e )
+ {
+ exportMenu->add( va( "Export event '%s' to .vce...", e->GetEvent()->GetName() ), IDC_EXPORTEVENTS );
+ }
+ exportMenu->add( va( "Export as .vcd..." ), IDC_EXPORT_VCD );
+ pop->addMenu( "Export", exportMenu );
+ }
+
+ mxPopupMenu *importMenu = new mxPopupMenu();
+ importMenu->add( va( "Import events from .vce..." ), IDC_IMPORTEVENTS );
+ importMenu->add( va( "Merge from .vcd..." ), IDC_IMPORT_VCD );
+ pop->addMenu( "Import", importMenu );
+
+ bool bShowAlignLeft = ( CountSelectedEvents() + CountSelectedGlobalEvents() ) > 1 ? true : false;
+
+ if ( e && ( ( CountSelectedEvents() > 1 ) || bShowAlignLeft ) )
+ {
+ pop->addSeparator();
+
+ mxPopupMenu *alignMenu = new mxPopupMenu();
+ alignMenu->add( "Align Left", IDC_CV_ALIGN_LEFT );
+ if ( CountSelectedEvents() > 1 )
+ {
+ alignMenu->add( "Align Right", IDC_CV_ALIGN_RIGHT );
+ alignMenu->add( "Size to Smallest", IDC_CV_SAMESIZE_SMALLEST );
+ alignMenu->add( "Size to Largest", IDC_CV_SAMESIZE_LARGEST );
+ }
+ pop->addMenu( "Align", alignMenu );
+ }
+
+ // Misc.
+ pop->addSeparator();
+ pop->add( va( "Change scale..." ), IDC_CV_CHANGESCALE );
+ pop->add( va( "Check sequences" ), IDC_CV_CHECKSEQLENGTHS );
+ pop->add( va( "Process sequences" ), IDC_CV_PROCESSSEQUENCES );
+ pop->add( va( m_bRampOnly ? "Ramp normal" : "Ramp only" ), IDC_CV_TOGGLERAMPONLY );
+ pop->setChecked( IDC_CV_PROCESSSEQUENCES, m_bProcessSequences );
+
+ bool onmaster= ( m_pClickedChannel &&
+ m_pClickedChannel->GetCaptionClickedEvent() &&
+ m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) ? true : false;
+ bool ondisabled = ( m_pClickedChannel &&
+ m_pClickedChannel->GetCaptionClickedEvent() &&
+ m_pClickedChannel->GetCaptionClickedEvent()->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) ? true : false;
+
+ // The close captioning menu
+ if ( m_bShowCloseCaptionData && ( AreSelectedEventsCombinable() || AreSelectedEventsInSpeakGroup() || onmaster || ondisabled ) )
+ {
+ pop->addSeparator();
+ if ( AreSelectedEventsCombinable() )
+ {
+ pop->add( "Combine Speak Events", IDC_CV_COMBINESPEAKEVENTS );
+ }
+ if ( AreSelectedEventsInSpeakGroup() )
+ {
+ pop->add( "Uncombine Speak Events", IDC_CV_REMOVESPEAKEVENTFROMGROUP );
+ }
+ if ( onmaster )
+ {
+ // Can only change tokens for "combined" files
+ if ( m_pClickedChannel->GetCaptionClickedEvent()->GetNumSlaves() >= 1 )
+ {
+ pop->add( "Change Token", IDC_CV_CHANGECLOSECAPTIONTOKEN );
+ }
+ pop->add( "Disable captions", IDC_CV_TOGGLECLOSECAPTIONS );
+ }
+ if ( ondisabled )
+ {
+ pop->add( "Enable captions", IDC_CV_TOGGLECLOSECAPTIONS );
+ }
+ }
+
+ // Undo/redo
+ if ( CanUndo() || CanRedo() )
+ {
+
+ pop->addSeparator();
+
+ if ( CanUndo() )
+ {
+ pop->add( va( "Undo %s", GetUndoDescription() ), IDC_CVUNDO );
+ }
+ if ( CanRedo() )
+ {
+ pop->add( va( "Redo %s", GetRedoDescription() ), IDC_CVREDO );
+ }
+ }
+
+ if ( m_pScene )
+ {
+ // Associate map file
+ pop->addSeparator();
+ pop->add( va( "Associate .bsp (%s)", m_pScene->GetMapname() ), IDC_ASSOCIATEBSP );
+ if ( a )
+ {
+ if ( a->GetActor() && a->GetActor()->GetFacePoserModelName()[0] )
+ {
+ pop->add( va( "Change .mdl for %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL );
+ }
+ else
+ {
+ pop->add( va( "Associate .mdl with %s", a->GetActor()->GetName() ), IDC_ASSOCIATEMODEL );
+ }
+ }
+ }
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::AssociateModel( void )
+{
+ if ( !m_pScene )
+ return;
+
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( !actor )
+ return;
+
+ CChoreoActor *a = actor->GetActor();
+ if ( !a )
+ return;
+
+ CChoiceParams params;
+ strcpy( params.m_szDialogTitle, "Associate Model" );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ strcpy( params.m_szPrompt, "Choose model:" );
+
+ params.m_Choices.RemoveAll();
+
+ params.m_nSelected = -1;
+ int oldsel = -1;
+
+ int c = models->Count();
+ ChoiceText text;
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *modelname = models->GetModelName( i );
+
+ strcpy( text.choice, modelname );
+
+ if ( !stricmp( a->GetName(), modelname ) )
+ {
+ params.m_nSelected = i;
+ oldsel = -1;
+ }
+
+ params.m_Choices.AddToTail( text );
+ }
+
+ // Add an extra entry which is "No association"
+ strcpy( text.choice, "No Associated Model" );
+ params.m_Choices.AddToTail( text );
+
+ if ( !ChoiceProperties( &params ) )
+ return;
+
+ if ( params.m_nSelected == oldsel )
+ return;
+
+ // Chose something new...
+ if ( params.m_nSelected >= 0 &&
+ params.m_nSelected < params.m_Choices.Count() )
+ {
+ AssociateModelToActor( a, params.m_nSelected );
+ }
+ else
+ {
+ // Chose "No association"
+ AssociateModelToActor( a, -1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// modelindex -
+//-----------------------------------------------------------------------------
+void CChoreoView::AssociateModelToActor( CChoreoActor *actor, int modelindex )
+{
+ Assert( actor );
+
+ SetDirty( true );
+
+ PushUndo( "Associate model" );
+
+ // Chose something new...
+ if ( modelindex >= 0 &&
+ modelindex < models->Count() )
+ {
+ actor->SetFacePoserModelName( models->GetModelFileName( modelindex ) );
+ }
+ else
+ {
+ // Chose "No Associated Model"
+ actor->SetFacePoserModelName( "" );
+ }
+
+ RecomputeWaves();
+
+ PushRedo( "Associate model" );
+}
+
+void CChoreoView::AssociateBSP( void )
+{
+ if ( !m_pScene )
+ return;
+
+ // Strip game directory and slash
+ char mapname[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( mapname, sizeof( mapname ), "maps", "*.bsp" ) )
+ {
+ return;
+ }
+
+ m_pScene->SetMapname( mapname );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawFocusRect( void )
+{
+ HDC dc = GetDC( NULL );
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ RECT rc = m_FocusRects[ i ].m_rcFocus;
+
+ ::DrawFocusRect( dc, &rc );
+ }
+
+ ReleaseDC( NULL, dc );
+}
+
+int CChoreoView::GetSelectedEventWidgets( CUtlVector< CChoreoEventWidget * >& events )
+{
+ events.RemoveAll();
+
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ {
+ events.AddToTail( event );
+ c++;
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : events -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetSelectedEvents( CUtlVector< CChoreoEvent * >& events )
+{
+ events.RemoveAll();
+
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ {
+ events.AddToTail( event->GetEvent() );
+ c++;
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+int CChoreoView::CountSelectedGlobalEvents( void )
+{
+ int c = 0;
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ ++c;
+ }
+ return c;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::CountSelectedEvents( void )
+{
+ int c = 0;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( event->IsSelected() )
+ c++;
+
+ }
+ }
+ }
+
+ return c;
+}
+
+bool CChoreoView::IsMouseOverEvent( CChoreoEventWidget *ew, int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ RECT bounds = ew->getBounds();
+ mx -= bounds.left;
+ my -= bounds.top;
+
+ if ( mx <= -tolerance )
+ {
+ return false;
+ }
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event )
+ {
+ if ( event->HasEndTime() )
+ {
+ int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w();
+
+ if ( mx > rightside + tolerance )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool CChoreoView::IsMouseOverEventEdge( CChoreoEventWidget *ew, bool bLeftEdge, int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ RECT bounds = ew->getBounds();
+ mx -= bounds.left;
+ my -= bounds.top;
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event && event->HasEndTime() )
+ {
+ if ( mx > -tolerance && mx <= tolerance )
+ {
+ return bLeftEdge;
+ }
+
+ int rightside = ew->GetDurationRightEdge() ? ew->GetDurationRightEdge() : ew->w();
+
+ if ( mx >= rightside - tolerance )
+ {
+ if ( mx > rightside + tolerance )
+ {
+ return false;
+ }
+ else
+ {
+ return !bLeftEdge;
+ }
+ }
+ }
+
+ return false;
+}
+
+int CChoreoView::GetEarliestEventIndex( CUtlVector< CChoreoEventWidget * >& events )
+{
+ int best = -1;
+ float minTime = FLT_MAX;
+
+ int c = events.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ]->GetEvent();
+ float t = e->GetStartTime();
+ if ( t < minTime )
+ {
+ minTime = t;
+ best = i;
+ }
+ }
+
+ return best;
+}
+
+int CChoreoView::GetLatestEventIndex( CUtlVector< CChoreoEventWidget * >& events )
+{
+ int best = -1;
+ float maxTime = FLT_MIN;
+
+ int c = events.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ]->GetEvent();
+ float t = e->GetEndTime();
+ if ( t > maxTime )
+ {
+ maxTime = t;
+ best = i;
+ }
+ }
+
+ return best;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeEventDragType( int mx, int my )
+{
+ int tolerance = DRAG_EVENT_EDGE_TOLERANCE;
+
+ // Iterate the events and see who's closest
+ CChoreoEventWidget *ew = GetEventUnderCursorPos( mx, my );
+ if ( !ew )
+ {
+ return DRAGTYPE_NONE;
+ }
+
+ // Deal with small windows by lowering tolerance
+ if ( ew->w() < 4 * tolerance )
+ {
+ tolerance = 2;
+ }
+
+ int tagnum = GetTagUnderCursorPos( ew, mx, my );
+ if ( tagnum != -1 && CountSelectedEvents() <= 1 )
+ {
+ return DRAGTYPE_EVENTTAG_MOVE;
+ }
+
+ CEventAbsoluteTag *tag = GetAbsoluteTagUnderCursorPos( ew, mx, my );
+ if ( tag != NULL && CountSelectedEvents() <= 1 )
+ {
+ return DRAGTYPE_EVENTABSTAG_MOVE;
+ }
+
+ if ( CountSelectedEvents() > 1 )
+ {
+ CUtlVector< CChoreoEventWidget * > events;
+ GetSelectedEventWidgets( events );
+
+ int iStart, iEnd;
+ iStart = GetEarliestEventIndex( events );
+ iEnd = GetLatestEventIndex( events );
+
+ if ( events.IsValidIndex( iStart ) )
+ {
+ // See if mouse is over left edge of starting event
+ if ( IsMouseOverEventEdge( events[ iStart ], true, mx, my ) )
+ {
+ return DRAGTYPE_RESCALELEFT;
+ }
+ }
+ if ( events.IsValidIndex( iEnd ) )
+ {
+ if ( IsMouseOverEventEdge( events[ iEnd ], false, mx, my ) )
+ {
+ return DRAGTYPE_RESCALERIGHT;
+ }
+ }
+
+ return DRAGTYPE_EVENT_MOVE;
+ }
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( event )
+ {
+ if ( event->IsFixedLength() || !event->HasEndTime() )
+ {
+ return DRAGTYPE_EVENT_MOVE;
+ }
+ }
+
+ if ( IsMouseOverEventEdge( ew, true, mx, my ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ return DRAGTYPE_EVENT_STARTTIME_RESCALE;
+ return DRAGTYPE_EVENT_STARTTIME;
+ }
+
+ if ( IsMouseOverEventEdge( ew, false, mx, my ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ return DRAGTYPE_EVENT_ENDTIME_RESCALE;
+ return DRAGTYPE_EVENT_ENDTIME;
+ }
+
+ if ( IsMouseOverEvent( ew, mx, my ) )
+ {
+ return DRAGTYPE_EVENT_MOVE;
+ }
+
+ return DRAGTYPE_NONE;
+}
+
+void CChoreoView::StartDraggingSceneEndTime( int mx, int my )
+{
+ m_nDragType = DRAGTYPE_SCENE_ENDTIME;
+
+ m_FocusRects.Purge();
+
+ RECT rcFocus;
+ rcFocus.left = mx;
+ rcFocus.top = 0;
+ rcFocus.bottom = h2();
+ rcFocus.right = rcFocus.left + 2;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ // Relative tag events don't move
+ m_FocusRects.AddToTail( fr );
+
+ m_xStart = mx;
+ m_yStart = my;
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+
+ DrawFocusRect();
+
+ m_bDragging = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::StartDraggingEvent( int mx, int my )
+{
+ m_nDragType = ComputeEventDragType( mx, my );
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ if( m_pClickedGlobalEvent )
+ {
+ m_nDragType = DRAGTYPE_EVENT_MOVE;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ m_FocusRects.Purge();
+
+ // Go through all selected events
+ RECT rcFocus;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( !event->IsSelected() )
+ continue;
+
+ if ( event == m_pClickedEvent &&
+ ( m_nClickedTag != -1 || m_pClickedAbsoluteTag ) )
+ {
+ int leftEdge = 0;
+ int tagWidth = 1;
+ if ( !m_pClickedAbsoluteTag )
+ {
+ CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( m_nClickedTag );
+ if ( tag )
+ {
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ leftEdge = (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+ }
+ }
+ }
+ else
+ {
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ leftEdge = (int)( m_pClickedAbsoluteTag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+ }
+ }
+
+ rcFocus.left = event->x() + leftEdge - tagWidth;
+ rcFocus.top = event->y() - tagWidth;
+ rcFocus.right = rcFocus.left + 2 * tagWidth;
+ rcFocus.bottom = event->y() + event->h();
+ }
+ else
+ {
+ rcFocus.left = event->x();
+ rcFocus.top = event->y();
+ if ( event->GetDurationRightEdge() )
+ {
+ rcFocus.right = event->x() + event->GetDurationRightEdge();
+ }
+ else
+ {
+ rcFocus.right = rcFocus.left + event->w();
+ }
+ rcFocus.bottom = rcFocus.top + event->h();
+ }
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ // Relative tag events don't move
+ m_FocusRects.AddToTail( fr );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ];
+ if ( !gew )
+ continue;
+
+ if ( !gew->IsSelected() )
+ continue;
+
+ rcFocus.left = gew->x() + gew->w() / 2;
+ rcFocus.top = 0;
+ rcFocus.right = rcFocus.left + 2;
+ rcFocus.bottom = h2();
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+ }
+
+ m_xStart = mx;
+ m_yStart = my;
+ m_hPrevCursor = NULL;
+ switch ( m_nDragType )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) );
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ }
+
+ DrawFocusRect();
+
+ m_bDragging = true;
+}
+
+bool CChoreoView::IsMouseOverSceneEndTime( int mx )
+{
+ // See if mouse if over scene end time instead
+ if ( m_pScene )
+ {
+ float endtime = m_pScene->FindStopTime();
+
+ bool clip = false;
+ int lastpixel = GetPixelForTimeValue( endtime, &clip );
+ if ( !clip )
+ {
+ if ( abs( mx - lastpixel ) < DRAG_EVENT_EDGE_TOLERANCE )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseStartDrag( mxEvent *event, int mx, int my )
+{
+ bool isrightbutton = event->buttons & mxEvent::MouseRightButton ? true : false;
+
+ if ( m_bDragging )
+ {
+ return;
+ }
+
+ GetObjectsUnderMouse( mx, my, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton );
+
+ if ( m_pClickedEvent )
+ {
+ CChoreoEvent *e = m_pClickedEvent->GetEvent();
+ Assert( e );
+
+ int dtPreview = ComputeEventDragType( mx, my );
+ // Shift clicking on exact edge shouldn't toggle selection state
+ bool bIsEdgeRescale = ( dtPreview == DRAGTYPE_EVENT_ENDTIME_RESCALE || dtPreview == DRAGTYPE_EVENT_STARTTIME_RESCALE );
+
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ if ( !m_pClickedEvent->IsSelected() )
+ {
+ DeselectAll();
+ }
+ TraverseWidgets( &CChoreoView::Select, m_pClickedEvent );
+ }
+ else if ( !bIsEdgeRescale )
+ {
+ m_pClickedEvent->SetSelected( !m_pClickedEvent->IsSelected() );
+ }
+
+ switch ( m_pClickedEvent->GetEvent()->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ g_pExpressionTool->SetEvent( e );
+ g_pFlexPanel->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ g_pGestureTool->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ g_pWaveBrowser->SetEvent( e );
+ }
+ break;
+ }
+
+ if ( e->HasEndTime() )
+ {
+ g_pRampTool->SetEvent( e );
+ }
+
+ redraw();
+ StartDraggingEvent( mx, my );
+ }
+ else if ( m_pClickedGlobalEvent )
+ {
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ if ( !m_pClickedGlobalEvent->IsSelected() )
+ {
+ DeselectAll();
+ }
+ TraverseWidgets( &CChoreoView::Select, m_pClickedGlobalEvent );
+ }
+ else
+ {
+ m_pClickedGlobalEvent->SetSelected( !m_pClickedGlobalEvent->IsSelected() );
+ }
+
+ redraw();
+ StartDraggingEvent( mx, my );
+ }
+ else if ( IsMouseOverScrubArea( event ) )
+ {
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_nDragType = DRAGTYPE_SCRUBBER;
+
+ m_bDragging = true;
+
+ float t = GetTimeValueForMouse( (short)event->x );
+ m_flScrubberTimeOffset = m_flScrub - t;
+ float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond();
+ m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset );
+ t += m_flScrubberTimeOffset;
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ redraw();
+
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, true );
+
+ m_FocusRects.Purge();
+
+ // Go through all selected events
+ RECT rcFocus;
+
+ rcFocus.top = GetStartRow();
+ rcFocus.bottom = h2() - m_nScrollbarHeight - m_nInfoHeight;
+ rcFocus.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcFocus.right = rcFocus.left;
+
+ POINT pt;
+ pt.x = pt.y = 0;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ OffsetRect( &rcFocus, pt.x, pt.y );
+
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+
+ m_xStart = mx;
+ m_yStart = my;
+
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+
+ DrawFocusRect();
+ }
+ else
+ {
+ float t = GetTimeValueForMouse( mx );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTargetTime( t );
+
+ // Unpause the scene
+ m_bPaused = false;
+ redraw();
+ }
+ }
+ else if ( IsMouseOverSceneEndTime( mx ) )
+ {
+ redraw();
+ StartDraggingSceneEndTime( mx, my );
+ }
+ else if ( m_pClickedChannel &&
+ m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_NONE &&
+ m_nClickedChannelCloseCaptionButton != CChoreoChannelWidget::CLOSECAPTION_CAPTION )
+ {
+ switch ( m_nClickedChannelCloseCaptionButton )
+ {
+ default:
+ case CChoreoChannelWidget::CLOSECAPTION_EXPANDCOLLAPSE:
+ {
+ OnToggleCloseCaptionTags();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_PREVLANGUAGE:
+ {
+ // Change language
+ int id = GetCloseCaptionLanguageId();
+ --id;
+ if ( id < 0 )
+ {
+ id = CC_NUM_LANGUAGES - 1;
+ Assert( id >= 0 );
+ }
+ SetCloseCaptionLanguageId( id );
+ redraw();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_NEXTLANGUAGE:
+ {
+ int id = GetCloseCaptionLanguageId();
+ ++id;
+ if ( id >= CC_NUM_LANGUAGES )
+ {
+ id = 0;
+ }
+ SetCloseCaptionLanguageId( id );
+ redraw();
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_SELECTOR:
+ {
+ SetDirty( true );
+
+ PushUndo( "Change selector" );
+
+ m_pClickedChannel->HandleSelectorClicked();
+
+ PushRedo( "Change selector" );
+
+ redraw();
+ }
+ break;
+ }
+ }
+ else
+ {
+ if ( !( event->modifiers & ( mxEvent::KeyCtrl | mxEvent::KeyShift ) ) )
+ {
+ DeselectAll();
+
+ if ( !isrightbutton )
+ {
+ if ( realtime - m_flLastMouseClickTime < 0.3f )
+ {
+ OnDoubleClicked();
+ m_flLastMouseClickTime = -1.0f;
+ }
+ else
+ {
+ m_flLastMouseClickTime = realtime;
+ }
+ }
+
+ redraw();
+ }
+ }
+
+ CalcBounds( m_nDragType );
+}
+
+void CChoreoView::OnDoubleClicked()
+{
+ if ( m_pClickedChannel )
+ {
+ switch (m_nClickedChannelCloseCaptionButton )
+ {
+ default:
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_NONE:
+ {
+ SetDirty( true );
+ PushUndo( "Enable/disable Channel" );
+
+ m_pClickedChannel->GetChannel()->SetActive( !m_pClickedChannel->GetChannel()->GetActive() );
+
+ PushRedo( "Enable/disable Channel" );
+ }
+ break;
+ case CChoreoChannelWidget::CLOSECAPTION_CAPTION:
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetNumSlaves() >= 1 )
+ {
+ OnChangeCloseCaptionToken( e );
+ }
+ }
+ break;
+ }
+
+ return;
+ }
+
+ if ( m_pClickedActor )
+ {
+ SetDirty( true );
+ PushUndo( "Enable/disable Actor" );
+
+ m_pClickedActor->GetActor()->SetActive( !m_pClickedActor->GetActor()->GetActive() );
+
+ PushRedo( "Enable/disable Actor" );
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseContinueDrag( mxEvent *event, int mx, int my )
+{
+ if ( !m_bDragging )
+ return;
+
+ DrawFocusRect();
+
+ ApplyBounds( mx, my );
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_SCRUBBER:
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+
+ ClampTimeToSelectionInterval( t );
+
+ float dt = t - m_flScrub;
+
+ SetScrubTargetTime( t );
+
+ m_bSimulating = true;
+ ScrubThink( dt, true, this );
+
+ SetScrubTime( t );
+
+ OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 );
+ }
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENTTAG_MOVE:
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ {
+ int dx = mx - m_xStart;
+ int dy = my - m_yStart;
+ if ( m_pClickedEvent )
+ {
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+
+ // Only allow jumping channels if shift is down
+ if ( !shiftdown )
+ {
+ dy = 0;
+ }
+ if ( abs( dy ) < m_pClickedEvent->GetItemHeight() )
+ {
+ dy = 0;
+ }
+ if ( m_nSelectedEvents > 1 )
+ {
+ dy = 0;
+ }
+ if ( m_nDragType == DRAGTYPE_EVENTTAG_MOVE || m_nDragType == DRAGTYPE_EVENTABSTAG_MOVE )
+ {
+ dy = 0;
+ }
+
+ if ( m_pClickedEvent->GetEvent()->IsUsingRelativeTag() )
+ {
+ dx = 0;
+ }
+ }
+ else
+ {
+ dy = 0;
+ }
+ OffsetRect( &f->m_rcFocus, dx, dy );
+ }
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ f->m_rcFocus.left += ( mx - m_xStart );
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ f->m_rcFocus.right += ( mx - m_xStart );
+ break;
+ case DRAGTYPE_SCENE_ENDTIME:
+ OffsetRect( &f->m_rcFocus, ( mx - m_xStart ), 0 );
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ //f->m_rcFocus.right += ( mx - m_xStart );
+ break;
+ }
+ }
+
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT ||
+ m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ int c = m_FocusRects.Count();
+ int m_nStart = INT_MAX;
+ int m_nEnd = INT_MIN;
+
+ for ( int i = 0; i < c; ++i )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ if ( f->m_rcFocus.left < m_nStart )
+ {
+ m_nStart = f->m_rcFocus.left;
+ }
+ if ( f->m_rcFocus.right > m_nEnd )
+ {
+ m_nEnd = f->m_rcFocus.right;
+ }
+ }
+
+ // Now figure out rescaling logic
+ int dxPixels = mx - m_xStart;
+
+ int oldSize = m_nEnd - m_nStart;
+ if ( oldSize > 0 )
+ {
+ float rescale = 1.0f;
+ if ( m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ rescale = (float)( oldSize + dxPixels )/(float)oldSize;
+ }
+ else
+ {
+ rescale = (float)( oldSize - dxPixels )/(float)oldSize;
+ }
+
+ for ( int i = 0; i < c; ++i )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ int w = f->m_rcFocus.right - f->m_rcFocus.left;
+ if ( m_nDragType == DRAGTYPE_RESCALERIGHT )
+ {
+ f->m_rcFocus.left = m_nStart + ( int )( rescale * (float)( f->m_rcFocus.left - m_nStart ) + 0.5f );
+ f->m_rcFocus.right = f->m_rcFocus.left + ( int )( rescale * (float)w + 0.5f );
+ }
+ else
+ {
+ f->m_rcFocus.right = m_nEnd - ( int )( rescale * (float)( m_nEnd - f->m_rcFocus.right ) + 0.5f );
+ f->m_rcFocus.left = f->m_rcFocus.right - ( int )( rescale * (float)w + 0.5f );
+ }
+ }
+ }
+ }
+ DrawFocusRect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseMove( int mx, int my )
+{
+ if ( m_bDragging )
+ return;
+
+ int dragtype = ComputeEventDragType( mx, my );
+ if ( dragtype == DRAGTYPE_NONE )
+ {
+ CChoreoGlobalEventWidget *ge = NULL;
+ GetObjectsUnderMouse( mx, my, NULL, NULL, NULL, &ge, NULL, NULL, NULL );
+ if ( ge )
+ {
+ dragtype = DRAGTYPE_EVENT_MOVE;
+ }
+
+ if ( dragtype == DRAGTYPE_NONE )
+ {
+ if ( IsMouseOverSceneEndTime( mx ) )
+ {
+ dragtype = DRAGTYPE_SCENE_ENDTIME;
+ }
+ }
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ switch ( dragtype )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_IBEAM ) );
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ case DRAGTYPE_SCENE_ENDTIME:
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckGestureLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ {
+ Con_Printf( "CheckGestureLength: called on non-GESTURE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return UpdateGestureLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::DefaultGestureLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ {
+ Con_Printf( "DefaultGestureLength: called on non-GESTURE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ if ( !model->GetStudioHdr() )
+ return false;
+
+ int iSequence = model->LookupSequence( e->GetParameters() );
+ if ( iSequence < 0 )
+ return false;
+
+ bool bret = false;
+
+ float seqduration = model->GetDuration( iSequence );
+ if ( seqduration != 0.0f )
+ {
+ bret = true;
+ if ( !bCheckOnly )
+ {
+ e->SetEndTime( e->GetStartTime() + seqduration );
+ }
+ }
+
+ return bret;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *e -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::AutoaddGestureKeys( CChoreoEvent *e, bool bCheckOnly )
+{
+ if ( !e )
+ return false;
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return AutoAddGestureKeys( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckSequenceLength( CChoreoEvent *e, bool bCheckOnly )
+{
+ Assert( e );
+ if ( !e )
+ return false;
+
+ if ( e->GetType() != CChoreoEvent::SEQUENCE )
+ {
+ Con_Printf( "CheckSequenceLength: called on non-SEQUENCE event %s\n", e->GetName() );
+ return false;
+ }
+
+ StudioModel *model = FindAssociatedModel( e->GetScene(), e->GetActor() );
+ if ( !model )
+ return false;
+
+ CStudioHdr *pStudioHdr = model->GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ return UpdateSequenceLength( e, pStudioHdr, model->GetPoseParameters(), bCheckOnly, true );
+}
+
+void CChoreoView::FinishDraggingSceneEndTime( mxEvent *event, int mx, int my )
+{
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ m_bDragging = false;
+
+ float mouse_dt = GetTimeDeltaForMouseDelta( mx, m_xStart );
+ if ( !mouse_dt )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ const char *desc = "Change Scene Duration";
+
+ PushUndo( desc );
+
+ float newendtime = GetTimeValueForMouse( mx );
+ float oldendtime = m_pScene->FindStopTime();
+
+ float scene_dt = newendtime - oldendtime;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ int k;
+
+ CChoreoEvent *finalGesture = NULL;
+ for ( k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ CChoreoEvent *e = event->GetEvent();
+ if ( e->GetType() != CChoreoEvent::GESTURE )
+ continue;
+
+ if ( !finalGesture )
+ {
+ finalGesture = e;
+ }
+ else
+ {
+ if ( e->GetStartTime() > finalGesture->GetStartTime() )
+ {
+ finalGesture = e;
+ }
+ }
+ }
+
+
+ for ( k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ CChoreoEvent *e = event->GetEvent();
+
+ // Event starts after new end time, kill it
+ if ( e->GetStartTime() > newendtime )
+ {
+ channel->GetChannel()->RemoveEvent( e );
+ m_pScene->DeleteReferencedObjects( e );
+ continue;
+ }
+
+ // No change to normal events that end earlier than new time (but do change gestures)
+ if ( e->GetEndTime() < newendtime &&
+ e != finalGesture )
+ {
+ continue;
+ }
+
+ float dt = scene_dt;
+ if ( e->GetType() == CChoreoEvent::GESTURE )
+ {
+ if ( e->GetEndTime() < newendtime )
+ {
+ dt = newendtime - e->GetEndTime();
+ }
+ }
+
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ }
+ }
+
+ // Remove event and move to new object
+ DeleteSceneWidgets();
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ PushRedo( desc );
+
+ CreateSceneWidgets();
+
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after association changes to reset .wav file images
+// Input : -
+//-----------------------------------------------------------------------------
+void CChoreoView::RecomputeWaves()
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ e->RecomputeWave();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::FinishDraggingEvent( mxEvent *event, int mx, int my )
+{
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ m_bDragging = false;
+
+ float dt = GetTimeDeltaForMouseDelta( mx, m_xStart );
+ if ( !dt )
+ {
+ if ( m_pScene && m_pClickedEvent && m_pClickedEvent->GetEvent()->GetType() == CChoreoEvent::SPEAK )
+ {
+ // Show phone wav in wav viewer
+ char sndname[ 512 ];
+ Q_strncpy( sndname, FacePoser_TranslateSoundName( m_pClickedEvent->GetEvent() ), sizeof( sndname ) );
+ if ( sndname[ 0 ] )
+ {
+ SetCurrentWaveFile( va( "sound/%s", sndname ), m_pClickedEvent->GetEvent() );
+ }
+ else
+ {
+ Warning( "Unable to resolve sound name for '%s', check actor associations\n", m_pClickedEvent->GetEvent()->GetName() );
+ }
+ }
+ return;
+ }
+
+ SetDirty( true );
+
+ char const *desc = "";
+
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_EVENT_MOVE:
+ desc = "Event Move";
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ desc = "Change Start Time";
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ desc = "Change End Time";
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ desc = "Move Event Tag";
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ desc = "Move Abs Event Tag";
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ desc = "Rescale Time";
+ break;
+ }
+ PushUndo( desc );
+
+ CUtlVector< CChoreoEvent * > rescaleHelper;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( !event->IsSelected() )
+ continue;
+
+ // Figure out true dt
+ CChoreoEvent *e = event->GetEvent();
+ if ( e )
+ {
+ switch ( m_nDragType )
+ {
+ default:
+ case DRAGTYPE_EVENT_MOVE:
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ {
+ float newduration = e->GetDuration() - dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime() + dt, e->GetEndTime(), m_nDragType == DRAGTYPE_EVENT_STARTTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime() + dt, e->GetEndTime() );
+ }
+ break;
+ }
+ e->OffsetStartTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, m_nDragType == DRAGTYPE_EVENT_ENDTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ break;
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ {
+ rescaleHelper.AddToTail( e );
+ }
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ {
+ // Get current x position
+ if ( m_nClickedTag != -1 )
+ {
+ CEventRelativeTag *tag = e->GetRelativeTag( m_nClickedTag );
+ if ( tag )
+ {
+ float dx = mx - m_xStart;
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ left += dx;
+
+ if ( left < bounds.left )
+ {
+ left = bounds.left;
+ }
+ else if ( left >= bounds.right )
+ {
+ left = bounds.right - 1;
+ }
+
+ // Now convert back to a percentage
+ float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left );
+
+ tag->SetPercentage( frac );
+ }
+ }
+ }
+ }
+ break;
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ {
+ // Get current x position
+ if ( m_pClickedAbsoluteTag != NULL )
+ {
+ CEventAbsoluteTag *tag = m_pClickedAbsoluteTag;
+ if ( tag )
+ {
+ float dx = mx - m_xStart;
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ if ( bounds.right - bounds.left > 0 )
+ {
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ left += dx;
+
+ if ( left < bounds.left )
+ {
+ left = bounds.left;
+ }
+ else if ( left >= bounds.right )
+ {
+ left = bounds.right - 1;
+ }
+
+ // Now convert back to a percentage
+ float frac = (float)( left - bounds.left ) / (float)( bounds.right - bounds.left );
+
+ tag->SetPercentage( frac );
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rescaleHelper.Count() > 0 )
+ {
+ int i;
+ // Determine start and end times for existing "selection"
+ float flStart = FLT_MAX;
+ float flEnd = FLT_MIN;
+ for ( i = 0; i < rescaleHelper.Count(); ++i )
+ {
+ CChoreoEvent *e = rescaleHelper[ i ];
+ float st = e->GetStartTime();
+ float ed = e->GetEndTime();
+
+ if ( st < flStart )
+ {
+ flStart = st;
+ }
+ if ( ed > flEnd )
+ {
+ flEnd = ed;
+ }
+ }
+
+ float flSelectionDuration = flEnd - flStart;
+ if ( flSelectionDuration > 0.0f )
+ {
+ float flNewDuration = 0.0f;
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT )
+ {
+ flNewDuration = max( 0.1f, flSelectionDuration - dt );
+ }
+ else
+ {
+ flNewDuration = max( 0.1f, flSelectionDuration + dt );
+ }
+ float flScale = flNewDuration / flSelectionDuration;
+
+ for ( i = 0; i < rescaleHelper.Count(); ++i )
+ {
+ CChoreoEvent *e = rescaleHelper[ i ];
+ float st = e->GetStartTime();
+ float et = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+ float flTimeFromStart = st - flStart;
+ float flTimeFromEnd = flEnd - et;
+ float flDuration = e->GetDuration();
+
+ float flNewStartTime = 0.0f;
+ float flNewDuration = 0.0f;
+
+ if ( m_nDragType == DRAGTYPE_RESCALELEFT )
+ {
+ float flNewEndTime = flEnd - flTimeFromEnd * flScale;
+ if ( !e->HasEndTime() || e->IsFixedLength() )
+ {
+ e->OffsetTime( flNewEndTime - flDuration - st );
+ continue;
+ }
+ flNewDuration = flDuration * flScale;
+ flNewStartTime = flNewEndTime - flNewDuration;
+ }
+ else
+ {
+ flNewStartTime = flTimeFromStart * flScale + flStart;
+ if ( !e->HasEndTime() || e->IsFixedLength() )
+ {
+ e->OffsetTime( flNewStartTime - st );
+ continue;
+ }
+ flNewDuration = flDuration * flScale;
+ }
+
+ RescaleRamp( e, flNewDuration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( flNewStartTime, flNewStartTime + flNewDuration, m_nDragType == DRAGTYPE_EVENT_STARTTIME || m_nDragType == DRAGTYPE_EVENT_ENDTIME );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, flNewStartTime, flNewStartTime + flNewDuration );
+ }
+ break;
+ }
+
+ e->SetStartTime( flNewStartTime );
+ Assert( e->HasEndTime() );
+ e->SetEndTime( flNewStartTime + flNewDuration );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *gew = m_SceneGlobalEvents[ i ];
+ if ( !gew || !gew->IsSelected() )
+ continue;
+
+ CChoreoEvent *e = gew->GetEvent();
+ if ( !e )
+ continue;
+
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ CChoreoEvent *e = m_pClickedEvent ? m_pClickedEvent->GetEvent() : NULL;
+
+ if ( e )
+ {
+ // See if event is moving to a new owner
+ CChoreoChannelWidget *chOrig, *chNew;
+
+ int dy = my - m_yStart;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+ if ( !shiftdown )
+ {
+ dy = 0;
+ }
+
+ if ( abs( dy ) < m_pClickedEvent->GetItemHeight() )
+ {
+ my = m_yStart;
+ }
+
+ chNew = GetChannelUnderCursorPos( mx, my );
+
+ InvalidateLayout();
+
+ mx = m_xStart;
+ my = m_yStart;
+
+ chOrig = m_pClickedChannel;
+
+ if ( chOrig && chNew && chOrig != chNew )
+ {
+ // Swap underlying objects
+ CChoreoChannel *pOrigChannel, *pNewChannel;
+
+ pOrigChannel = chOrig->GetChannel();
+ pNewChannel = chNew->GetChannel();
+
+ Assert( pOrigChannel && pNewChannel );
+
+ // Remove event and move to new object
+ DeleteSceneWidgets();
+
+ pOrigChannel->RemoveEvent( e );
+ pNewChannel->AddEvent( e );
+
+ e->SetChannel( pNewChannel );
+ e->SetActor( pNewChannel->GetActor() );
+
+ CreateSceneWidgets();
+ }
+ else
+ {
+ if ( e && e->GetType() == CChoreoEvent::SPEAK )
+ {
+ // Show phone wav in wav viewer
+ SetCurrentWaveFile( va( "sound/%s", FacePoser_TranslateSoundName( e ) ), e );
+ }
+ }
+ }
+
+ PushRedo( desc );
+ InvalidateLayout();
+
+ if ( e )
+ {
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ g_pExpressionTool->SetEvent( e );
+ g_pFlexPanel->SetEvent( e );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ g_pGestureTool->SetEvent( e );
+ }
+ break;
+ }
+
+ if ( e->HasEndTime() )
+ {
+ g_pRampTool->SetEvent( e );
+ }
+ }
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::MouseFinishDrag( mxEvent *event, int mx, int my )
+{
+ if ( !m_bDragging )
+ return;
+
+ ApplyBounds( mx, my );
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_SCRUBBER:
+ {
+ DrawFocusRect();
+
+ m_FocusRects.Purge();
+
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ m_flScrubberTimeOffset = 0.0f;
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ m_bDragging = false;
+ m_nDragType = DRAGTYPE_NONE;
+
+ redraw();
+ }
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ case DRAGTYPE_EVENTTAG_MOVE:
+ case DRAGTYPE_EVENTABSTAG_MOVE:
+ case DRAGTYPE_RESCALELEFT:
+ case DRAGTYPE_RESCALERIGHT:
+ FinishDraggingEvent( event, mx, my );
+ break;
+ case DRAGTYPE_SCENE_ENDTIME:
+ FinishDraggingSceneEndTime( event, mx, my );
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene )
+ {
+ int tz = GetTimeZoom( GetToolName() );
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+ int stepMultipiler = shiftdown ? 5 : 1;
+
+ // Zoom time in / out
+ if ( event->height > 0 )
+ {
+ tz = min( tz + TIME_ZOOM_STEP * stepMultipiler, MAX_TIME_ZOOM );
+ }
+ else
+ {
+ tz = max( tz - TIME_ZOOM_STEP * stepMultipiler, TIME_ZOOM_STEP );
+ }
+
+ SetTimeZoom( GetToolName(), tz, true );
+
+ CUtlVector< CChoreoEvent * > selected;
+ RememberSelectedEvents( selected );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ ReselectEvents( selected );
+
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) );
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::Size:
+ {
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ InvalidateLayout();
+ PositionControls();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ if ( !m_bDragging )
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) )
+ {
+ PlaceABPoint( (short)event->x );
+ redraw();
+ }
+ else if ( IsMouseOverScrubArea( event ) )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ sound->Flush();
+
+ // Unpause the scene
+ m_bPaused = false;
+
+ redraw();
+ }
+ else
+ {
+ // Show right click menu
+ ShowContextMenu( (short)event->x, (short)event->y );
+ }
+ }
+ else
+ {
+ if ( IsMouseOverTimeline( (short)event->x, (short)event->y ) )
+ {
+ ClearABPoints();
+ redraw();
+ }
+ else
+ {
+ // Handle mouse dragging here
+ MouseStartDrag( event, (short)event->x, (short)event->y );
+ }
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ {
+ MouseContinueDrag( event, (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ MouseFinishDrag( event, (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseMove:
+ {
+ MouseMove( (short)event->x, (short)event->y );
+ UpdateStatusArea( (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ iret = 1;
+
+ switch ( event->key )
+ {
+ default:
+ iret = 0;
+ break;
+ case 'E':
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ OnPlaceNextSpeakEvent();
+ }
+ break;
+ case VK_ESCAPE:
+ DeselectAll();
+ break;
+ case 'C':
+ CopyEvents();
+ iret = 1;
+ break;
+ case 'V':
+ PasteEvents();
+ redraw();
+ break;
+ case VK_DELETE:
+ {
+ if ( IsActiveTool() )
+ {
+ DeleteSelectedEvents();
+ }
+ }
+ break;
+ case VK_RETURN:
+ {
+ CUtlVector< CChoreoEvent * > events;
+ GetSelectedEvents( events );
+ if ( events.Count() == 1 )
+ {
+ if ( GetAsyncKeyState( VK_MENU ) )
+ {
+ EditEvent( events[ 0 ] );
+ redraw();
+ iret = 1;
+ }
+ }
+ }
+ break;
+ case 'Z': // Undo/Redo
+ {
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ if ( GetAsyncKeyState( VK_SHIFT ) )
+ {
+ if ( CanRedo() )
+ {
+ Con_Printf( "Redo %s\n", GetRedoDescription() );
+ Redo();
+ iret = 1;
+ }
+ }
+ else
+ {
+ if ( CanUndo() )
+ {
+ Con_Printf( "Undo %s\n", GetUndoDescription() );
+ Undo();
+ iret = 1;
+ }
+ }
+ }
+ }
+ break;
+
+ case VK_SPACE:
+ {
+ if ( IsPlayingScene() )
+ {
+ StopScene();
+ }
+ }
+ break;
+ case 188: // VK_OEM_COMMA:
+ {
+ SetScrubTargetTime( 0.0f );
+ }
+ break;
+ case 190: // VK_OEM_PERIOD:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene )
+ {
+ SetScrubTargetTime( scene->FindStopTime() );
+ }
+ }
+ break;
+ case VK_LEFT:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = m_flScrub;
+ curscrub -= ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = max( curscrub, 0.0f );
+ SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case VK_RIGHT:
+ {
+ CChoreoScene *scene = GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = m_flScrub;
+ curscrub += ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = min( curscrub, scene->FindStopTime() );
+ SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case VK_HOME:
+ {
+ MoveTimeSliderToPos( 0 );
+ }
+ break;
+ case VK_END:
+ {
+ float maxtime = m_pScene->FindStopTime() - 1.0f;
+ int pixels = (int)( maxtime * GetPixelsPerSecond() );
+ MoveTimeSliderToPos( pixels - 1 );
+ }
+ break;
+ case VK_PRIOR: // PgUp
+ {
+ int window = w2() - GetLabelWidth();
+ m_flLeftOffset = max( m_flLeftOffset - (float)window, 0.0f );
+ MoveTimeSliderToPos( (int)m_flLeftOffset );
+ }
+ break;
+ case VK_NEXT: // PgDown
+ {
+ int window = w2() - GetLabelWidth();
+ int pixels = ComputeHPixelsNeeded();
+ m_flLeftOffset = min( m_flLeftOffset + (float)window, (float)pixels );
+ MoveTimeSliderToPos( (int)m_flLeftOffset );
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ {
+ iret = 0;
+ int lang_index = event->action - IDC_CV_CC_LANGUAGESTART;
+ if ( lang_index >= 0 && lang_index < CC_NUM_LANGUAGES )
+ {
+ iret = 1;
+ SetCloseCaptionLanguageId( lang_index );
+ }
+ }
+ break;
+ case IDC_CV_TOGGLECLOSECAPTIONS:
+ {
+ OnToggleCloseCaptionsForEvent();
+ }
+ break;
+ case IDC_CV_CHANGECLOSECAPTIONTOKEN:
+ {
+ if ( m_pClickedChannel )
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetNumSlaves() >= 1 )
+ {
+ OnChangeCloseCaptionToken( e );
+ }
+ }
+ }
+ break;
+ case IDC_CV_REMOVESPEAKEVENTFROMGROUP:
+ {
+ OnRemoveSpeakEventFromGroup();
+ }
+ break;
+ case IDC_CV_COMBINESPEAKEVENTS:
+ {
+ OnCombineSpeakEvents();
+ }
+ break;
+ case IDC_CV_CC_SHOW:
+ {
+ OnToggleCloseCaptionTags();
+ }
+ break;
+ case IDC_CV_TOGGLERAMPONLY:
+ {
+ m_bRampOnly = !m_bRampOnly;
+ redraw();
+ }
+ break;
+ case IDC_CV_PROCESSSEQUENCES:
+ {
+ m_bProcessSequences = !m_bProcessSequences;
+ }
+ break;
+ case IDC_CV_CHECKSEQLENGTHS:
+ {
+ OnCheckSequenceLengths();
+ }
+ break;
+ case IDC_CV_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ case IDC_CHOREO_PLAYBACKRATE:
+ {
+ m_flPlaybackRate = m_pPlaybackRate->getValue();
+ redraw();
+ }
+ break;
+ case IDC_COPYEVENTS:
+ CopyEvents();
+ break;
+ case IDC_PASTEEVENTS:
+ PasteEvents();
+ redraw();
+ break;
+ case IDC_IMPORTEVENTS:
+ ImportEvents();
+ redraw();
+ break;
+ case IDC_EXPORTEVENTS:
+ ExportEvents();
+ redraw();
+ break;
+ case IDC_EXPORT_VCD:
+ ExportVCD();
+ redraw();
+ break;
+ case IDC_IMPORT_VCD:
+ ImportVCD();
+ redraw();
+ break;
+ case IDC_EXPRESSIONTOOL:
+ OnExpressionTool();
+ break;
+ case IDC_GESTURETOOL:
+ OnGestureTool();
+ break;
+ case IDC_ASSOCIATEBSP:
+ AssociateBSP();
+ break;
+ case IDC_ASSOCIATEMODEL:
+ AssociateModel();
+ break;
+ case IDC_CVUNDO:
+ Undo();
+ break;
+ case IDC_CVREDO:
+ Redo();
+ break;
+ case IDC_SELECTALL:
+ SelectAll();
+ break;
+ case IDC_DESELECTALL:
+ DeselectAll();
+ break;
+ case IDC_PLAYSCENE:
+ Con_Printf( "Commencing playback\n" );
+ PlayScene( true );
+ break;
+ case IDC_PAUSESCENE:
+ Con_Printf( "Pausing playback\n" );
+ PauseScene();
+ break;
+ case IDC_STOPSCENE:
+ Con_Printf( "Canceling playback\n" );
+ StopScene();
+ break;
+ case IDC_CHOREOVSCROLL:
+ {
+ int offset = 0;
+ bool processed = true;
+
+ switch ( event->modifiers )
+ {
+ case SB_THUMBTRACK:
+ offset = event->height;
+ break;
+ case SB_PAGEUP:
+ offset = m_pVertScrollBar->getValue();
+ offset -= 20;
+ offset = max( offset, m_pVertScrollBar->getMinValue() );
+ break;
+ case SB_PAGEDOWN:
+ offset = m_pVertScrollBar->getValue();
+ offset += 20;
+ offset = min( offset, m_pVertScrollBar->getMaxValue() );
+ break;
+ case SB_LINEDOWN:
+ offset = m_pVertScrollBar->getValue();
+ offset += 10;
+ offset = min( offset, m_pVertScrollBar->getMaxValue() );
+ break;
+ case SB_LINEUP:
+ offset = m_pVertScrollBar->getValue();
+ offset -= 10;
+ offset = max( offset, m_pVertScrollBar->getMinValue() );
+ break;
+ default:
+ processed = false;
+ break;
+ }
+
+ if ( processed )
+ {
+ m_pVertScrollBar->setValue( offset );
+ InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
+ m_nTopOffset = offset;
+ InvalidateLayout();
+ }
+ }
+ break;
+ case IDC_CHOREOHSCROLL:
+ {
+ int offset = 0;
+ bool processed = true;
+
+ switch ( event->modifiers )
+ {
+ case SB_THUMBTRACK:
+ offset = event->height;
+ break;
+ case SB_PAGEUP:
+ offset = m_pHorzScrollBar->getValue();
+ offset -= 20;
+ offset = max( offset, m_pHorzScrollBar->getMinValue() );
+ break;
+ case SB_PAGEDOWN:
+ offset = m_pHorzScrollBar->getValue();
+ offset += 20;
+ offset = min( offset, m_pHorzScrollBar->getMaxValue() );
+ break;
+ case SB_LINEUP:
+ offset = m_pHorzScrollBar->getValue();
+ offset -= 10;
+ offset = max( offset, m_pHorzScrollBar->getMinValue() );
+ break;
+ case SB_LINEDOWN:
+ offset = m_pHorzScrollBar->getValue();
+ offset += 10;
+ offset = min( offset, m_pHorzScrollBar->getMaxValue() );
+ break;
+ default:
+ processed = false;
+ break;
+ }
+
+ if ( processed )
+ {
+ MoveTimeSliderToPos( offset );
+ }
+ }
+ break;
+ case IDC_ADDACTOR:
+ {
+ NewActor();
+ }
+ break;
+ case IDC_EDITACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ EditActor( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_DELETEACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ DeleteActor( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_MOVEACTORUP:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ MoveActorUp( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_MOVEACTORDOWN:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ MoveActorDown( actor->GetActor() );
+ }
+ }
+ break;
+ case IDC_CHANNELOPEN:
+ {
+ CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget );
+ if ( btn )
+ {
+ CChoreoActorWidget *a = btn->GetActor();
+ if ( a )
+ {
+ a->ShowChannels( true );
+ }
+ }
+ }
+ break;
+ case IDC_CHANNELCLOSE:
+ {
+ CActorBitmapButton *btn = static_cast< CActorBitmapButton * >( event->widget );
+ if ( btn )
+ {
+ CChoreoActorWidget *a = btn->GetActor();
+ if ( a )
+ {
+ a->ShowChannels( false );
+ }
+ }
+ }
+ break;
+ case IDC_ADDEVENT_INTERRUPT:
+ {
+ AddEvent( CChoreoEvent::INTERRUPT );
+ }
+ break;
+ case IDC_ADDEVENT_PERMITRESPONSES:
+ {
+ AddEvent( CChoreoEvent::PERMIT_RESPONSES );
+ }
+ break;
+ case IDC_ADDEVENT_EXPRESSION:
+ {
+ AddEvent( CChoreoEvent::EXPRESSION );
+ }
+ break;
+ case IDC_ADDEVENT_FLEXANIMATION:
+ {
+ AddEvent( CChoreoEvent::FLEXANIMATION );
+ }
+ break;
+ case IDC_ADDEVENT_GESTURE:
+ {
+ AddEvent( CChoreoEvent::GESTURE );
+ }
+ break;
+ case IDC_ADDEVENT_NULLGESTURE:
+ {
+ AddEvent( CChoreoEvent::GESTURE, 1 );
+ }
+ break;
+ case IDC_ADDEVENT_LOOKAT:
+ {
+ AddEvent( CChoreoEvent::LOOKAT );
+ }
+ break;
+ case IDC_ADDEVENT_MOVETO:
+ {
+ AddEvent( CChoreoEvent::MOVETO );
+ }
+ break;
+ case IDC_ADDEVENT_FACE:
+ {
+ AddEvent( CChoreoEvent::FACE );
+ }
+ break;
+ case IDC_ADDEVENT_SPEAK:
+ {
+ AddEvent( CChoreoEvent::SPEAK );
+ }
+ break;
+ case IDC_ADDEVENT_FIRETRIGGER:
+ {
+ AddEvent( CChoreoEvent::FIRETRIGGER );
+ }
+ break;
+ case IDC_ADDEVENT_GENERIC:
+ {
+ AddEvent( CChoreoEvent::GENERIC );
+ }
+ break;
+ case IDC_ADDEVENT_SUBSCENE:
+ {
+ AddEvent( CChoreoEvent::SUBSCENE );
+ }
+ break;
+ case IDC_ADDEVENT_SEQUENCE:
+ {
+ AddEvent( CChoreoEvent::SEQUENCE );
+ }
+ break;
+ case IDC_EDITEVENT:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ EditEvent( event->GetEvent() );
+ redraw();
+ }
+ }
+ break;
+ case IDC_DELETEEVENT:
+ {
+ DeleteSelectedEvents();
+ }
+ break;
+ case IDC_CV_ENABLEEVENTS:
+ {
+ EnableSelectedEvents( true );
+ }
+ break;
+ case IDC_CV_DISABLEEVENTS:
+ {
+ EnableSelectedEvents( false );
+ }
+ break;
+ case IDC_MOVETOBACK:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ MoveEventToBack( event->GetEvent() );
+ }
+ }
+ break;
+ case IDC_DELETERELATIVETAG:
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event && m_nClickedTag >= 0 )
+ {
+ DeleteEventRelativeTag( event->GetEvent(), m_nClickedTag );
+ }
+ }
+ break;
+ case IDC_ADDTIMINGTAG:
+ {
+ AddEventRelativeTag();
+ }
+ break;
+ case IDC_ADDEVENT_PAUSE:
+ {
+ AddGlobalEvent( CChoreoEvent::SECTION );
+ }
+ break;
+ case IDC_ADDEVENT_LOOP:
+ {
+ AddGlobalEvent( CChoreoEvent::LOOP );
+ }
+ break;
+ case IDC_ADDEVENT_STOPPOINT:
+ {
+ AddGlobalEvent( CChoreoEvent::STOPPOINT );
+ }
+ break;
+ case IDC_EDITGLOBALEVENT:
+ {
+ CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent;
+ if ( event )
+ {
+ EditGlobalEvent( event->GetEvent() );
+ redraw();
+ }
+ }
+ break;
+ case IDC_DELETEGLOBALEVENT:
+ {
+ CChoreoGlobalEventWidget *event = m_pClickedGlobalEvent;
+ if ( event )
+ {
+ DeleteGlobalEvent( event->GetEvent() );
+ }
+ }
+ break;
+ case IDC_ADDCHANNEL:
+ {
+ NewChannel();
+ }
+ break;
+ case IDC_EDITCHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ EditChannel( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_DELETECHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ DeleteChannel( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_MOVECHANNELUP:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ MoveChannelUp( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_MOVECHANNELDOWN:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ MoveChannelDown( channel->GetChannel() );
+ }
+ }
+ break;
+ case IDC_CV_ALLEVENTS_CHANNEL:
+ {
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( channel )
+ {
+ SelectAllEventsInChannel( channel );
+ }
+ }
+ break;
+ case IDC_CV_ALLEVENTS_ACTOR:
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ SelectAllEventsInActor( actor );
+ }
+ }
+ break;
+ case IDC_SELECTEVENTS_ALL_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ALL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ALL_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ALL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ACTIVE_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ACTIVE;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_ACTIVE_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_ACTIVE;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_CHANNEL_BEFORE:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = false;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_CHANNEL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_SELECTEVENTS_CHANNEL_AFTER:
+ {
+ SelectionParams_t params;
+ Q_memset( &params, 0, sizeof( params ) );
+ params.forward = true;
+ params.time = GetTimeValueForMouse( m_nClickedX );
+ params.type = SelectionParams_t::SP_CHANNEL;
+
+ SelectEvents( params );
+ }
+ break;
+ case IDC_INSERT_TIME:
+ {
+ OnInsertTime();
+ }
+ break;
+ case IDC_DELETE_TIME:
+ {
+ OnDeleteTime();
+ }
+ break;
+ case IDC_CV_ALIGN_LEFT:
+ {
+ OnAlign( true );
+ }
+ break;
+ case IDC_CV_ALIGN_RIGHT:
+ {
+ OnAlign( false );
+ }
+ break;
+ case IDC_CV_SAMESIZE_SMALLEST:
+ {
+ OnMakeSameSize( true );
+ }
+ break;
+ case IDC_CV_SAMESIZE_LARGEST:
+ {
+ OnMakeSameSize( false );
+ }
+ break;
+ }
+
+ if ( iret == 1 )
+ {
+ SetActiveTool( this );
+ }
+ }
+ break;
+ }
+ return iret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PlayScene( bool forward )
+{
+ m_bForward = forward;
+ if ( !m_pScene )
+ return;
+
+ sound->Flush();
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ // Unpause
+ if ( m_bSimulating && m_bPaused )
+ {
+ m_bPaused = false;
+ return;
+ }
+
+ m_bSimulating = true;
+ m_bPaused = false;
+
+// float soundlatency = max( sound->GetAmountofTimeAhead(), 0.0f );
+// soundlatency = min( 0.5f, soundlatency );
+
+ float soundlatency = 0.0f;
+
+ float sceneendtime = m_pScene->FindStopTime();
+
+ m_pScene->SetSoundFileStartupLatency( soundlatency );
+
+ if ( m_rgABPoints[ 0 ].active ||
+ m_rgABPoints[ 1 ].active )
+ {
+ if ( m_rgABPoints[ 0 ].active &&
+ m_rgABPoints[ 1 ].active )
+ {
+ float st = m_rgABPoints[ 0 ].time;
+ float ed = m_rgABPoints[ 1 ].time;
+
+ m_pScene->ResetSimulation( m_bForward, st, ed );
+
+ SetScrubTime( m_bForward ? st : ed );
+ SetScrubTargetTime( m_bForward ? ed : st );
+ }
+ else
+ {
+ float startonly = m_rgABPoints[ 0 ].active ? m_rgABPoints[ 0 ].time : m_rgABPoints[ 1 ].time;
+
+ m_pScene->ResetSimulation( m_bForward, startonly );
+
+ SetScrubTime( m_bForward ? startonly : sceneendtime );
+ SetScrubTargetTime( m_bForward ? sceneendtime : startonly );
+ }
+ }
+ else
+ {
+ // NO start end/loop
+ m_pScene->ResetSimulation( m_bForward );
+
+ SetScrubTime( m_bForward ? 0 : sceneendtime );
+ SetScrubTargetTime( m_bForward ? sceneendtime : 0 );
+ }
+
+ if ( g_viewerSettings.speedScale == 0.0f )
+ {
+ m_flLastSpeedScale = g_viewerSettings.speedScale;
+ m_bResetSpeedScale = true;
+
+ g_viewerSettings.speedScale = 1.0f;
+
+ Con_Printf( "Resetting speed scale to 1.0\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = (float)x;
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PauseScene( void )
+{
+ if ( !m_bSimulating )
+ return;
+
+ m_bPaused = true;
+ sound->StopAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply expression to actor's face
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessExpression( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::EXPRESSION );
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CExpClass *p = expressions->FindClass( event->GetParameters(), true );
+ if ( !p )
+ {
+ return;
+ }
+
+ CExpression *exp = p->FindExpression( event->GetParameters2() );
+ if ( !exp )
+ {
+ return;
+ }
+
+ CChoreoActor *a = event->GetActor();
+ if ( !a )
+ return;
+
+ CChoreoActorWidget *actor = NULL;
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ if ( actor->GetActor() == a )
+ break;
+ }
+
+ if ( !actor || i >= m_SceneActors.Size() )
+ return;
+
+ float *settings = exp->GetSettings();
+ Assert( settings );
+ float *weights = exp->GetWeights();
+ Assert( weights );
+ float *current = actor->GetSettings();
+ Assert( current );
+
+ float flIntensity = event->GetIntensity( scene->GetTime() );
+
+ // blend in target values for correct actor
+ for ( LocalFlexController_t i = (LocalFlexController_t)0; i < hdr->numflexcontrollers(); i++ )
+ {
+ mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( i );
+ int j = pFlex->localToGlobal;
+ if ( j < 0 )
+ continue;
+ float s = clamp( weights[j] * flIntensity, 0.0, 1.0 );
+ current[ j ] = current[j] * (1.0f - s) + settings[ j ] * s;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *hdr -
+// *event -
+//-----------------------------------------------------------------------------
+void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event )
+{
+ Assert( hdr );
+ Assert( event );
+
+ if ( !hdr )
+ return;
+
+ if ( !event )
+ return;
+
+ // Already done
+ if ( event->GetTrackLookupSet() )
+ return;
+
+ /*
+ // FIXME: Brian hooked this stuff up for some took work, but at this point the .mdl files don't look like they've been updated to include the remapping data yet...
+ int c = hdr->numflexcontrollerremaps();
+ for ( i = 0; i < c; ++i )
+ {
+ mstudioflexcontrollerremap_t *remap = hdr->pFlexcontrollerRemap( i );
+ Msg( "remap %s\n", remap->pszName() );
+ Msg( " type %d\n", remap->remaptype );
+ Msg( " num remaps %d (stereo %s)\n", remap->numremaps, remap->stereo ? "true" : "false" );
+ for ( int j = 0 ; j < remap->numremaps; ++j )
+ {
+ int index = remap->pRemapControlIndex( j );
+ Msg( " %d: maps to %d (%s) with %s\n", j, index, hdr->pFlexcontroller( index )->pszName(), remap->pRemapControl( j ) );
+ }
+ }
+ */
+
+ // Unlink stuff in case it doesn't exist
+ int nTrackCount = event->GetNumFlexAnimationTracks();
+ for ( int i = 0; i < nTrackCount; ++i )
+ {
+ CFlexAnimationTrack *pTrack = event->GetFlexAnimationTrack( i );
+ pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 0 );
+ pTrack->SetFlexControllerIndex( LocalFlexController_t(-1), -1, 1 );
+ }
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ char const *name = hdr->pFlexcontroller( i )->pszName();
+ if ( !name )
+ continue;
+
+ bool combo = false;
+ // Look up or create all necessary tracks
+ if ( strncmp( "right_", name, 6 ) == 0 )
+ {
+ combo = true;
+ name = &name[6];
+ }
+
+ CFlexAnimationTrack *track = event->FindTrack( name );
+ if ( !track )
+ {
+ track = event->AddTrack( name );
+ Assert( track );
+ }
+
+ track->SetFlexControllerIndex( i, j, 0 );
+ if ( combo )
+ {
+ track->SetFlexControllerIndex( LocalFlexController_t(i + 1), hdr->pFlexcontroller( LocalFlexController_t(i + 1) )->localToGlobal, 1 );
+ track->SetComboType( true );
+ }
+
+ float orig_min = track->GetMin( );
+ float orig_max = track->GetMax( );
+
+ // set range
+ if (hdr->pFlexcontroller( i )->min == 0.0f || hdr->pFlexcontroller( i )->max == 1.0f)
+ {
+ track->SetInverted( false );
+ track->SetMin( hdr->pFlexcontroller( i )->min );
+ track->SetMax( hdr->pFlexcontroller( i )->max );
+ }
+ else
+ {
+ // invert ranges for wide ranged, makes sense considering flexcontroller names...
+ track->SetInverted( true );
+ track->SetMin( hdr->pFlexcontroller( i )->max );
+ track->SetMax( hdr->pFlexcontroller( i )->min );
+ }
+
+ // resample track based on this models dynamic range
+ if (track->GetNumSamples( 0 ) > 0)
+ {
+ float range = track->GetMax( ) - track->GetMin( );
+
+ for (int i = 0; i < track->GetNumSamples( 0 ); i++)
+ {
+ CExpressionSample *sample = track->GetSample( i, 0 );
+ float rangedValue = orig_min * (1 - sample->value) + orig_max * sample->value;
+ sample->value = clamp( (rangedValue - track->GetMin( )) / range, 0.0, 1.0 );
+ }
+ }
+
+ // skip next flex since we've already assigned it
+ if ( combo )
+ {
+ i++;
+ }
+ }
+
+ event->SetTrackLookupSet( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply flexanimation to actor's face
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessFlexAnimation( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::FLEXANIMATION );
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ CChoreoActor *a = event->GetActor();
+
+ CChoreoActorWidget *actor = NULL;
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ if ( !stricmp( actor->GetActor()->GetName(), a->GetName() ) )
+ break;
+ }
+
+ if ( !actor || i >= m_SceneActors.Size() )
+ return;
+
+ float *current = actor->GetSettings();
+ Assert( current );
+
+ if ( !event->GetTrackLookupSet() )
+ {
+ SetupFlexControllerTracks( hdr, event );
+ }
+
+ float weight = event->GetIntensity( scene->GetTime() );
+
+ CChoreoEventWidget *eventwidget = FindWidgetForEvent( event );
+ bool bUpdateSliders = (eventwidget && eventwidget->IsSelected() && model == models->GetActiveStudioModel() );
+
+ // Iterate animation tracks
+ for ( i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ // Disabled
+ if ( !track->IsTrackActive() )
+ {
+ if ( bUpdateSliders )
+ {
+ for ( int side = 0; side < 1 + track->IsComboType(); side++ )
+ {
+ int controller = track->GetFlexControllerIndex( side );
+ if ( controller != -1 && !g_pFlexPanel->IsEdited( controller ))
+ {
+ g_pFlexPanel->SetSlider( controller, 0.0 );
+ g_pFlexPanel->SetInfluence( controller, 0.0f );
+ }
+ }
+ }
+ continue;
+ }
+
+ // Map track flex controller to global name
+ if ( track->IsComboType() )
+ {
+ for ( int side = 0; side < 2; side++ )
+ {
+ int controller = track->GetFlexControllerIndex( side );
+ if ( controller != -1 )
+ {
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scene->GetTime(), side );
+
+ if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) )
+ {
+ g_pFlexPanel->SetSlider( controller, flIntensity );
+ g_pFlexPanel->SetInfluence( controller, 1.0f );
+ }
+
+ flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight;
+ current[ controller ] = flIntensity;
+ }
+ }
+ }
+ else
+ {
+ int controller = track->GetFlexControllerIndex( 0 );
+ if ( controller != -1 )
+ {
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scene->GetTime(), 0 );
+
+ if (bUpdateSliders && !g_pFlexPanel->IsEdited( controller ) )
+ {
+ g_pFlexPanel->SetSlider( controller, flIntensity );
+ g_pFlexPanel->SetInfluence( controller, 1.0f );
+ }
+
+ flIntensity = current[ controller ] * (1 - weight) + flIntensity * weight;
+ current[ controller ] = flIntensity;
+ }
+ }
+ }
+}
+
+
+#include "mapentities.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply lookat target
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessLookat( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::LOOKAT );
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return;
+ }
+
+ float flIntensity = event->GetIntensity( scene->GetTime() );
+
+ // clamp in-ramp to 0.3 seconds
+ float flDuration = scene->GetTime() - event->GetStartTime();
+ float flMaxIntensity = flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f;
+ flDuration = event->GetEndTime() - scene->GetTime();
+ flMaxIntensity = min( flMaxIntensity, flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f );
+ flIntensity = clamp( flIntensity, 0.0f, flMaxIntensity );
+
+ if (!stricmp( event->GetParameters(), a->GetName() ) || !stricmp( event->GetParameters(), "!self" ))
+ {
+ model->AddLookTargetSelf( flIntensity );
+ }
+ else if ( !stricmp( event->GetParameters(), "player" ) ||
+ !stricmp( event->GetParameters(), "!player" ) )
+ {
+ Vector vecTarget = model->m_origin;
+ vecTarget.z = 0;
+
+ model->AddLookTarget( vecTarget, flIntensity );
+ }
+ else
+ {
+ mapentities->CheckUpdateMap( scene->GetMapname() );
+
+ Vector orgActor;
+ Vector orgTarget;
+ QAngle anglesActor;
+ QAngle anglesDummy;
+
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ QAngle angles( -(float)event->GetPitch(),
+ (float)event->GetYaw(),
+ 0 );
+
+ matrix3x4_t matrix;
+
+ AngleMatrix( model->m_angles, matrix );
+
+ Vector vecForward;
+ AngleVectors( angles, &vecForward );
+
+ Vector eyeTarget;
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorScale( eyeTarget, 75, eyeTarget );
+
+ model->AddLookTarget( eyeTarget, flIntensity );
+ }
+ else
+ {
+ if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) )
+ {
+ if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) )
+ {
+ Vector delta = orgTarget - orgActor;
+
+ matrix3x4_t matrix;
+ Vector lookTarget;
+
+ // Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv
+ AngleMatrix( anglesActor, matrix );
+ VectorIRotate( delta, matrix, lookTarget );
+
+ model->AddLookTarget( lookTarget, flIntensity );
+ return;
+ }
+ }
+ // hack up something based on the name.
+ {
+ const char *cp = event->GetParameters();
+ float value = 0.0;
+ while (*cp)
+ {
+ value += *cp++;
+ }
+ value = cos( value );
+ value = acos( value );
+ QAngle angles( 0.0, value * 45 / M_PI, 0.0 );
+
+ matrix3x4_t matrix;
+ AngleMatrix( model->m_angles, matrix );
+
+ Vector vecForward;
+ AngleVectors( angles, &vecForward );
+
+ Vector eyeTarget;
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorScale( eyeTarget, 75, eyeTarget );
+
+ model->AddLookTarget( eyeTarget, flIntensity );
+ }
+
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a target for Faceing
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::GetTarget( CChoreoScene *scene, CChoreoEvent *event, Vector &vecTarget, QAngle &vecAngle )
+{
+ if ( !event->GetActor() )
+ return false;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return false;
+ }
+
+ if (!stricmp( event->GetParameters(), a->GetName() ))
+ {
+ vecTarget = vec3_origin;
+ return true;
+ }
+ else if ( !stricmp( event->GetParameters(), "player" ) ||
+ !stricmp( event->GetParameters(), "!player" ) )
+ {
+ vecTarget = model->m_origin;
+ vecTarget.z = 0;
+ vecAngle = model->m_angles;
+
+ return true;
+ }
+ else
+ {
+ mapentities->CheckUpdateMap( scene->GetMapname() );
+
+ Vector orgActor;
+ Vector orgTarget;
+ QAngle anglesActor;
+ QAngle anglesDummy;
+
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ QAngle angles( -(float)event->GetPitch(),
+ (float)event->GetYaw(),
+ 0 );
+
+ matrix3x4_t matrix;
+
+ AngleMatrix( model->m_angles, matrix );
+
+ QAngle angles2 = angles;
+ angles2.x *= 0.6f;
+ angles2.y *= 0.8f;
+
+ Vector vecForward, vecForward2;
+ AngleVectors( angles, &vecForward );
+ AngleVectors( angles2, &vecForward2 );
+
+ VectorNormalize( vecForward );
+ VectorNormalize( vecForward2 );
+
+ Vector eyeTarget, headTarget;
+
+ VectorRotate( vecForward, matrix, eyeTarget );
+ VectorRotate( vecForward2, matrix, headTarget );
+
+ VectorScale( eyeTarget, 150, eyeTarget );
+
+ VectorScale( headTarget, 150, vecTarget );
+ return true;
+
+ }
+ else
+ {
+ if ( mapentities->LookupOrigin( a->GetName(), orgActor, anglesActor ) )
+ {
+ if ( mapentities->LookupOrigin( event->GetParameters(), orgTarget, anglesDummy ) )
+ {
+ Vector delta = orgTarget - orgActor;
+
+ matrix3x4_t matrix;
+ Vector lookTarget;
+
+ // Rotate around actor's placed forward direction since we look straight down x in faceposer/hlmv
+ AngleMatrix( anglesActor, matrix );
+ VectorIRotate( delta, matrix, vecTarget );
+
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply lookat target
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessFace( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::FACE );
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ StudioModel *model = FindAssociatedModel( scene, a );
+ if ( !model )
+ {
+ return;
+ }
+
+ Vector vecTarget;
+ QAngle vecAngle;
+
+ if (!GetTarget( scene, event, vecTarget, vecAngle ))
+ {
+ return;
+ }
+
+ /*
+ // FIXME: this is broke
+ float goalYaw = -(vecAngle.y > 180 ? 360 - vecAngle.y : vecAngle.y );
+
+ float intensity = event->GetIntensity( scene->GetTime() );
+
+ float diff = goalYaw * intensity;
+ float dir = 1.0;
+
+ if (diff < 0)
+ {
+ diff = -diff;
+ dir = -1;
+ }
+
+ float spineintensity = 0 * max( 0.0, (intensity - 0.5) / 0.5 );
+ float goalSpineYaw = min( diff * (1.0 - spineintensity), 30 );
+ //float idealYaw = info->m_flInitialYaw + (diff - m_goalBodyYaw * dir - m_goalSpineYaw * dir) * dir;
+ // float idealYaw = UTIL_AngleMod( info->m_flInitialYaw + diff * intensity );
+
+ // FIXME: this is broke
+ // model->SetSpineYaw( goalSpineYaw * dir);
+ // model->SetBodyYaw( goalBodyYaw * dir );
+
+ // Msg("yaw %.1f : %.1f (%.1f)\n", info->m_flInitialYaw, idealYaw, intensity );
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessLoop( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::LOOP );
+
+ // Don't loop when dragging scrubber!
+ if ( IsScrubbing() )
+ return;
+
+ float backtime = (float)atof( event->GetParameters() );
+
+ bool process = true;
+ int counter = event->GetLoopCount();
+ if ( counter != -1 )
+ {
+ int remaining = event->GetNumLoopsRemaining();
+ if ( remaining <= 0 )
+ {
+ process = false;
+ }
+ else
+ {
+ event->SetNumLoopsRemaining( --remaining );
+ }
+ }
+
+ if ( !process )
+ return;
+
+ scene->LoopToTime( backtime );
+ SetScrubTime( backtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a gesture layer
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessGesture( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::GESTURE );
+
+ // NULL event is just a placeholder
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ int iSequence = model->LookupSequence( event->GetParameters() );
+ if (iSequence < 0)
+ return;
+
+ // Get spline intensity for controller
+ float eventlocaltime = scene->GetTime() - event->GetStartTime();
+
+ float referencetime = event->GetOriginalPercentageFromPlaybackPercentage( eventlocaltime / event->GetDuration() ) * event->GetDuration();
+
+ float resampledtime = event->GetStartTime() + referencetime;
+
+ float cycle = event->GetCompletion( resampledtime );
+
+ int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) );
+
+ model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply a sequence
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSequence( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SEQUENCE );
+
+ if ( !m_bProcessSequences )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ CChoreoActor *a = event->GetActor();
+
+ Assert( a );
+
+ int iSequence = model->LookupSequence( event->GetParameters() );
+ if (iSequence < 0)
+ return;
+
+ float flFrameRate;
+ float flGroundSpeed;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float cycle;
+ bool looping = model->GetSequenceLoops( iSequence );
+ if (looping)
+ {
+ float dt = scene->GetTime() - event->m_flPrevTime;
+ event->m_flPrevTime = scene->GetTime();
+ dt = clamp( dt, 0.0, 0.1 );
+ cycle = event->m_flPrevCycle + flFrameRate * dt;
+ cycle = cycle - (int)cycle;
+ event->m_flPrevCycle = cycle;
+ }
+ else
+ {
+ float dt = scene->GetTime() - event->GetStartTime();
+ cycle = flFrameRate * dt;
+ cycle = cycle - (int)(cycle);
+ }
+
+ // FIXME: shouldn't sequences always be lower priority than gestures?
+ int iLayer = model->GetNewAnimationLayer( a->FindChannelIndex( event->GetChannel() ) );
+ model->SetOverlaySequence( iLayer, iSequence, event->GetIntensity( scene->GetTime() ) );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply a walking animation
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessMoveto( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::MOVETO );
+
+ if ( !m_bProcessSequences )
+ {
+ return;
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+ if ( !model )
+ return;
+
+ if ( !event->GetActor() )
+ return;
+
+ int iSequence = GetMovetoSequence( scene, event, model );
+ if (iSequence < 0)
+ return;
+
+ float flFrameRate;
+ float flGroundSpeed;
+ model->GetSequenceInfo( iSequence, &flFrameRate, &flGroundSpeed );
+
+ float dt = scene->GetTime() - event->GetStartTime();
+ float cycle = flFrameRate * dt;
+ cycle = cycle - (int)(cycle);
+
+ float idealAccel = 100;
+
+ // accel to ideal
+ float t1 = flGroundSpeed / idealAccel;
+
+ float intensity = 1.0;
+
+ if (dt < t1)
+ {
+ intensity = dt / t1;
+ }
+ else if (event->GetDuration() - dt < t1)
+ {
+ intensity = (event->GetDuration() - dt) / t1;
+ }
+
+ // movement should always be higher priority than postures, but not gestures....grrr, any way to tell them apart?
+ int iLayer = model->GetNewAnimationLayer( 0 /* a->FindChannelIndex( event->GetChannel() ) */ );
+ model->SetOverlaySequence( iLayer, iSequence, intensity );
+ model->SetOverlayRate( iLayer, cycle, 0.0 );
+}
+
+
+
+int CChoreoView::GetMovetoSequence( CChoreoScene *scene, CChoreoEvent *event, StudioModel *model )
+{
+ // FIXME: needs to pull from event (activity or sequence?)
+ if ( !event->GetParameters2() || !event->GetParameters2()[0] )
+ return model->LookupSequence( "walk_all" );
+
+ // Custom distance styles are appended to param2 with a space as a separator
+ const char *pszAct = Q_strstr( event->GetParameters2(), " " );
+ if ( pszAct )
+ {
+ char szActName[256];
+ Q_strncpy( szActName, event->GetParameters2(), sizeof(szActName) );
+ szActName[ (pszAct-event->GetParameters2()) ] = '\0';
+ pszAct = szActName;
+ }
+ else
+ {
+ pszAct = event->GetParameters2();
+ }
+
+ if ( !Q_strcmp( pszAct, "Walk" ) )
+ {
+ pszAct = "ACT_WALK";
+ }
+ else if ( !Q_strcmp( pszAct, "Run" ) )
+ {
+ pszAct = "ACT_RUN";
+ }
+ else if ( !Q_strcmp( pszAct, "CrouchWalk" ) )
+ {
+ pszAct = "ACT_WALK_CROUCH";
+ }
+
+ int iSequence = model->LookupActivity( pszAct );
+
+ if (iSequence == -1)
+ {
+ return model->LookupSequence( "walk_all" );
+ }
+ return iSequence;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Process a pause event
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessPause( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SECTION );
+
+ // Don't pause if scrubbing
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ if ( scrubbing )
+ return;
+
+ PauseScene();
+
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ // Check for auto resume/cancel
+ ParseFromMemory( (char *)event->GetParameters(), strlen( event->GetParameters() ) );
+ if ( tokenprocessor->TokenAvailable() )
+ {
+ tokenprocessor->GetToken( false );
+ if ( !stricmp( tokenprocessor->CurrentToken(), "automate" ) )
+ {
+ if ( tokenprocessor->TokenAvailable() )
+ {
+ tokenprocessor->GetToken( false );
+ if ( !stricmp( tokenprocessor->CurrentToken(), "Cancel" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_CANCEL;
+ }
+ else if ( !stricmp( tokenprocessor->CurrentToken(), "Resume" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_RESUME;
+ }
+
+ if ( tokenprocessor->TokenAvailable() &&
+ m_nAutomatedAction != SCENE_ACTION_UNKNOWN )
+ {
+ tokenprocessor->GetToken( false );
+ m_flAutomationDelay = (float)atof( tokenprocessor->CurrentToken() );
+
+ if ( m_flAutomationDelay > 0.0f )
+ {
+ // Success
+ m_bAutomated = true;
+ m_flAutomationTime = 0.0f;
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Main event processor
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ switch( event->GetType() )
+ {
+ case CChoreoEvent::EXPRESSION:
+ ProcessExpression( scene, event );
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ ProcessFlexAnimation( scene, event );
+ break;
+ case CChoreoEvent::LOOKAT:
+ ProcessLookat( scene, event );
+ break;
+ case CChoreoEvent::FACE:
+ ProcessFace( scene, event );
+ break;
+ case CChoreoEvent::GESTURE:
+ ProcessGesture( scene, event );
+ break;
+ case CChoreoEvent::SEQUENCE:
+ ProcessSequence( scene, event );
+ break;
+ case CChoreoEvent::SUBSCENE:
+ ProcessSubscene( scene, event );
+ break;
+ case CChoreoEvent::SPEAK:
+ ProcessSpeak( scene, event );
+ break;
+ case CChoreoEvent::MOVETO:
+ ProcessMoveto( scene, event );
+ break;
+ case CChoreoEvent::STOPPOINT:
+ // Nothing
+ break;
+ case CChoreoEvent::INTERRUPT:
+ ProcessInterrupt( scene, event );
+ break;
+ case CChoreoEvent::PERMIT_RESPONSES:
+ ProcessPermitResponses( scene, event );
+ break;
+ default:
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Main event completion checker
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return true;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return true;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return true;
+ }
+
+ switch( event->GetType() )
+ {
+ case CChoreoEvent::EXPRESSION:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ break;
+ case CChoreoEvent::LOOKAT:
+ break;
+ case CChoreoEvent::GESTURE:
+ break;
+ case CChoreoEvent::SEQUENCE:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ break;
+ case CChoreoEvent::SPEAK:
+ break;
+ case CChoreoEvent::MOVETO:
+ break;
+ case CChoreoEvent::INTERRUPT:
+ break;
+ case CChoreoEvent::PERMIT_RESPONSES:
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::PauseThink( void )
+{
+ // FIXME: Game code would check for conditions being met
+
+ if ( !m_bAutomated )
+ return;
+
+ m_flAutomationTime += fabs( m_flFrameTime );
+
+ RECT rcPauseRect;
+ rcPauseRect.left = 0;
+ rcPauseRect.right = w2();
+ rcPauseRect.top = GetCaptionHeight() + SCRUBBER_HEIGHT;
+ rcPauseRect.bottom = rcPauseRect.top + 10;
+
+ CChoreoWidgetDrawHelper drawHelper( this,
+ rcPauseRect,
+ COLOR_CHOREO_BACKGROUND );
+
+ DrawSceneABTicks( drawHelper );
+
+ if ( m_flAutomationDelay > 0.0f &&
+ m_flAutomationTime < m_flAutomationDelay )
+ {
+ char sz[ 256 ];
+ sprintf( sz, "Pause %.2f/%.2f", m_flAutomationTime, m_flAutomationDelay );
+
+ int textlen = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ RECT rcText;
+ GetScrubHandleRect( rcText, true );
+
+ rcText.left = ( rcText.left + rcText.right ) / 2;
+ rcText.left -= ( textlen * 0.5f );
+ rcText.right = rcText.left + textlen + 1;
+
+ rcText.top = rcPauseRect.top;
+ rcText.bottom = rcPauseRect.bottom;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, COLOR_CHOREO_PLAYBACKTICKTEXT, rcText, sz );
+
+ return;
+ }
+
+ // Time to act
+ m_bAutomated = false;
+
+ switch ( m_nAutomatedAction )
+ {
+ case SCENE_ACTION_RESUME:
+ m_bPaused = false;
+ sound->StopAll();
+ break;
+ case SCENE_ACTION_CANCEL:
+ FinishSimulation();
+ break;
+ default:
+ break;
+ }
+
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationTime = 0.0f;
+ m_flAutomationDelay = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Conclude simulation
+//-----------------------------------------------------------------------------
+void CChoreoView::FinishSimulation( void )
+{
+ if ( !m_bSimulating )
+ return;
+
+// m_pScene->ResetSimulation();
+
+ m_bSimulating = false;
+ m_bPaused = false;
+
+ sound->StopAll();
+
+ if ( m_bResetSpeedScale )
+ {
+ m_bResetSpeedScale = false;
+ g_viewerSettings.speedScale = m_flLastSpeedScale;
+ m_flLastSpeedScale = 0.0f;
+
+ Con_Printf( "Resetting speed scale to %f\n", m_flLastSpeedScale );
+ }
+
+ models->ClearOverlaysSequences();
+
+ // redraw();
+}
+
+void CChoreoView::SceneThink( float time )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( m_bSimulating )
+ {
+ if ( m_bPaused )
+ {
+ PauseThink();
+ }
+ else
+ {
+ m_pScene->SetSoundFileStartupLatency( 0.0f );
+
+ models->CheckResetFlexes();
+
+ ResetTargetSettings();
+
+ models->ClearOverlaysSequences();
+
+ // Tell scene to go
+ m_pScene->Think( time );
+
+ // Move flexes toward their targets
+ UpdateCurrentSettings();
+ }
+ }
+ else
+ {
+ FinishSimulation();
+ }
+
+ if ( !ShouldProcessSpeak() )
+ {
+ bool autoprocess = ShouldAutoProcess();
+ bool anyscrub = IsAnyToolScrubbing() ;
+ bool anyprocessing = IsAnyToolProcessing();
+
+ //Con_Printf( "autoprocess %i anyscrub %i anyprocessing %i\n",
+ // autoprocess ? 1 : 0,
+ // anyscrub ? 1 : 0,
+ // anyprocessing ? 1 : 0 );
+
+ if ( !anyscrub &&
+ !anyprocessing &&
+ autoprocess &&
+ !m_bForceProcess )
+ {
+ sound->StopAll();
+
+ // why clear lookat?
+ //models->ClearModelTargets( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::LayoutScene( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( m_bLayoutIsValid )
+ return;
+
+ m_pScene->ReconcileTags();
+
+ RECT rc;
+ GetClientRect( (HWND)getHandle(), &rc );
+
+ RECT rcClient = rc;
+ rcClient.top += GetStartRow();
+ OffsetRect( &rcClient, 0, -m_nTopOffset );
+
+ m_flStartTime = m_flLeftOffset / GetPixelsPerSecond();
+
+ m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond();
+
+ m_rcTimeLine = rcClient;
+ m_rcTimeLine.top = GetCaptionHeight() + SCRUBBER_HEIGHT;
+ m_rcTimeLine.bottom = m_rcTimeLine.top + 44;
+
+ int currentRow = rcClient.top + 2;
+ int itemHeight;
+
+ // Draw actors
+ int i;
+ for ( i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ Assert( a );
+ if ( !a )
+ {
+ continue;
+ }
+
+ // Figure out rectangle
+ itemHeight = a->GetItemHeight();
+
+ RECT rcActor = rcClient;
+ rcActor.top = currentRow;
+ rcActor.bottom = currentRow + itemHeight;
+
+ a->Layout( rcActor );
+
+ currentRow += itemHeight;
+ }
+
+ // Draw section tabs
+ for ( i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ];
+ if ( !e )
+ continue;
+
+ RECT rcEvent;
+ rcEvent = m_rcTimeLine;
+
+ float frac = ( e->GetEvent()->GetStartTime() - m_flStartTime ) / ( m_flEndTime - m_flStartTime );
+
+ rcEvent.left = GetLabelWidth() + rcEvent.left + (int)( frac * ( m_rcTimeLine.right - m_rcTimeLine.left - GetLabelWidth() ) );
+ rcEvent.left -= 4;
+ rcEvent.right = rcEvent.left + 8;
+ rcEvent.bottom += 0;
+ rcEvent.top = rcEvent.bottom - 8;
+
+ if ( rcEvent.left + 10 < GetLabelWidth() )
+ {
+ e->setVisible( false );
+ }
+ else
+ {
+ e->setVisible( true );
+ }
+
+ // OffsetRect( &rcEvent, GetLabelWidth(), 0 );
+
+ e->Layout( rcEvent );
+ }
+
+ m_bLayoutIsValid = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteSceneWidgets( void )
+{
+ bool oldcandraw = m_bCanDraw;
+
+ m_bCanDraw = false;
+
+ int i;
+ CChoreoWidget *w;
+
+ ClearStatusArea();
+
+ for( i = 0 ; i < m_SceneActors.Size(); i++ )
+ {
+ w = m_SceneActors[ i ];
+ m_ActorExpanded[ i ].expanded = ((CChoreoActorWidget *)w)->GetShowChannels();
+ delete w;
+ }
+
+ m_SceneActors.RemoveAll();
+
+ for( i = 0 ; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ w = m_SceneGlobalEvents[ i ];
+ delete w;
+ }
+
+ m_SceneGlobalEvents.RemoveAll();
+
+ m_bCanDraw = oldcandraw;
+
+ // Make sure nobody is still pointing at us
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ if ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded )
+ {
+ RepositionVSlider();
+ }
+
+ // Recheck gesture start/end times
+ if ( m_pScene )
+ {
+ m_pScene->ReconcileGestureTimes();
+ m_pScene->ReconcileCloseCaption();
+ }
+
+ m_bLayoutIsValid = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::CreateSceneWidgets( void )
+{
+ DeleteSceneWidgets();
+
+ m_bSuppressLayout = true;
+
+ int i;
+ for ( i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ Assert( a );
+ if ( !a )
+ continue;
+
+ CChoreoActorWidget *actorWidget = new CChoreoActorWidget( NULL );
+ Assert( actorWidget );
+
+ actorWidget->SetActor( a );
+ actorWidget->Create();
+
+ m_SceneActors.AddToTail( actorWidget );
+
+ actorWidget->ShowChannels( m_ActorExpanded[ i ].expanded );
+ }
+
+ // Find global events
+ for ( i = 0; i < m_pScene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *e = m_pScene->GetEvent( i );
+ if ( !e || e->GetActor() )
+ continue;
+
+ CChoreoGlobalEventWidget *eventWidget = new CChoreoGlobalEventWidget( NULL );
+ Assert( eventWidget );
+
+ eventWidget->SetEvent( e );
+ eventWidget->Create();
+
+ m_SceneGlobalEvents.AddToTail( eventWidget );
+ }
+
+ m_bSuppressLayout = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetLabelWidth( void )
+{
+ return m_nLabelWidth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetStartRow( void )
+{
+ return m_nStartRow + GetCaptionHeight() + SCRUBBER_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetRowHeight( void )
+{
+ return m_nRowHeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetFontSize( void )
+{
+ return m_nFontSize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeVPixelsNeeded( void )
+{
+ int pixels = 0;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ pixels += actor->GetItemHeight() + 2;
+ }
+
+ pixels += GetStartRow() + 15;
+
+// pixels += m_nInfoHeight;
+
+ //pixels += 30;
+ return pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::ComputeHPixelsNeeded( void )
+{
+ if ( !m_pScene )
+ {
+ return 0;
+ }
+
+ int pixels = 0;
+ float maxtime = m_pScene->FindStopTime();
+ if ( maxtime < 5.0 )
+ {
+ maxtime = 5.0f;
+ }
+ pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() );
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::RepositionVSlider( void )
+{
+ int pixelsneeded = ComputeVPixelsNeeded();
+
+ if ( pixelsneeded <= ( h2() - GetStartRow() ))
+ {
+ m_pVertScrollBar->setVisible( false );
+ m_nTopOffset = 0;
+ }
+ else
+ {
+ m_pVertScrollBar->setVisible( true );
+ }
+
+ m_pVertScrollBar->setBounds( w2() - m_nScrollbarHeight, GetStartRow(), m_nScrollbarHeight, h2() - m_nScrollbarHeight - GetStartRow() );
+
+ //int visiblepixels = h2() - m_nScrollbarHeight - GetStartRow();
+ //m_nTopOffset = min( pixelsneeded - visiblepixels, m_nTopOffset );
+ m_nTopOffset = max( 0, m_nTopOffset );
+ m_nTopOffset = min( pixelsneeded, m_nTopOffset );
+
+ m_pVertScrollBar->setRange( 0, pixelsneeded );
+ m_pVertScrollBar->setValue( m_nTopOffset );
+ m_pVertScrollBar->setPagesize( h2() - GetStartRow() );
+
+ m_nLastVPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ int w = w2();
+ int lw = GetLabelWidth();
+
+ if ( pixelsneeded <= ( w - lw ) )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w - m_nScrollbarHeight, m_nScrollbarHeight );
+
+ m_flLeftOffset = max( 0.f, m_flLeftOffset );
+ m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset );
+
+ m_pHorzScrollBar->setRange( 0, pixelsneeded );
+ m_pHorzScrollBar->setValue( (int)m_flLeftOffset );
+ m_pHorzScrollBar->setPagesize(w - lw );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetDirty( bool dirty, bool clearundo /*=true*/ )
+{
+ bool changed = dirty != m_bDirty;
+
+ m_bDirty = dirty;
+
+ if ( !dirty && clearundo )
+ {
+ WipeUndo();
+ redraw();
+ }
+
+ if ( changed )
+ {
+ SetPrefix( m_bDirty ? "* " : "" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::New( void )
+{
+ if ( m_pScene )
+ {
+ Close( );
+ if ( m_pScene )
+ {
+ return;
+ }
+ }
+
+ char scenefile[ 512 ];
+ if ( FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ m_pScene = new CChoreoScene( this );
+ g_MDLViewer->InitGridSettings();
+ SetChoreoFile( scenefile );
+ m_pScene->SetPrintFunc( Con_Printf );
+
+ ShowButtons( true );
+
+ SetDirty( false );
+ }
+
+ if ( !m_pScene )
+ return;
+
+ // Get first actor name
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Actor" );
+ strcpy( params.m_szName, "" );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Create Actor" );
+
+ Con_Printf( "Creating scene %s with actor '%s'\n", GetChoreoFile(), params.m_szName );
+
+ CChoreoActor *actor = m_pScene->AllocActor();
+ if ( actor )
+ {
+ actor->SetName( params.m_szName );
+ }
+
+ PushRedo( "Create Actor" );
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::Save( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !MakeFileWriteablePrompt( GetChoreoFile(), "VCD File" ) )
+ {
+ Con_Printf( "Not saving changes to %s\n", GetChoreoFile() );
+ return;
+ }
+
+ Con_Printf( "Saving changes to %s\n", GetChoreoFile() );
+
+ CP4AutoEditAddFile checkout( GetChoreoFile() );
+ if ( !m_pScene->SaveToFile( GetChoreoFile() ) )
+ {
+ mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ),
+ "SaveToFile", MX_MB_OK | MX_MB_ERROR );
+ }
+
+ g_MDLViewer->OnVCDSaved();
+
+ // Refresh the suffix
+ SetChoreoFile( GetChoreoFile() );
+
+ SetDirty( false, false );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::SaveAs( void )
+{
+ if ( !m_pScene )
+ return;
+
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ return;
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ Con_Printf( "Saving %s\n", scenefile );
+
+ MakeFileWriteable( scenefile );
+
+ // Change filename
+ SetChoreoFile( scenefile );
+
+ // Write it out baby
+ CP4AutoEditAddFile checkout( scenefile );
+ if (!m_pScene->SaveToFile( GetChoreoFile() ))
+ {
+ mxMessageBox( this, va( "Unable to write \"%s\"", GetChoreoFile() ),
+ "SaveToFile", MX_MB_OK | MX_MB_ERROR );
+ }
+
+ g_MDLViewer->OnVCDSaved();
+
+ SetDirty( false, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::Load( void )
+{
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ LoadSceneFromFile( scenefile );
+
+ m_nextFileList.RemoveAll();
+}
+
+void CChoreoView::LoadNext( void )
+{
+ if (GetChoreoFile() == NULL)
+ return;
+
+ char fixedupFile[ 512 ];
+ V_FixupPathName( fixedupFile, sizeof( fixedupFile ), GetChoreoFile() );
+
+ char relativeFile[ 512 ];
+ filesystem->FullPathToRelativePath( fixedupFile, relativeFile, sizeof( relativeFile ) );
+
+ char relativePath[ 512 ];
+ Q_ExtractFilePath( relativeFile, relativePath, sizeof( relativePath ) );
+
+ if (m_nextFileList.Count() == 0)
+ {
+ // iterate files in the local directory
+ char path[ 512 ];
+ strcpy( path, relativePath );
+ strcat( path, "/*.vcd" );
+
+ FileFindHandle_t hFindFile;
+ char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile );
+ if ( fn )
+ {
+ while ( fn )
+ {
+ // Don't do anything with directories
+ if ( !filesystem->FindIsDirectory( hFindFile ) )
+ {
+ CUtlString s = fn;
+ m_nextFileList.AddToTail( s );
+ }
+
+ fn = filesystem->FindNext( hFindFile );
+ }
+
+ filesystem->FindClose( hFindFile );
+ }
+ }
+
+ // look for a match, then pick the next in the list
+ const char *fileBase;
+ fileBase = V_UnqualifiedFileName( fixedupFile );
+
+ for (int i = 0; i < m_nextFileList.Count(); i++)
+ {
+ if (!stricmp( fileBase, m_nextFileList[i] ))
+ {
+ char fileName[512];
+ strcpy( fileName, relativePath );
+ if (i < m_nextFileList.Count() - 1)
+ {
+ strcat( fileName, m_nextFileList[i+1] );
+ }
+ else
+ {
+ strcat( fileName, m_nextFileList[0] );
+ }
+
+ LoadSceneFromFile( fileName );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CChoreoView::LoadSceneFromFile( const char *filename )
+{
+ if ( filename[ 0 ] == '/' ||
+ filename[ 0 ] == '\\' )
+ {
+ ++filename;
+ }
+
+ char fn[ 512 ];
+ Q_strncpy( fn, filename, sizeof( fn ) );
+ if ( m_pScene )
+ {
+ Close();
+ if ( m_pScene )
+ {
+ return;
+ }
+ }
+
+ m_pScene = LoadScene( fn );
+ g_MDLViewer->InitGridSettings();
+ if ( !m_pScene )
+ return;
+
+ g_MDLViewer->OnFileLoaded( fn );
+
+ ShowButtons( true );
+
+ CChoreoWidget::m_pScene = m_pScene;
+ SetChoreoFile( fn );
+
+ bool cleaned = FixupSequenceDurations( m_pScene, false );
+
+ SetDirty( cleaned );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : closing -
+//-----------------------------------------------------------------------------
+void CChoreoView::UnloadScene( void )
+{
+ InvalidateLayout();
+ ReportSceneClearToTools();
+
+ ClearStatusArea();
+
+ delete m_pScene;
+ m_pScene = NULL;
+ SetDirty( false );
+ SetChoreoFile( "" );
+ g_MDLViewer->InitGridSettings();
+ CChoreoWidget::m_pScene = NULL;
+
+ DeleteSceneWidgets();
+
+ m_pVertScrollBar->setVisible( false );
+ m_pHorzScrollBar->setVisible( false );
+
+ ShowButtons( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteChannel( CChoreoChannel *channel )
+{
+ if ( !channel || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Channel" );
+
+ DeleteSceneWidgets();
+
+ // Delete channel and it's children
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ if ( a->FindChannelIndex( channel ) == -1 )
+ continue;
+
+ Con_Printf( "Deleting %s\n", channel->GetName() );
+ a->RemoveChannel( channel );
+
+ m_pScene->DeleteReferencedObjects( channel );
+ break;
+ }
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::NewChannel( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pScene->GetNumActors() )
+ {
+ Con_Printf( "You must create an actor before you can add a channel\n" );
+ return;
+ }
+
+ CChannelParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Channel" );
+ strcpy( params.m_szName, "" );
+ params.m_bShowActors = true;
+ strcpy( params.m_szSelectedActor, "" );
+ params.m_pScene = m_pScene;
+
+ if ( !ChannelProperties( &params ) )
+ {
+ return;
+ }
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ return;
+ }
+
+ CChoreoActor *actor = m_pScene->FindActor( params.m_szSelectedActor );
+ if ( !actor )
+ {
+ Con_Printf( "Can't add channel %s, actor %s doesn't exist\n", params.m_szName, params.m_szSelectedActor );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Channel" );
+
+ DeleteSceneWidgets();
+
+ CChoreoChannel *channel = m_pScene->AllocChannel();
+ if ( !channel )
+ {
+ Con_Printf( "Unable to allocate channel %s!\n", params.m_szName );
+ }
+ else
+ {
+ channel->SetName( params.m_szName );
+ channel->SetActor( actor );
+ actor->AddChannel( channel );
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Add Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveChannelUp( CChoreoChannel *channel )
+{
+ SetDirty( true );
+
+ PushUndo( "Move Channel Up" );
+
+ DeleteSceneWidgets();
+
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ int index = a->FindChannelIndex( channel );
+ if ( index == -1 )
+ continue;
+
+ if ( index != 0 )
+ {
+ Con_Printf( "Moving %s up\n", channel->GetName() );
+ a->SwapChannels( index, index - 1 );
+ }
+ break;
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Move Channel Up" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveChannelDown( CChoreoChannel *channel )
+{
+ SetDirty( true );
+
+ PushUndo( "Move Channel Down" );
+
+ DeleteSceneWidgets();
+
+ // Find the appropriate actor
+ for ( int i = 0; i < m_pScene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = m_pScene->GetActor( i );
+ if ( !a )
+ continue;
+
+ int index = a->FindChannelIndex( channel );
+ if ( index == -1 )
+ continue;
+
+ if ( index < a->GetNumChannels() - 1 )
+ {
+ Con_Printf( "Moving %s down\n", channel->GetName() );
+ a->SwapChannels( index, index + 1 );
+ }
+ break;
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Move Channel Down" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *channel -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditChannel( CChoreoChannel *channel )
+{
+ if ( !channel )
+ return;
+
+ CChannelParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Channel" );
+ V_strcpy_safe( params.m_szName, channel->GetName() );
+
+ if ( !ChannelProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Edit Channel" );
+
+ channel->SetName( params.m_szName );
+
+ PushRedo( "Edit Channel" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteActor( CChoreoActor *actor )
+{
+ if ( !actor || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Actor" );
+
+ DeleteSceneWidgets();
+
+ // Delete channel and it's children
+ // Find the appropriate actor
+ Con_Printf( "Deleting %s\n", actor->GetName() );
+ m_pScene->RemoveActor( actor );
+
+ m_pScene->DeleteReferencedObjects( actor );
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::NewActor( void )
+{
+ if ( !m_pScene )
+ {
+ Con_ErrorPrintf( "You must load or create a scene file first\n" );
+ return;
+ }
+
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Create Actor" );
+ strcpy( params.m_szName, "" );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Add Actor" );
+
+ DeleteSceneWidgets();
+
+ Con_Printf( "Adding new actor '%s'\n", params.m_szName );
+
+ CChoreoActor *actor = m_pScene->AllocActor();
+ if ( actor )
+ {
+ actor->SetName( params.m_szName );
+ }
+
+ CreateSceneWidgets();
+
+ PushRedo( "Add Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveActorUp( CChoreoActor *actor )
+{
+ DeleteSceneWidgets();
+
+ int index = m_pScene->FindActorIndex( actor );
+ // found it and it's not first
+ if ( index != -1 && index != 0 )
+ {
+ Con_Printf( "Moving %s up\n", actor->GetName() );
+
+ SetDirty( true );
+
+ PushUndo( "Move Actor Up" );
+
+ m_pScene->SwapActors( index, index - 1 );
+
+ PushRedo( "Move Actor Up" );
+ }
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveActorDown( CChoreoActor *actor )
+{
+ DeleteSceneWidgets();
+
+ int index = m_pScene->FindActorIndex( actor );
+ // found it and it's not first
+ if ( index != -1 && ( index < m_pScene->GetNumActors() - 1 ) )
+ {
+ Con_Printf( "Moving %s down\n", actor->GetName() );
+
+ SetDirty( true );
+
+ PushUndo( "Move Actor Down" );
+
+ m_pScene->SwapActors( index, index + 1 );
+
+ PushRedo( "Move Actor Down" );
+ }
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditActor( CChoreoActor *actor )
+{
+ if ( !actor )
+ return;
+
+ CActorParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Actor" );
+ V_strcpy_safe( params.m_szName, actor->GetName() );
+
+ if ( !ActorProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Edit Actor" );
+
+ actor->SetName( params.m_szName );
+
+ PushRedo( "Edit Actor" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+//-----------------------------------------------------------------------------
+void CChoreoView::AddEvent( int type, int subtype /*= 0*/, char const *defaultparameters /*= NULL*/ )
+{
+ int mx, my;
+ mx = m_nClickedX;
+ my = m_nClickedY;
+ CChoreoChannelWidget *channel = m_pClickedChannel;
+ if ( !channel || !channel->GetChannel() )
+ {
+ CChoreoActorWidget *actor = m_pClickedActor;
+ if ( actor )
+ {
+ if ( actor->GetNumChannels() <= 0 )
+ return;
+
+ channel = actor->GetChannel( 0 );
+ if ( !channel || !channel->GetChannel() )
+ return;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // Convert click position local to this window
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ CEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ if ( defaultparameters )
+ {
+ Q_strncpy( params.m_szParameters, defaultparameters, sizeof( params.m_szParameters ) );
+ }
+
+ strcpy( params.m_szDialogTitle, "Create Event" );
+
+ params.m_nType = type;
+ params.m_pScene = m_pScene;
+
+ params.m_bFixedLength = false;
+ params.m_bResumeCondition = false;
+ params.m_flStartTime = GetTimeValueForMouse( pt.x );
+ params.m_bCloseCaptionNoAttenuate = false;
+ params.m_bForceShortMovement = false;
+ params.m_bSyncToFollowingGesture = false;
+ params.m_bDisabled = false;
+ params.m_bPlayOverScript = false;
+
+ switch ( type )
+ {
+ case CChoreoEvent::EXPRESSION:
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::GESTURE:
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::MOVETO:
+ case CChoreoEvent::FACE:
+ case CChoreoEvent::SUBSCENE:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::GENERIC:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ params.m_bHasEndTime = true;
+ params.m_flEndTime = params.m_flStartTime + 0.5f;
+ if ( type == CChoreoEvent::GESTURE && subtype == 1 )
+ {
+ strcpy( params.m_szDialogTitle, "Create <NULL> Gesture" );
+ strcpy( params.m_szName, "NULL" );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ params.m_bFixedLength = true;
+ params.m_bHasEndTime = false;
+ params.m_flEndTime = -1.0f;
+ break;
+ default:
+ params.m_bHasEndTime = false;
+ params.m_flEndTime = -1.0f;
+ break;
+ }
+
+ params.m_bUsesTag = false;
+
+ while (1)
+ {
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+
+ m_bForceProcess = true;
+ if (!EventProperties( &params ))
+ {
+ m_bForceProcess = false;
+ return;
+ }
+ m_bForceProcess = false;
+
+ if ( Q_strlen( params.m_szName ) <= 0 )
+ {
+ mxMessageBox( this, va( "Event must have a valid name" ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+
+ if ( Q_strlen( params.m_szParameters ) <= 0 )
+ {
+ bool shouldBreak = false;
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ shouldBreak = true;
+ break;
+ case CChoreoEvent::GESTURE:
+ if ( subtype == 1 )
+ {
+ shouldBreak = true;
+ }
+ break;
+ default:
+ // Have to have a non-null parameters block
+ break;
+ }
+
+ if ( !shouldBreak )
+ {
+ mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Event" );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( event )
+ {
+ event->SetType( (CChoreoEvent::EVENTTYPE)type );
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szParameters );
+ event->SetParameters2( params.m_szParameters2 );
+ event->SetParameters3( params.m_szParameters3 );
+ event->SetStartTime( params.m_flStartTime );
+
+ event->SetResumeCondition( params.m_bResumeCondition );
+ event->SetLockBodyFacing( params.m_bLockBodyFacing );
+ event->SetDistanceToTarget( params.m_flDistanceToTarget );
+ event->SetForceShortMovement( params.m_bForceShortMovement );
+ event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
+ event->SetActive( !params.m_bDisabled );
+ event->SetPlayOverScript( params.m_bPlayOverScript );
+
+ if ( params.m_bUsesTag )
+ {
+ event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
+ }
+ else
+ {
+ event->SetUsingRelativeTag( false );
+ }
+ CChoreoChannel *pchannel = channel->GetChannel();
+
+ event->SetChannel( pchannel );
+ event->SetActor( pchannel->GetActor() );
+
+ if ( params.m_bHasEndTime &&
+ params.m_flEndTime != -1.0 &&
+ params.m_flEndTime > params.m_flStartTime )
+ {
+ event->SetEndTime( params.m_flEndTime );
+ }
+ else
+ {
+ event->SetEndTime( -1.0f );
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Just grab end time
+ CChoreoScene *scene = LoadScene( event->GetParameters() );
+ if ( scene )
+ {
+ event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
+ }
+ delete scene;
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( event, false );
+ // AutoaddSequenceKeys( event);
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ DefaultGestureLength( event, false );
+ AutoaddGestureKeys( event, false );
+ }
+ break;
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FACE:
+ {
+ if ( params.usepitchyaw )
+ {
+ event->SetPitch( params.pitch );
+ event->SetYaw( params.yaw );
+ }
+ else
+ {
+ event->SetPitch( 0 );
+ event->SetYaw( 0 );
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
+ }
+ break;
+ }
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ pchannel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( "Add Event" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a scene "pause" event
+//-----------------------------------------------------------------------------
+void CChoreoView::AddGlobalEvent( CChoreoEvent::EVENTTYPE type )
+{
+ int mx, my;
+ mx = m_nClickedX;
+ my = m_nClickedY;
+
+ // Convert click position local to this window
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ CGlobalEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ params.m_nType = type;
+
+ switch ( type )
+ {
+ default:
+ Assert( 0 );
+ strcpy( params.m_szDialogTitle, "???" );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ strcpy( params.m_szDialogTitle, "Add Pause Point" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ strcpy( params.m_szDialogTitle, "Add Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ strcpy( params.m_szDialogTitle, "Add Fire Completion" );
+ }
+ break;
+ }
+ strcpy( params.m_szName, "" );
+ strcpy( params.m_szAction, "" );
+
+ params.m_flStartTime = GetTimeValueForMouse( pt.x );
+
+ if ( !GlobalEventProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ Con_Printf( "Pause section event must have a valid name\n" );
+ return;
+ }
+
+ if ( strlen( params.m_szAction ) <= 0 )
+ {
+ Con_Printf( "No action specified for section pause\n" );
+ return;
+ }
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( type )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Add Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Add Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Add Fire Completion" );
+ }
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( undotext );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( event )
+ {
+ event->SetType( type );
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szAction );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetEndTime( -1.0f );
+
+ switch ( type )
+ {
+ default:
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ event->SetLoopCount( params.m_nLoopCount );
+ event->SetParameters( va( "%f", params.m_flLoopTime ) );
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ CreateSceneWidgets();
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( undotext );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditGlobalEvent( CChoreoEvent *event )
+{
+ if ( !event )
+ return;
+
+ CGlobalEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ params.m_nType = event->GetType();
+
+ switch ( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ strcpy( params.m_szDialogTitle, "???" );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Pause Point" );
+ V_strcpy_safe( params.m_szAction, event->GetParameters() );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Loop Point" );
+ strcpy( params.m_szAction, "" );
+ params.m_flLoopTime = (float)atof( event->GetParameters() );
+ params.m_nLoopCount = event->GetLoopCount();
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ strcpy( params.m_szDialogTitle, "Edit Fire Completion" );
+ strcpy( params.m_szAction, "" );
+ }
+ break;
+ }
+
+ strcpy( params.m_szName, event->GetName() );
+
+ params.m_flStartTime = event->GetStartTime();
+
+ if ( !GlobalEventProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szName ) <= 0 )
+ {
+ Con_Printf( "Event %s must have a valid name\n", event->GetName() );
+ return;
+ }
+
+ if ( strlen( params.m_szAction ) <= 0 )
+ {
+ Con_Printf( "No action specified for %s\n", event->GetName() );
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Edit Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Edit Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Edit Fire Completion" );
+ }
+ break;
+ }
+
+ PushUndo( undotext );
+
+ event->SetName( params.m_szName );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetEndTime( -1.0f );
+
+ switch ( event->GetType() )
+ {
+ default:
+ {
+ event->SetParameters( params.m_szAction );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ event->SetLoopCount( params.m_nLoopCount );
+ event->SetParameters( va( "%f", params.m_flLoopTime ) );
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ PushRedo( undotext );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteGlobalEvent( CChoreoEvent *event )
+{
+ if ( !event || !m_pScene )
+ return;
+
+ SetDirty( true );
+
+
+ char undotext[ 256 ];
+ undotext[0]=0;
+ switch( event->GetType() )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ Q_strcpy( undotext, "Delete Section Pause" );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ Q_strcpy( undotext, "Delete Loop Point" );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ Q_strcpy( undotext, "Delete Fire Completion" );
+ }
+ break;
+ }
+
+ PushUndo( undotext );
+
+ DeleteSceneWidgets();
+
+ Con_Printf( "Deleting %s\n", event->GetName() );
+
+ m_pScene->DeleteReferencedObjects( event );
+
+ CreateSceneWidgets();
+
+ PushRedo( undotext );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EditEvent( CChoreoEvent *event )
+{
+ if ( !event )
+ return;
+
+ CEventParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Event" );
+
+ // Copy in current even properties
+ params.m_nType = event->GetType();
+ params.m_bDisabled = !event->GetActive();
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::EXPRESSION:
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::MOVETO:
+ case CChoreoEvent::SPEAK:
+ case CChoreoEvent::GESTURE:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ case CChoreoEvent::GENERIC:
+ V_strcpy_safe( params.m_szParameters3, event->GetParameters3() );
+ V_strcpy_safe( params.m_szParameters2, event->GetParameters2() );
+ V_strcpy_safe( params.m_szParameters, event->GetParameters() );
+ V_strcpy_safe( params.m_szName, event->GetName() );
+ break;
+ case CChoreoEvent::FACE:
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FIRETRIGGER:
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::SUBSCENE:
+ V_strcpy_safe( params.m_szParameters, event->GetParameters() );
+ V_strcpy_safe( params.m_szName, event->GetName() );
+
+ if ( params.m_nType == CChoreoEvent::LOOKAT || params.m_nType == CChoreoEvent::FACE )
+ {
+ if ( event->GetPitch() != 0 ||
+ event->GetYaw() != 0 )
+ {
+ params.usepitchyaw = true;
+ params.pitch = event->GetPitch();
+ params.yaw = event->GetYaw();
+ }
+ }
+ break;
+ default:
+ Con_Printf( "Don't know how to edit event type %s\n",
+ CChoreoEvent::NameForType( (CChoreoEvent::EVENTTYPE)params.m_nType ) );
+ return;
+ }
+
+ params.m_pScene = m_pScene;
+ params.m_pEvent = event;
+ params.m_flStartTime = event->GetStartTime();
+ params.m_flEndTime = event->GetEndTime();
+ params.m_bHasEndTime = event->HasEndTime();
+
+ params.m_bFixedLength = event->IsFixedLength();
+ params.m_bResumeCondition = event->IsResumeCondition();
+ params.m_bLockBodyFacing = event->IsLockBodyFacing();
+ params.m_flDistanceToTarget = event->GetDistanceToTarget();
+ params.m_bForceShortMovement = event->GetForceShortMovement();
+ params.m_bSyncToFollowingGesture = event->GetSyncToFollowingGesture();
+ params.m_bPlayOverScript = event->GetPlayOverScript();
+ params.m_bUsesTag = event->IsUsingRelativeTag();
+ params.m_bCloseCaptionNoAttenuate = event->IsSuppressingCaptionAttenuation();
+
+ if ( params.m_bUsesTag )
+ {
+ V_strcpy_safe( params.m_szTagName, event->GetRelativeTagName() );
+ V_strcpy_safe( params.m_szTagWav, event->GetRelativeWavName() );
+ }
+
+ while (1)
+ {
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+
+ m_bForceProcess = true;
+ if (!EventProperties( &params ))
+ {
+ m_bForceProcess = false;
+ return;
+ }
+ m_bForceProcess = false;
+
+ if ( Q_strlen( params.m_szName ) <= 0 )
+ {
+ mxMessageBox( this, va( "Event %s must have a valid name", event->GetName() ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+
+ if ( Q_strlen( params.m_szParameters ) <= 0 )
+ {
+ bool shouldBreak = false;
+
+ switch ( params.m_nType )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::INTERRUPT:
+ case CChoreoEvent::PERMIT_RESPONSES:
+ shouldBreak = true;
+ break;
+ case CChoreoEvent::GESTURE:
+ if ( !Q_stricmp( params.m_szName, "NULL" ) )
+ {
+ shouldBreak = true;
+ }
+ break;
+ default:
+ // Have to have a non-null parameters block
+ break;
+ }
+
+ if ( !shouldBreak )
+ {
+ mxMessageBox( this, va( "No parameters specified for %s\n", params.m_szName ),
+ "Edit Event", MX_MB_OK | MX_MB_ERROR );
+ continue;
+ }
+ }
+
+ break;
+ }
+
+
+ SetDirty( true );
+
+ PushUndo( "Edit Event" );
+
+ event->SetName( params.m_szName );
+ event->SetParameters( params.m_szParameters );
+ event->SetParameters2( params.m_szParameters2 );
+ event->SetParameters3( params.m_szParameters3 );
+ event->SetStartTime( params.m_flStartTime );
+ event->SetResumeCondition( params.m_bResumeCondition );
+ event->SetLockBodyFacing( params.m_bLockBodyFacing );
+ event->SetDistanceToTarget( params.m_flDistanceToTarget );
+ event->SetForceShortMovement( params.m_bForceShortMovement );
+ event->SetSyncToFollowingGesture( params.m_bSyncToFollowingGesture );
+ event->SetActive( !params.m_bDisabled );
+ event->SetPlayOverScript( params.m_bPlayOverScript );
+ if ( params.m_bUsesTag )
+ {
+ event->SetUsingRelativeTag( true, params.m_szTagName, params.m_szTagWav );
+ }
+ else
+ {
+ event->SetUsingRelativeTag( false );
+ }
+
+ if ( params.m_bHasEndTime &&
+ params.m_flEndTime != -1.0 &&
+ params.m_flEndTime > params.m_flStartTime )
+ {
+ float dt = params.m_flEndTime - event->GetEndTime();
+ float newduration = event->GetDuration() + dt;
+ RescaleRamp( event, newduration );
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ event->RescaleGestureTimes( event->GetStartTime(), event->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( event, event->GetStartTime(), event->GetEndTime() + dt );
+ }
+ break;
+ }
+ event->SetEndTime( params.m_flEndTime );
+ event->SnapTimes();
+ event->ResortRamp();
+ }
+ else
+ {
+ event->SetEndTime( -1.0f );
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( params.m_flStartTime + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ event->SetSuppressingCaptionAttenuation( params.m_bCloseCaptionNoAttenuate );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Just grab end time
+ CChoreoScene *scene = LoadScene( event->GetParameters() );
+ if ( scene )
+ {
+ event->SetEndTime( params.m_flStartTime + scene->FindStopTime() );
+ }
+ delete scene;
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( event, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( event, false );
+ AutoaddGestureKeys( event, false );
+ g_pGestureTool->redraw();
+ }
+ break;
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::FACE:
+ {
+ if ( params.usepitchyaw )
+ {
+ event->SetPitch( params.pitch );
+ event->SetYaw( params.yaw );
+ }
+ else
+ {
+ event->SetPitch( 0 );
+ event->SetYaw( 0 );
+ }
+ }
+ break;
+ }
+
+ event->SnapTimes();
+
+ PushRedo( "Edit Event" );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+void CChoreoView::EnableSelectedEvents( bool state )
+{
+ if ( !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ // If we right clicked on an unseleced event, then select it for the user.
+ if ( CountSelectedEvents() == 0 )
+ {
+ CChoreoEventWidget *event = m_pClickedEvent;
+ if ( event )
+ {
+ event->SetSelected( true );
+ }
+ }
+
+ char const *desc = state ? "Enable Events" : "Disable Events";
+
+ PushUndo( desc );
+
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event->IsSelected() )
+ continue;
+
+ event->GetEvent()->SetActive( state );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ event->GetEvent()->SetActive( state );
+ }
+
+ PushRedo( desc );
+
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteSelectedEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Events" );
+
+ int deleteCount = 0;
+
+ float oldstoptime = m_pScene->FindStopTime();
+
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = a->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = channel->GetNumEvents() - 1; k >= 0; k-- )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event->IsSelected() )
+ continue;
+
+ channel->GetChannel()->RemoveEvent( event->GetEvent() );
+ m_pScene->DeleteReferencedObjects( event->GetEvent() );
+
+ deleteCount++;
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ m_pScene->DeleteReferencedObjects( event->GetEvent() );
+
+ deleteCount++;
+ }
+
+ DeleteSceneWidgets();
+
+ ReportSceneClearToTools();
+
+ CreateSceneWidgets();
+
+ PushRedo( "Delete Events" );
+
+ Con_Printf( "Deleted <%i> events\n", deleteCount );
+
+ if ( m_pScene->FindStopTime() != oldstoptime )
+ {
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ }
+ // Redraw
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : resetthumb -
+//-----------------------------------------------------------------------------
+void CChoreoView::ForceScrollBarsToRecompute( bool resetthumb )
+{
+ if ( resetthumb )
+ {
+ m_flLeftOffset = 0.0f;
+ }
+ m_nLastHPixelsNeeded = -1;
+ m_nLastVPixelsNeeded = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// Output : CChoreoScene
+//-----------------------------------------------------------------------------
+CChoreoScene *CChoreoView::LoadScene( char const *filename )
+{
+ // If relative path, then make a full path
+ char pFullPathBuf[ MAX_PATH ];
+ if ( !Q_IsAbsolutePath( filename ) )
+ {
+ filesystem->RelativePathToFullPath( filename, "GAME", pFullPathBuf, sizeof( pFullPathBuf ) );
+ filename = pFullPathBuf;
+ }
+
+ if ( !filesystem->FileExists( filename ) )
+ return NULL;
+
+ LoadScriptFile( const_cast<char*>( filename ) );
+
+ CChoreoScene *scene = ChoreoLoadScene( filename, this, tokenprocessor, Con_Printf );
+ return scene;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CChoreoView::FixupSequenceDurations( CChoreoScene *scene, bool checkonly )
+{
+ bool bret = false;
+ if ( !scene )
+ return bret;
+
+ int c = scene->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ float endtime = event->GetStartTime() + wave->GetRunningLength();
+ if ( event->GetEndTime() != endtime )
+ {
+ event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
+ bret = true;
+ }
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ if ( CheckSequenceLength( event, checkonly ) )
+ {
+ bret = true;
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ if ( CheckGestureLength( event, checkonly ) )
+ {
+ bret = true;
+ }
+ if ( AutoaddGestureKeys( event, checkonly ) )
+ {
+ bret = true;
+ }
+ }
+ break;
+ }
+ }
+
+ return bret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: IChoreoEventCallback
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ // Con_Printf( "%8.4f: start %s\n", currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SEQUENCE:
+ {
+ ProcessSequence( scene, event );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ {
+ subscene = LoadScene( event->GetParameters() );
+ subscene->SetSubScene( true );
+ event->SetSubScene( subscene );
+ }
+
+ if ( subscene )
+ {
+ subscene->ResetSimulation( m_bForward );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ ProcessPause( scene, event );
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ if ( ShouldProcessSpeak() )
+ {
+ // See if we should trigger CC
+ char soundname[ 512 ];
+ Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
+
+ float actualEndTime = event->GetEndTime();
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ float duration = max( event->GetDuration(), event->GetLastSlaveEndTime() - event->GetStartTime() );
+
+ closecaptionmanager->Process( tok, duration, GetCloseCaptionLanguageId() );
+
+ // Use the token as the sound name lookup, too.
+ if ( event->IsUsingCombinedFile() &&
+ ( event->GetNumSlaves() > 0 ) )
+ {
+ Q_strncpy( soundname, tok, sizeof( soundname ) );
+ actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() );
+ }
+ }
+ }
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+
+ CAudioMixer *mixer = event->GetMixer();
+ if ( !mixer || !sound->IsSoundPlaying( mixer ) )
+ {
+ CSoundParameters params;
+
+ float volume = VOL_NORM;
+ gender_t gender = GENDER_NONE;
+ if (model)
+ {
+ gender = soundemitter->GetActorGender( model->GetFileName() );
+ }
+
+ if ( !Q_stristr( soundname, ".wav" ) &&
+ soundemitter->GetParametersForSound( soundname, params, gender ) )
+ {
+ volume = params.volume;
+ }
+
+ sound->PlaySound(
+ model,
+ volume,
+ va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ),
+ &mixer );
+ event->SetMixer( mixer );
+ }
+
+ if ( mixer )
+ {
+ mixer->SetDirection( m_flFrameTime >= 0.0f );
+ float starttime, endtime;
+ starttime = event->GetStartTime();
+ endtime = actualEndTime;
+
+ float soundtime = endtime - starttime;
+ if ( soundtime > 0.0f )
+ {
+ float f = ( currenttime - starttime ) / soundtime;
+ f = clamp( f, 0.0f, 1.0f );
+
+ // Compute sample
+ float numsamples = (float)mixer->GetSource()->SampleCount();
+
+ int cursample = f * numsamples;
+ cursample = clamp( cursample, 0, numsamples - 1 );
+ mixer->SetSamplePosition( cursample );
+ mixer->SetActive( true );
+ }
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::EXPRESSION:
+ {
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ ProcessLoop( scene, event );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ // Nothing, this is a symbolic event for keeping the vcd alive for ramping out after the last true event
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !event || !event->GetActive() )
+ return;
+
+ CChoreoActor *actor = event->GetActor();
+ if ( actor && !actor->GetActive() )
+ {
+ return;
+ }
+
+ CChoreoChannel *channel = event->GetChannel();
+ if ( channel && !channel->GetActive() )
+ {
+ return;
+ }
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SUBSCENE:
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( subscene )
+ {
+ subscene->ResetSimulation();
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ CAudioMixer *mixer = event->GetMixer();
+ if ( mixer && sound->IsSoundPlaying( mixer ) )
+ {
+ sound->StopSound( mixer );
+ }
+ event->SetMixer( NULL );
+ }
+ break;
+ default:
+ break;
+ }
+
+// Con_Printf( "%8.4f: finish %s\n", currenttime, event->GetDescription() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// mx -
+// my -
+//-----------------------------------------------------------------------------
+int CChoreoView::GetTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
+{
+ if ( !event )
+ {
+ return -1;
+ }
+
+ for ( int i = 0; i < event->GetEvent()->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = event->GetEvent()->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ int tolerance = 3;
+
+ if ( abs( mx - left ) < tolerance )
+ {
+ if ( abs( my - bounds.top ) < tolerance )
+ {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// mx -
+// my -
+//-----------------------------------------------------------------------------
+CEventAbsoluteTag *CChoreoView::GetAbsoluteTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my )
+{
+ if ( !event )
+ {
+ return NULL;
+ }
+
+ for ( int i = 0; i < event->GetEvent()->GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); i++ )
+ {
+ CEventAbsoluteTag *tag = event->GetEvent()->GetAbsoluteTag( CChoreoEvent::PLAYBACK, i );
+ if ( !tag )
+ continue;
+
+ // Determine left edcge
+ RECT bounds;
+ bounds = event->getBounds();
+ int left = bounds.left + (int)( tag->GetPercentage() * (float)( bounds.right - bounds.left ) + 0.5f );
+
+ int tolerance = 3;
+
+ if ( abs( mx - left ) < tolerance )
+ {
+ if ( abs( my - bounds.top ) < tolerance )
+ {
+ return tag;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// **actor -
+// **channel -
+// **event -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetObjectsUnderMouse( int mx, int my, CChoreoActorWidget **actor,
+ CChoreoChannelWidget **channel, CChoreoEventWidget **event, CChoreoGlobalEventWidget **globalevent,
+ int *clickedTag,
+ CEventAbsoluteTag **absolutetag, int *clickedCCArea )
+{
+ if ( actor )
+ {
+ *actor = GetActorUnderCursorPos( mx, my );
+ }
+ if ( channel )
+ {
+ *channel = GetChannelUnderCursorPos( mx, my );
+ if ( *channel && clickedCCArea )
+ {
+ *clickedCCArea = (*channel)->GetChannelItemUnderMouse( mx, my );
+ }
+ }
+ if ( event )
+ {
+ *event = GetEventUnderCursorPos( mx, my );
+ }
+ if ( globalevent )
+ {
+ *globalevent = GetGlobalEventUnderCursorPos( mx, my );
+ }
+ if ( clickedTag )
+ {
+ if ( event && *event )
+ {
+ *clickedTag = GetTagUnderCursorPos( *event, mx, my );
+ }
+ else
+ {
+ *clickedTag = -1;
+ }
+ }
+ if ( absolutetag )
+ {
+ if ( event && *event )
+ {
+ *absolutetag = GetAbsoluteTagUnderCursorPos( *event, mx, my );
+ }
+ else
+ {
+ *absolutetag = NULL;
+ }
+ }
+
+
+ m_nSelectedEvents = CountSelectedEvents();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : CChoreoGlobalEventWidget
+//-----------------------------------------------------------------------------
+CChoreoGlobalEventWidget *CChoreoView::GetGlobalEventUnderCursorPos( int mx, int my )
+{
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoGlobalEventWidget *event;
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ RECT bounds;
+ event->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+CChoreoActorWidget *CChoreoView::GetActorUnderCursorPos( int mx, int my )
+{
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoActorWidget *actor;
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ RECT bounds;
+ actor->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return actor;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+// Output : CChoreoChannelWidget
+//-----------------------------------------------------------------------------
+CChoreoChannelWidget *CChoreoView::GetChannelUnderCursorPos( int mx, int my )
+{
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( mx, my );
+ if ( !actor )
+ {
+ return NULL;
+ }
+
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ CChoreoChannelWidget *channel;
+ for ( int i = 0; i < actor->GetNumChannels(); i++ )
+ {
+ channel = actor->GetChannel( i );
+ if ( !channel )
+ continue;
+
+ RECT bounds;
+ channel->getBounds( bounds );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return channel;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Caller must first translate mouse into screen coordinates
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+CChoreoEventWidget *CChoreoView::GetEventUnderCursorPos( int mx, int my )
+{
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( mx, my );
+ if ( !channel )
+ {
+ return NULL;
+ }
+
+ POINT check;
+ check.x = mx;
+ check.y = my;
+
+ if ( mx < GetLabelWidth() )
+ return NULL;
+
+ if ( my < GetStartRow() )
+ return NULL;
+
+ if ( my >= h2() - ( m_nInfoHeight + m_nScrollbarHeight ) )
+ return NULL;
+
+ CChoreoEventWidget *event;
+ for ( int i = 0; i < channel->GetNumEvents(); i++ )
+ {
+ event = channel->GetEvent( i );
+ if ( !event )
+ continue;
+
+ RECT bounds;
+ event->getBounds( bounds );
+
+ // Events get an expanded border
+ InflateRect( &bounds, 8, 4 );
+
+ if ( PtInRect( &bounds, check ) )
+ {
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Select wave file for phoneme editing
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CChoreoView::SetCurrentWaveFile( const char *filename, CChoreoEvent *event )
+{
+ g_pPhonemeEditor->SetCurrentWaveFile( filename, false, event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfn -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::TraverseWidgets( CVMEMBERFUNC pfn, CChoreoWidget *param1 )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ (this->*pfn)( actor, param1 );
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ (this->*pfn)( channel, param1 );
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ (this->*pfn)( event, param1 );
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ (this->*pfn)( event, param1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::Deselect( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ if ( widget->IsSelected() )
+ {
+ widget->SetSelected( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::Select( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ if ( widget != param1 )
+ return;
+
+ if ( widget->IsSelected() )
+ return;
+
+ widget->SetSelected( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// *param1 -
+//-----------------------------------------------------------------------------
+void CChoreoView::SelectAllEvents( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ew = dynamic_cast< CChoreoEventWidget * >( widget );
+ CChoreoGlobalEventWidget *gew = dynamic_cast< CChoreoGlobalEventWidget * >( widget );
+
+ if ( ew || gew )
+ {
+ if ( widget->IsSelected() )
+ return;
+
+ widget->SetSelected( true );
+ }
+}
+
+bool CChoreoView::CreateAnimationEvent( int mx, int my, char const *animationname )
+{
+ if ( !animationname || !animationname[0] )
+ return false;
+
+ // Convert screen to client
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ if ( pt.x < 0 || pt.y < 0 )
+ return false;
+
+ if ( pt.x > w2() || pt.y > h2() )
+ return false;
+
+ pt.x -= GetLabelWidth();
+ m_nClickedX = pt.x;
+
+ GetObjectsUnderMouse( pt.x, pt.y, &m_pClickedActor, &m_pClickedChannel, &m_pClickedEvent, &m_pClickedGlobalEvent, &m_nClickedTag, &m_pClickedAbsoluteTag, &m_nClickedChannelCloseCaptionButton );
+
+ // Find channel actor and time ( uses screen space coordinates )
+ //
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y );
+ if ( !channel )
+ {
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y );
+ if ( !actor )
+ return false;
+
+ // Grab first channel
+ if ( !actor->GetNumChannels() )
+ return false;
+
+ channel = actor->GetChannel( 0 );
+ }
+
+ if ( !channel )
+ return false;
+
+ CChoreoChannel *pchannel = channel->GetChannel();
+ if ( !pchannel )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ // At this point we need to ask the user what type of even to create (gesture or sequence) and just show the approprite dialog
+ CChoiceParams params;
+ strcpy( params.m_szDialogTitle, "Create Animation Event" );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ strcpy( params.m_szPrompt, "Type of event:" );
+
+ params.m_Choices.RemoveAll();
+
+ params.m_nSelected = 0;
+ ChoiceText text;
+ strcpy( text.choice, "gesture" );
+ params.m_Choices.AddToTail( text );
+ strcpy( text.choice, "sequence" );
+ params.m_Choices.AddToTail( text );
+
+ if ( !ChoiceProperties( &params ) )
+ return false;
+
+ if ( params.m_nSelected < 0 )
+ return false;
+
+ switch ( params.m_nSelected )
+ {
+ default:
+ case 0:
+ AddEvent( CChoreoEvent::GESTURE, 0, animationname );
+ break;
+ case 1:
+ AddEvent( CChoreoEvent::SEQUENCE, 0, animationname );
+ break;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// *cl -
+// *exp -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CreateExpressionEvent( int mx, int my, CExpClass *cl, CExpression *exp )
+{
+ if ( !m_pScene )
+ return false;
+
+ if ( !exp )
+ return false;
+
+ // Convert screen to client
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ if ( pt.x < 0 || pt.y < 0 )
+ return false;
+
+ if ( pt.x > w2() || pt.y > h2() )
+ return false;
+
+ // Find channel actor and time ( uses screen space coordinates )
+ //
+ CChoreoChannelWidget *channel = GetChannelUnderCursorPos( pt.x, pt.y );
+ if ( !channel )
+ {
+ CChoreoActorWidget *actor = GetActorUnderCursorPos( pt.x, pt.y );
+ if ( !actor )
+ return false;
+
+ // Grab first channel
+ if ( !actor->GetNumChannels() )
+ return false;
+
+ channel = actor->GetChannel( 0 );
+ }
+
+ if ( !channel )
+ return false;
+
+ CChoreoChannel *pchannel = channel->GetChannel();
+ if ( !pchannel )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ if ( !event )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ float starttime = GetTimeValueForMouse( pt.x, false );
+
+ SetDirty( true );
+
+ PushUndo( "Create Expression" );
+
+ event->SetType( CChoreoEvent::EXPRESSION );
+ event->SetName( exp->name );
+ event->SetParameters( cl->GetName() );
+ event->SetParameters2( exp->name );
+ event->SetStartTime( starttime );
+ event->SetChannel( pchannel );
+ event->SetActor( pchannel->GetActor() );
+ event->SetEndTime( starttime + 1.0f );
+
+ event->SnapTimes();
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ pchannel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ PushRedo( "Create Expression" );
+
+ // Redraw
+ InvalidateLayout();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsPlayingScene( void )
+{
+ return m_bSimulating;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ResetTargetSettings( void )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *w = m_SceneActors[ i ];
+ if ( w )
+ {
+ w->ResetSettings();
+ }
+ }
+
+ models->ClearModelTargets( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: copies the actors "settings" into the models FlexControllers
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CChoreoView::UpdateCurrentSettings( void )
+{
+ StudioModel *defaultModel = models->GetActiveStudioModel();
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *w = m_SceneActors[ i ];
+ if ( !w )
+ continue;
+
+ if ( !w->GetActor()->GetActive() )
+ continue;
+
+ StudioModel *model = FindAssociatedModel( m_pScene, w->GetActor() );
+ if ( !model )
+ continue;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ continue;
+
+ float *current = w->GetSettings();
+
+ for ( LocalFlexController_t j = LocalFlexController_t(0); j < hdr->numflexcontrollers(); j++ )
+ {
+ int k = hdr->pFlexcontroller( j )->localToGlobal;
+
+ if (k != -1)
+ {
+ if ( defaultModel == model && g_pFlexPanel->IsEdited( k ) )
+ {
+ model->SetFlexController( j, g_pFlexPanel->GetSlider( k ) );
+ }
+ else
+ {
+ model->SetFlexController( j, current[ k ] );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// tagnum -
+//-----------------------------------------------------------------------------
+void CChoreoView::DeleteEventRelativeTag( CChoreoEvent *event, int tagnum )
+{
+ if ( !event )
+ return;
+
+ CEventRelativeTag *tag = event->GetRelativeTag( tagnum );
+ if ( !tag )
+ return;
+
+ SetDirty( true );
+
+ PushUndo( "Delete Event Tag" );
+
+ event->RemoveRelativeTag( tag->GetName() );
+
+ m_pScene->ReconcileTags();
+
+ PushRedo( "Delete Event Tag" );
+
+ g_pPhonemeEditor->redraw();
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::AddEventRelativeTag( void )
+{
+ CChoreoEventWidget *ew = m_pClickedEvent;
+ if ( !ew )
+ return;
+
+ CChoreoEvent *event = ew->GetEvent();
+ if ( !event->GetEndTime() )
+ {
+ Con_ErrorPrintf( "Event Tag: Can only tag events with an end time\n" );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Event Tag Name" );
+ strcpy( params.m_szPrompt, "Name:" );
+
+ strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
+ return;
+ }
+
+ RECT bounds = ew->getBounds();
+
+ // Convert click to frac
+ float frac = 0.0f;
+ if ( bounds.right - bounds.left > 0 )
+ {
+ frac = (float)( m_nClickedX - bounds.left ) / (float)( bounds.right - bounds.left );
+ frac = min( 1.0f, frac );
+ frac = max( 0.0f, frac );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Add Event Tag" );
+
+ event->AddRelativeTag( params.m_szInputText, frac );
+
+ PushRedo( "Add Event Tag" );
+
+ InvalidateLayout();
+ g_pPhonemeEditor->redraw();
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+CChoreoChannelWidget *CChoreoView::FindChannelForEvent( CChoreoEvent *event )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( e->GetEvent() != event )
+ continue;
+
+ return c;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : CChoreoEventWidget
+//-----------------------------------------------------------------------------
+CChoreoEventWidget *CChoreoView::FindWidgetForEvent( CChoreoEvent *event )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( e->GetEvent() != event )
+ continue;
+
+ return e;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::SelectAll( void )
+{
+ TraverseWidgets( &CChoreoView::SelectAllEvents, NULL );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::DeselectAll( void )
+{
+ TraverseWidgets( &CChoreoView::Deselect, NULL );
+ redraw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoView::UpdateStatusArea( int mx, int my )
+{
+ FLYOVER fo;
+
+ GetObjectsUnderMouse( mx, my, &fo.a, &fo.c,
+ &fo.e, &fo.ge, &fo.tag, &fo.at, &fo.ccbutton );
+
+ if ( fo.a )
+ {
+ m_Flyover.a = fo.a;
+ }
+ if ( fo.e )
+ {
+ m_Flyover.e = fo.e;
+ }
+ if ( fo.c )
+ {
+ m_Flyover.c = fo.c;
+ }
+ if ( fo.ge )
+ {
+ m_Flyover.ge = fo.ge;
+ }
+ if ( fo.tag != -1 )
+ {
+ m_Flyover.tag = fo.tag;
+ }
+ if ( fo.ccbutton != -1 )
+ {
+ m_Flyover.ccbutton = fo.ccbutton;
+ // m_Flyover.e = NULL;
+ }
+
+ RECT rcClip;
+ GetClientRect( (HWND)getHandle(), &rcClip );
+ rcClip.bottom -= m_nScrollbarHeight;
+ rcClip.top = rcClip.bottom - m_nInfoHeight;
+ rcClip.right -= m_nScrollbarHeight;
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcClip, COLOR_CHOREO_BACKGROUND );
+
+ drawHelper.StartClipping( rcClip );
+
+ RedrawStatusArea( drawHelper, rcClip );
+
+ drawHelper.StopClipping();
+ ValidateRect( (HWND)getHandle(), &rcClip );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::ClearStatusArea( void )
+{
+ memset( &m_Flyover, 0, sizeof( m_Flyover ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcStatus -
+//-----------------------------------------------------------------------------
+void CChoreoView::RedrawStatusArea( CChoreoWidgetDrawHelper& drawHelper, RECT& rcStatus )
+{
+ drawHelper.DrawFilledRect( COLOR_CHOREO_BACKGROUND, rcStatus );
+
+ drawHelper.DrawColoredLine( COLOR_INFO_BORDER, PS_SOLID, 1, rcStatus.left, rcStatus.top,
+ rcStatus.right, rcStatus.top );
+
+ RECT rcInfo = rcStatus;
+
+ rcInfo.top += 2;
+
+ if ( m_Flyover.e )
+ {
+ m_Flyover.e->redrawStatus( drawHelper, rcInfo );
+ }
+ if ( m_Flyover.c &&
+ m_Flyover.ccbutton != -1 )
+ {
+ m_Flyover.c->redrawStatus( drawHelper, rcInfo, m_Flyover.ccbutton );
+ }
+
+ if ( m_pScene )
+ {
+ char sz[ 512 ];
+
+ int fontsize = 9;
+ int fontweight = FW_NORMAL;
+
+ RECT rcText;
+ rcText = rcInfo;
+ rcText.bottom = rcText.top + fontsize + 2;
+
+ char const *mapname = m_pScene->GetMapname();
+ if ( mapname )
+ {
+ sprintf( sz, "Associated .bsp: %s", mapname[ 0 ] ? mapname : "none" );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz );
+ rcText.left = rcText.right - len - 10;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz );
+
+ OffsetRect( &rcText, 0, fontsize + 2 );
+ }
+
+ sprintf( sz, "Scene: %s", GetChoreoFile() );
+
+ int len = drawHelper.CalcTextWidth( "Arial", fontsize, fontweight, sz );
+ rcText.left = rcText.right - len - 10;
+
+ drawHelper.DrawColoredText( "Arial", fontsize, fontweight, COLOR_INFO_TEXT, rcText, sz );
+ }
+
+// drawHelper.DrawColoredText( "Arial", 12, 500, RGB( 0, 0, 0 ), rcInfo, m_Flyover.e ? m_Flyover.e->GetEvent()->GetName() : "" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::MoveEventToBack( CChoreoEvent *event )
+{
+ // Now find channel widget
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( event == e->GetEvent() )
+ {
+ // Move it to back of channel's list
+ c->MoveEventToTail( e );
+ InvalidateLayout();
+ return;
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoView::GetEndRow( void )
+{
+ RECT rcClient;
+ GetClientRect( (HWND)getHandle(), &rcClient );
+
+ return rcClient.bottom - ( m_nInfoHeight + m_nScrollbarHeight );
+}
+
+// Undo/Redo
+void CChoreoView::Undo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 )
+ {
+ m_nUndoLevel--;
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->undo );
+
+ DeleteSceneWidgets();
+
+ *m_pScene = *(u->undo);
+ g_MDLViewer->InitGridSettings();
+
+ CreateSceneWidgets();
+
+ ReportSceneClearToTools();
+ ClearStatusArea();
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+ }
+
+ InvalidateLayout();
+}
+
+void CChoreoView::Redo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->redo );
+
+ DeleteSceneWidgets();
+
+ *m_pScene = *(u->redo);
+ g_MDLViewer->InitGridSettings();
+
+ CreateSceneWidgets();
+
+ ReportSceneClearToTools();
+ ClearStatusArea();
+ m_pClickedActor = NULL;
+ m_pClickedChannel = NULL;
+ m_pClickedEvent = NULL;
+ m_pClickedGlobalEvent = NULL;
+
+ m_nUndoLevel++;
+ }
+
+ InvalidateLayout();
+}
+
+static char *CopyString( const char *in )
+{
+ int len = strlen( in );
+ char *n = new char[ len + 1 ];
+ strcpy( n, in );
+ return n;
+}
+
+void CChoreoView::PushUndo( const char *description )
+{
+ Assert( !m_bRedoPending );
+ m_bRedoPending = true;
+ WipeRedo();
+
+ // Copy current data
+ CChoreoScene *u = new CChoreoScene( this );
+ *u = *m_pScene;
+ CVUndo *undo = new CVUndo;
+ undo->undo = u;
+ undo->redo = NULL;
+ undo->udescription = CopyString( description );
+ undo->rdescription = NULL;
+ m_UndoStack.AddToTail( undo );
+ m_nUndoLevel++;
+}
+
+void CChoreoView::PushRedo( const char *description )
+{
+ Assert( m_bRedoPending );
+ m_bRedoPending = false;
+
+ // Copy current data
+ CChoreoScene *r = new CChoreoScene( this );
+ *r = *m_pScene;
+ CVUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ];
+ undo->redo = r;
+ undo->rdescription = CopyString( description );
+
+ // Always redo here to reflect that someone has made a change
+ redraw();
+}
+
+void CChoreoView::WipeUndo( void )
+{
+ while ( m_UndoStack.Size() > 0 )
+ {
+ CVUndo *u = m_UndoStack[ 0 ];
+ delete u->undo;
+ delete u->redo;
+ delete[] u->udescription;
+ delete[] u->rdescription;
+ delete u;
+ m_UndoStack.Remove( 0 );
+ }
+ m_nUndoLevel = 0;
+}
+
+void CChoreoView::WipeRedo( void )
+{
+ // Wipe everything above level
+ while ( m_UndoStack.Size() > m_nUndoLevel )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ delete u->undo;
+ delete u->redo;
+ delete[] u->udescription;
+ delete[] u->rdescription;
+ delete u;
+ m_UndoStack.Remove( m_nUndoLevel );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CChoreoView::GetUndoDescription( void )
+{
+ if ( CanUndo() )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel - 1 ];
+ return u->udescription;
+ }
+ return "???undo";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CChoreoView::GetRedoDescription( void )
+{
+ if ( CanRedo() )
+ {
+ CVUndo *u = m_UndoStack[ m_nUndoLevel ];
+ return u->rdescription;
+ }
+ return "???redo";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CanUndo()
+{
+ return m_nUndoLevel != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::CanRedo()
+{
+ return m_nUndoLevel != m_UndoStack.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::OnGestureTool( void )
+{
+ if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::GESTURE )
+ return;
+
+ g_pGestureTool->SetEvent( m_pClickedEvent->GetEvent() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CChoreoView::OnExpressionTool( void )
+{
+ if ( m_pClickedEvent->GetEvent()->GetType() != CChoreoEvent::FLEXANIMATION )
+ return;
+
+ g_pExpressionTool->SetEvent( m_pClickedEvent->GetEvent() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CChoreoScene
+//-----------------------------------------------------------------------------
+CChoreoScene *CChoreoView::GetScene( void )
+{
+ return m_pScene;
+}
+
+bool CChoreoView::CanPaste( void )
+{
+ char const *copyfile = COPYPASTE_FILENAME;
+
+ if ( !filesystem->FileExists( copyfile ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CChoreoView::CopyEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ char const *copyfile = COPYPASTE_FILENAME;
+ MakeFileWriteable( copyfile );
+ ExportVCDFile( copyfile );
+}
+
+void CChoreoView::PasteEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !CanPaste() )
+ return;
+
+ char const *copyfile = COPYPASTE_FILENAME;
+
+ ImportVCDFile( copyfile );
+}
+
+void CChoreoView::ImportEvents( void )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor || !m_pClickedChannel )
+ return;
+
+ char eventfile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( eventfile, sizeof( eventfile ), "scenes", "*.vce" ) )
+ return;
+
+ char fullpathbuf[ 512 ];
+ char *fullpath = eventfile;
+ if ( !Q_IsAbsolutePath( eventfile ) )
+ {
+ filesystem->RelativePathToFullPath( eventfile, "GAME", fullpathbuf, sizeof( fullpathbuf ) );
+ fullpath = fullpathbuf;
+ }
+
+ if ( !filesystem->FileExists( fullpath ) )
+ return;
+
+ LoadScriptFile( fullpath );
+
+ DeselectAll();
+
+ SetDirty( true );
+
+ PushUndo( "Import Events" );
+
+ m_pScene->ImportEvents( tokenprocessor, m_pClickedActor->GetActor(), m_pClickedChannel->GetChannel() );
+
+ PushRedo( "Import Events" );
+
+ CreateSceneWidgets();
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Imported events from %s\n", fullpath );
+}
+
+void CChoreoView::ExportEvents( void )
+{
+ char eventfilename[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( eventfilename, sizeof( eventfilename ), "scenes", "*.vce" ) )
+ return;
+
+ Q_DefaultExtension( eventfilename, ".vce", sizeof( eventfilename ) );
+
+ Con_Printf( "Exporting events to %s\n", eventfilename );
+
+ // Write to file
+ CUtlVector< CChoreoEvent * > events;
+
+ // Find selected eventss
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( !e->IsSelected() )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ events.AddToTail( event );
+ }
+ }
+ }
+
+ if ( events.Size() > 0 )
+ {
+ m_pScene->ExportEvents( eventfilename, events );
+ }
+ else
+ {
+ Con_Printf( "No events selected\n" );
+ }
+}
+
+void CChoreoView::ExportVCDFile( char const *filename )
+{
+ Con_Printf( "Exporting to %s\n", filename );
+
+ // Unmark everything
+ m_pScene->MarkForSaveAll( false );
+
+ // Mark everything related to selected events
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( !e->IsSelected() )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ event->SetMarkedForSave( true );
+ if ( event->GetChannel() )
+ {
+ event->GetChannel()->SetMarkedForSave( true );
+ }
+ if ( event->GetActor() )
+ {
+ event->GetActor()->SetMarkedForSave( true );
+ }
+ }
+ }
+ }
+
+ m_pScene->ExportMarkedToFile( filename );
+}
+
+void CChoreoView::ImportVCDFile( char const *filename )
+{
+ CChoreoScene *merge = LoadScene( filename );
+ if ( !merge )
+ {
+ Con_Printf( "Couldn't load from .vcd %s\n", filename );
+ return;
+ }
+
+ DeselectAll();
+
+ CUtlRBTree< CChoreoEvent *, int > oldEvents( 0, 0, DefLessFunc( CChoreoEvent * ) );
+
+ int i;
+ for ( i = 0; i < m_SceneActors.Count(); ++i )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ oldEvents.Insert( event->GetEvent() );
+ }
+ }
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Merge/Import VCD" );
+
+ m_pScene->Merge( merge );
+
+ PushRedo( "Merge/Import VCD" );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ // Now walk through the "new" events and select everything that wasn't already there (all of the stuff that was "added" during the merge)
+ for ( i = 0; i < m_SceneActors.Count(); ++i )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ if ( oldEvents.Find( event->GetEvent() ) == oldEvents.InvalidIndex() )
+ {
+ event->SetSelected( true );
+ }
+ }
+ }
+ }
+
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Imported vcd '%s'\n", filename );
+
+ delete merge;
+
+ redraw();
+}
+
+void CChoreoView::ExportVCD()
+{
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ Q_DefaultExtension( scenefile, ".vcd", sizeof( scenefile ) );
+
+ ExportVCDFile( scenefile );
+}
+
+void CChoreoView::ImportVCD()
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor || !m_pClickedChannel )
+ return;
+
+ char scenefile[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( scenefile, sizeof( scenefile ), "scenes", "*.vcd" ) )
+ {
+ return;
+ }
+
+ ImportVCDFile( scenefile );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsProcessing( void )
+{
+ if ( !m_pScene )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::ShouldProcessSpeak( void )
+{
+ if ( !g_pControlPanel->AllToolsDriveSpeech() )
+ {
+ if ( !IsActiveTool() )
+ return false;
+ }
+
+ if ( IFacePoserToolWindow::IsAnyToolScrubbing() )
+ return true;
+
+ if ( IFacePoserToolWindow::IsAnyToolProcessing() )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSpeak( CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !ShouldProcessSpeak() )
+ return;
+
+ Assert( event->GetType() == CChoreoEvent::SPEAK );
+ Assert( scene );
+
+ float t = scene->GetTime();
+
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+
+ // See if we should trigger CC
+ char soundname[ 512 ];
+ Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
+
+ float actualEndTime = event->GetEndTime();
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ // Use the token as the sound name lookup, too.
+ if ( event->IsUsingCombinedFile() &&
+ ( event->GetNumSlaves() > 0 ) )
+ {
+ Q_strncpy( soundname, tok, sizeof( soundname ) );
+ actualEndTime = max( actualEndTime, event->GetLastSlaveEndTime() );
+ }
+ }
+ }
+
+ CAudioMixer *mixer = event->GetMixer();
+ if ( !mixer || !sound->IsSoundPlaying( mixer ) )
+ {
+ CSoundParameters params;
+ float volume = VOL_NORM;
+
+ gender_t gender = GENDER_NONE;
+ if (model)
+ {
+ gender = soundemitter->GetActorGender( model->GetFileName() );
+ }
+
+ if ( !Q_stristr( soundname, ".wav" ) &&
+ soundemitter->GetParametersForSound( soundname, params, gender ) )
+ {
+ volume = params.volume;
+ }
+
+ sound->PlaySound(
+ model,
+ volume,
+ va( "sound/%s", FacePoser_TranslateSoundName( soundname, model ) ),
+ &mixer );
+ event->SetMixer( mixer );
+ }
+
+ mixer = event->GetMixer();
+ if ( !mixer )
+ return;
+
+ mixer->SetDirection( m_flFrameTime >= 0.0f );
+ float starttime, endtime;
+ starttime = event->GetStartTime();
+ endtime = actualEndTime;
+
+ float soundtime = endtime - starttime;
+ if ( soundtime <= 0.0f )
+ return;
+
+ float f = ( t - starttime ) / soundtime;
+ f = clamp( f, 0.0f, 1.0f );
+
+ // Compute sample
+ float numsamples = (float)mixer->GetSource()->SampleCount();
+
+ int cursample = f * numsamples;
+ cursample = clamp( cursample, 0, numsamples - 1 );
+
+ int realsample = mixer->GetSamplePosition();
+
+ int dsample = cursample - realsample;
+
+ int samplelimit = mixer->GetSource()->SampleRate() * 0.02f; // don't shift until samples are off by this much
+ if (IsScrubbing())
+ {
+ samplelimit = mixer->GetSource()->SampleRate() * 0.01f; // make it shorter tolerance when scrubbing
+ }
+
+ if ( abs( dsample ) > samplelimit )
+ {
+ mixer->SetSamplePosition( cursample, IsScrubbing() );
+ }
+ mixer->SetActive( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessSubscene( CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event->GetType() == CChoreoEvent::SUBSCENE );
+
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ return;
+
+ if ( subscene->SimulationFinished() )
+ return;
+
+ // Have subscenes think for appropriate time
+ subscene->Think( m_flScrub );
+}
+
+void CChoreoView::PositionControls()
+{
+ int topx = GetCaptionHeight() + SCRUBBER_HEIGHT;
+
+ int bx = 2;
+ int bw = 16;
+
+ m_btnPlay->setBounds( bx, topx + 4, 16, 16 );
+
+ bx += bw + 2;
+
+ m_btnPause->setBounds( bx, topx + 4, 16, 16 );
+ bx += bw + 2;
+ m_btnStop->setBounds( bx, topx + 4, 16, 16 );
+ bx += bw + 2;
+ m_pPlaybackRate->setBounds( bx, topx + 4, 100, 16 );
+}
+
+void CChoreoView::SetChoreoFile( char const *filename )
+{
+ strcpy( m_szChoreoFile, filename );
+ if ( m_szChoreoFile[ 0 ] )
+ {
+ char sz[ 256 ];
+ if ( IsFileWriteable( m_szChoreoFile ) )
+ {
+ Q_snprintf( sz, sizeof( sz ), " - %s", m_szChoreoFile );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), " - %s [Read-Only]", m_szChoreoFile );
+ }
+ SetSuffix( sz );
+ }
+ else
+ {
+ SetSuffix( "" );
+ }
+}
+
+char const *CChoreoView::GetChoreoFile( void ) const
+{
+ return m_szChoreoFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetScrubHandleRect( RECT& rcHandle, bool clipped )
+{
+ float pixel = 0.0f;
+
+ if ( m_pScene )
+ {
+ float currenttime = m_flScrub;
+ float starttime = m_flStartTime;
+ float endtime = m_flEndTime;
+
+ float screenfrac = ( currenttime - starttime ) / ( endtime - starttime );
+
+ pixel = GetLabelWidth() + screenfrac * ( w2() - GetLabelWidth() );
+
+ if ( clipped )
+ {
+ pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 );
+ }
+ }
+
+ rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2;
+ rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2;
+ rcHandle.top = 2 + GetCaptionHeight();
+ rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcArea -
+//-----------------------------------------------------------------------------
+void CChoreoView::GetScrubAreaRect( RECT& rcArea )
+{
+ rcArea.left = 0;
+ rcArea.right = w2();
+ rcArea.top = 2 + GetCaptionHeight();
+ rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void CChoreoView::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+
+ HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", m_flScrub );
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
+
+ RECT rcText = rcHandle;
+ int textw = rcText.right - rcText.left;
+
+ rcText.left += ( textw - len ) / 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz );
+
+ DeleteObject( br );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoView::IsMouseOverScrubHandle( mxEvent *event )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+ InflateRect( &rcHandle, 2, 2 );
+
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ if ( PtInRect( &rcHandle, pt ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+bool CChoreoView::IsMouseOverScrubArea( mxEvent *event )
+{
+ RECT rcArea;
+ GetScrubAreaRect( rcArea );
+
+ InflateRect( &rcArea, 2, 2 );
+
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ if ( PtInRect( &rcArea, pt ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool CChoreoView::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CChoreoView::Think( float dt )
+{
+ bool scrubbing = IFacePoserToolWindow::IsAnyToolScrubbing();
+
+ ScrubThink( dt, scrubbing, this );
+}
+
+static int lastthinkframe = -1;
+void CChoreoView::ScrubThink( float dt, bool scrubbing, IFacePoserToolWindow *invoker )
+{
+ // Make sure we don't get called more than once per frame
+ int thisframe = g_MDLViewer->GetCurrentFrame();
+ if ( thisframe == lastthinkframe )
+ return;
+
+ lastthinkframe = thisframe;
+
+ if ( !m_pScene )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ {
+ // Act like it's paused
+ if ( IFacePoserToolWindow::ShouldAutoProcess() )
+ {
+ m_bSimulating = true;
+ SceneThink( m_flScrub );
+ }
+
+ if ( m_bSimulating && !m_bPaused )
+ {
+ //FinishSimulation();
+ }
+ return;
+ }
+
+ // Make sure we're solving head turns during playback
+ models->SetSolveHeadTurn( 1 );
+
+ if ( m_bPaused )
+ {
+ SceneThink( m_flScrub );
+ return;
+ }
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ if ( !m_bSimulating )
+ {
+ m_bSimulating = true;
+ }
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt * m_flPlaybackRate;
+
+ float prevScrub = m_flScrub;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ m_flScrub = m_flScrubTarget;
+ }
+ else
+ {
+ m_flScrub += maxmove;
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ m_flScrub = m_flScrubTarget;
+ }
+ else
+ {
+ m_flScrub -= maxmove;
+ }
+ }
+
+ m_flFrameTime = ( m_flScrub - prevScrub );
+
+ SceneThink( m_flScrub );
+
+ DrawScrubHandle();
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+
+ if ( invoker != g_pExpressionTool )
+ {
+ g_pExpressionTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pGestureTool )
+ {
+ g_pGestureTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pRampTool )
+ {
+ g_pRampTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+ if ( invoker != g_pSceneRampTool )
+ {
+ g_pSceneRampTool->ForceScrubPositionFromSceneTime( m_flScrub );
+ }
+}
+
+void CChoreoView::DrawScrubHandle( void )
+{
+ if ( !m_bCanDraw )
+ return;
+
+ // Handle new time and
+ RECT rcArea;
+ GetScrubAreaRect( rcArea );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcArea, COLOR_CHOREO_BACKGROUND );
+ DrawScrubHandle( drawHelper );
+}
+
+void CChoreoView::SetScrubTime( float t )
+{
+ m_flScrub = t;
+
+ m_bPaused = false;
+}
+
+void CChoreoView::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+
+ m_bPaused = false;
+}
+
+void CChoreoView::ClampTimeToSelectionInterval( float& timeval )
+{
+ // FIXME hook this up later
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : show -
+//-----------------------------------------------------------------------------
+void CChoreoView::ShowButtons( bool show )
+{
+ m_btnPlay->setVisible( show );
+ m_btnPause->setVisible( show );
+ m_btnStop->setVisible( show );
+ m_pPlaybackRate->setVisible( show );
+}
+
+void CChoreoView::RememberSelectedEvents( CUtlVector< CChoreoEvent * >& list )
+{
+ GetSelectedEvents( list );
+}
+
+void CChoreoView::ReselectEvents( CUtlVector< CChoreoEvent * >& list )
+{
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *check = event->GetEvent();
+ if ( list.Find( check ) != list.InvalidIndex() )
+ {
+ event->SetSelected( true );
+ }
+ }
+ }
+ }
+
+}
+
+void CChoreoView::OnChangeScale( void )
+{
+ CChoreoScene *scene = m_pScene;
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change Zoom" );
+ strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ CUtlVector< CChoreoEvent * > selected;
+ RememberSelectedEvents( selected );
+
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+
+ ReselectEvents( selected );
+
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", GetTimeZoom( GetToolName() ) );
+}
+
+void CChoreoView::OnCheckSequenceLengths( void )
+{
+ if ( !m_pScene )
+ return;
+
+ Con_Printf( "Checking sequence durations...\n" );
+
+ bool changed = FixupSequenceDurations( m_pScene, true );
+
+ if ( !changed )
+ {
+ Con_Printf( " no changes...\n" );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Check sequence lengths" );
+
+ FixupSequenceDurations( m_pScene, false );
+
+ PushRedo( "Check sequence lengths" );
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateTrackLookup_R( CChoreoScene *scene )
+{
+ // No need to undo since this data doesn't matter
+ int c = scene->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ event->SetTrackLookupSet( false );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ CChoreoScene *sub = event->GetSubScene();
+ // NOTE: Don't bother loading it now if it's not on hand
+ if ( sub )
+ {
+ InvalidateTrackLookup_R( sub );
+ }
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Model changed so we'll have to re-index flex anim tracks
+//-----------------------------------------------------------------------------
+void CChoreoView::InvalidateTrackLookup( void )
+{
+ if ( !m_pScene )
+ return;
+
+ InvalidateTrackLookup_R( m_pScene );
+}
+
+
+
+bool CChoreoView::IsRampOnly( void ) const
+{
+ return m_bRampOnly;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessInterrupt( CChoreoScene *scene, CChoreoEvent *event )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CChoreoView::ProcessPermitResponses( CChoreoScene *scene, CChoreoEvent *event )
+{
+}
+
+void CChoreoView::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns -1 if no event found
+// Input : *channel -
+// *e -
+// forward -
+// Output : float
+//-----------------------------------------------------------------------------
+float CChoreoView::FindNextEventTime( CChoreoEvent::EVENTTYPE type, CChoreoChannel *channel, CChoreoEvent *e, bool forward )
+{
+ bool foundone = false;
+ float bestTime = -1.0f;
+ float bestGap = 999999.0f;
+
+ int c = channel->GetNumEvents();
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *test = channel->GetEvent( i );
+ if ( test->GetType() != type )
+ continue;
+
+ if ( forward )
+ {
+ float dt = test->GetStartTime() - e->GetEndTime();
+ if ( dt <= 0.0f )
+ continue;
+
+ if ( dt < bestGap )
+ {
+ foundone = true;
+ bestGap = dt;
+ bestTime = test->GetStartTime();
+ }
+ }
+ else
+ {
+ float dt = e->GetStartTime() - test->GetEndTime();
+ if ( dt <= 0.0f )
+ continue;
+
+ if ( dt < bestGap )
+ {
+ foundone = true;
+ bestGap = dt;
+ bestTime = test->GetEndTime();
+ }
+ }
+ }
+
+ return bestTime;
+}
+
+void CChoreoView::CalcBounds( int movetype )
+{
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+
+ if ( !m_pClickedEvent )
+ return;
+
+ switch ( movetype )
+ {
+ default:
+ break;
+ case DRAGTYPE_EVENT_MOVE:
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ m_nMinX = GetPixelForTimeValue( 0 );
+ m_nMaxX = GetPixelForTimeValue( m_pScene->FindStopTime() );
+
+ CChoreoEvent *e = m_pClickedEvent->GetEvent();
+
+ m_bUseBounds = false; // e && e->GetType() == CChoreoEvent::GESTURE;
+ // FIXME: use this for finding adjacent gesture edges (kenb)
+ if ( m_bUseBounds )
+ {
+ CChoreoChannel *channel = e->GetChannel();
+ Assert( channel );
+
+ float forwardTime = FindNextEventTime( e->GetType(), channel, e, true );
+ float reverseTime = FindNextEventTime( e->GetType(), channel, e, false );
+
+ // Compute pixel for time
+ int nextPixel = forwardTime != -1 ? GetPixelForTimeValue( forwardTime ) : m_nMaxX;
+ int prevPixel = reverseTime != -1 ? GetPixelForTimeValue( reverseTime ) : m_nMinX;
+
+ int startPixel = GetPixelForTimeValue( e->GetStartTime() );
+ int endPixel = GetPixelForTimeValue( e->GetEndTime() );
+
+ switch ( movetype )
+ {
+ case DRAGTYPE_EVENT_MOVE:
+ {
+ m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
+ m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
+ }
+ break;
+ case DRAGTYPE_EVENT_STARTTIME:
+ case DRAGTYPE_EVENT_STARTTIME_RESCALE:
+ {
+ m_nMinX = prevPixel + ( m_xStart - startPixel ) + 1;
+ }
+ break;
+ case DRAGTYPE_EVENT_ENDTIME:
+ case DRAGTYPE_EVENT_ENDTIME_RESCALE:
+ {
+ m_nMaxX = nextPixel - ( endPixel - m_xStart ) - 1;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+}
+
+bool CChoreoView::ShouldSelectEvent( SelectionParams_t &params, CChoreoEvent *event )
+{
+ if ( params.forward )
+ {
+ if ( event->GetStartTime() >= params.time )
+ return true;
+ }
+ else
+ {
+ float endtime = event->HasEndTime() ? event->GetEndTime() : event->GetStartTime();
+
+ if ( endtime <= params.time )
+ return true;
+ }
+ return false;
+}
+
+void CChoreoView::SelectEvents( SelectionParams_t& params )
+{
+ if ( !m_pScene )
+ return;
+
+ if ( !m_pClickedActor )
+ return;
+
+ if ( params.type == SelectionParams_t::SP_CHANNEL && !m_pClickedChannel )
+ return;
+
+ //CChoreoActor *actor = m_pClickedActor->GetActor();
+ CChoreoChannel *channel = m_pClickedChannel ? m_pClickedChannel->GetChannel() : NULL;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *a = m_SceneActors[ i ];
+ if ( !a )
+ continue;
+
+ //if ( a->GetActor() != actor )
+ // continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ if ( params.type == SelectionParams_t::SP_CHANNEL &&
+ c->GetChannel() != channel )
+ continue;
+
+ if ( params.type == SelectionParams_t::SP_ACTIVE &&
+ !c->GetChannel()->GetActive() )
+ continue;
+
+ for ( int k = 0; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !ShouldSelectEvent( params, event ) )
+ continue;
+
+ e->SetSelected( true );
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *e = m_SceneGlobalEvents[ i ];
+ if ( !e )
+ continue;
+
+ CChoreoEvent *event = e->GetEvent();
+ if ( !event )
+ continue;
+
+ if ( !ShouldSelectEvent( params, event ) )
+ continue;
+
+ e->SetSelected( true );
+ }
+
+ redraw();
+}
+
+void CChoreoView::SetTimeZoom( char const *tool, int tz, bool preserveFocus )
+{
+ if ( !m_pScene )
+ return;
+
+ // No change
+ int oldZoom = GetTimeZoom( tool );
+ if ( tz == oldZoom )
+ return;
+
+ SetDirty( true );
+
+ POINT pt;
+ ::GetCursorPos( &pt );
+ ::ScreenToClient( (HWND)getHandle(), &pt );
+
+ // Now figure out time under cursor at old zoom scale
+ float t = GetTimeValueForMouse( pt.x, true );
+
+ m_pScene->SetTimeZoom( tool, tz );
+
+ if ( preserveFocus )
+ {
+ RECT rc;
+ GetClientRect( (HWND)getHandle(), &rc );
+ RECT rcClient = rc;
+ rcClient.top += GetStartRow();
+ OffsetRect( &rcClient, 0, -m_nTopOffset );
+ m_flStartTime = m_flLeftOffset / GetPixelsPerSecond();
+ m_flEndTime = m_flStartTime + (float)( rcClient.right - GetLabelWidth() ) / GetPixelsPerSecond();
+
+ // Now figure out tie under pt.x
+ float newT = GetTimeValueForMouse( pt.x, true );
+ if ( newT != t )
+ {
+ // We need to scroll over a bit
+ float pps = GetPixelsPerSecond();
+ float movePixels = pps * ( newT - t );
+
+ m_flLeftOffset -= movePixels;
+ if ( m_flLeftOffset < 0.0f )
+ {
+ //float fixup = - m_flLeftOffset;
+ m_flLeftOffset = 0;
+ }
+
+ // float maxtime = m_pScene->FindStopTime();
+ float flApparentEndTime = max( m_pScene->FindStopTime(), 5.0f ) + 5.0f;
+ if ( m_flEndTime > flApparentEndTime )
+ {
+ movePixels = pps * ( m_flEndTime - flApparentEndTime );
+ m_flLeftOffset = max( 0.0f, m_flLeftOffset - movePixels );
+ }
+ }
+ }
+
+ // Deal with the slider
+ RepositionHSlider();
+ redraw();
+}
+
+int CChoreoView::GetTimeZoom( char const *tool )
+{
+ if ( !m_pScene )
+ return 100;
+
+ return m_pScene->GetTimeZoom( tool );
+}
+
+void CChoreoView::CheckInsertTime( CChoreoEvent *e, float dt, float starttime, float endtime )
+{
+ // Not influenced
+ float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+
+ if ( eventend < starttime )
+ return;
+
+ if ( e->GetStartTime() > starttime )
+ {
+ e->OffsetTime( dt );
+ e->SnapTimes();
+ }
+ else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time
+ {
+ float newduration = e->GetDuration() + dt;
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() + dt, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() + dt );
+ }
+ break;
+ }
+ e->OffsetEndTime( dt );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+}
+
+void CChoreoView::OnInsertTime()
+{
+ if ( !m_rgABPoints[ 0 ].active &&
+ !m_rgABPoints[ 1 ].active )
+ {
+ return;
+ }
+
+ Con_Printf( "OnInsertTime()\n" );
+
+ float starttime = m_rgABPoints[ 0 ].time;
+ float endtime = m_rgABPoints[ 1 ].time;
+
+ // Sort samples correctly
+ if ( starttime > endtime )
+ {
+ float temp = starttime;
+ starttime = endtime;
+ endtime = temp;
+ }
+
+ float dt = endtime - starttime;
+ if ( dt == 0.0f )
+ {
+ // Nothing to do...
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Insert Time" );
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ CheckInsertTime( e, dt, starttime, endtime );
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ CheckInsertTime( e, dt, starttime, endtime );
+ }
+
+ PushRedo( "Insert Time" );
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+void CChoreoView::CheckDeleteTime( CChoreoEvent *e, float dt, float starttime, float endtime, bool& deleteEvent )
+{
+ deleteEvent = false;
+
+ // Not influenced
+ float eventend = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime();
+
+ if ( eventend < starttime )
+ {
+ return;
+ }
+
+ // On right side of start mark, just shift left
+ if ( e->GetStartTime() > starttime )
+ {
+ // If it has no duration and it's in the bounds then kill it.
+ if ( !e->HasEndTime() && e->GetStartTime() < endtime )
+ {
+ deleteEvent = true;
+ return;
+ }
+ else
+ {
+ float shift = e->GetStartTime() - starttime;
+ float maxoffset = min( dt, shift );
+
+ e->OffsetTime( -maxoffset );
+ e->SnapTimes();
+ }
+ }
+ else if ( !e->IsFixedLength() && e->HasEndTime() ) // the event starts before start, but ends after start time, need to insert space into the event, act like user dragged end time
+ {
+ float shiftend = e->GetEndTime() - starttime;
+ float maxoffset = min( dt, shiftend );
+
+ float newduration = e->GetDuration() - maxoffset;
+ if ( newduration <= 0.0f )
+ {
+ deleteEvent = true;
+ return;
+ }
+ else
+ {
+ RescaleRamp( e, newduration );
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ e->RescaleGestureTimes( e->GetStartTime(), e->GetEndTime() - maxoffset, true );
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ RescaleExpressionTimes( e, e->GetStartTime(), e->GetEndTime() - maxoffset );
+ }
+ break;
+ }
+ e->OffsetEndTime( -maxoffset );
+ e->SnapTimes();
+ e->ResortRamp();
+ }
+ }
+
+ switch ( e->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ e->SetEndTime( e->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ CheckSequenceLength( e, false );
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CheckGestureLength( e, false );
+ }
+ break;
+ }
+}
+
+void CChoreoView::OnDeleteTime()
+{
+ if ( !m_rgABPoints[ 0 ].active &&
+ !m_rgABPoints[ 1 ].active )
+ {
+ return;
+ }
+
+ Con_Printf( "OnDeleteTime()\n" );
+
+ float starttime = m_rgABPoints[ 0 ].time;
+ float endtime = m_rgABPoints[ 1 ].time;
+
+ // Sort samples correctly
+ if ( starttime > endtime )
+ {
+ float temp = starttime;
+ starttime = endtime;
+ endtime = temp;
+ }
+
+ float dt = endtime - starttime;
+ if ( dt == 0.0f )
+ {
+ // Nothing to do...
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Delete Time" );
+
+ CUtlVector< CChoreoEventWidget * > deletions;
+ CUtlVector< CChoreoGlobalEventWidget * > global_deletions;
+
+ for ( int i = 0; i < m_SceneActors.Size(); i++ )
+ {
+ CChoreoActorWidget *actor = m_SceneActors[ i ];
+ if ( !actor )
+ continue;
+
+ for ( int j = 0; j < actor->GetNumChannels(); j++ )
+ {
+ CChoreoChannelWidget *channel = actor->GetChannel( j );
+ if ( !channel )
+ continue;
+
+ for ( int k = 0; k < channel->GetNumEvents(); k++ )
+ {
+ CChoreoEventWidget *event = channel->GetEvent( k );
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ bool deleteEvent = false;
+
+ CheckDeleteTime( e, dt, starttime, endtime, deleteEvent );
+
+ if ( deleteEvent )
+ {
+ deletions.AddToTail( event );
+ }
+ }
+ }
+ }
+
+ // Now handle global events, too
+ for ( int i = 0; i < m_SceneGlobalEvents.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event )
+ continue;
+
+ CChoreoEvent *e = event->GetEvent();
+ if ( !e )
+ continue;
+
+ bool deleteEvent = false;
+ CheckDeleteTime( e, dt, starttime, endtime, deleteEvent );
+
+ if ( deleteEvent )
+ {
+ global_deletions.AddToTail( event );
+ }
+ }
+
+ for ( int i = 0; i < deletions.Count(); i++ )
+ {
+ CChoreoEventWidget *w = deletions[ i ];
+
+ CChoreoEvent *e = w->GetEvent();
+ CChoreoChannel *channel = e->GetChannel();
+ if ( channel )
+ {
+ channel->RemoveEvent( e );
+ }
+ m_pScene->DeleteReferencedObjects( e );
+ }
+
+ for ( int i = 0; i < global_deletions.Count(); i++ )
+ {
+ CChoreoGlobalEventWidget *w = global_deletions[ i ];
+ CChoreoEvent *e = w->GetEvent();
+ m_pScene->DeleteReferencedObjects( e );
+ }
+
+ // Force scroll bars to recompute
+ ForceScrollBarsToRecompute( false );
+
+ if ( deletions.Count() > 0 || global_deletions.Count() > 0 )
+ {
+ DeleteSceneWidgets();
+ CreateSceneWidgets();
+ }
+
+ PushRedo( "Delete Time" );
+
+ InvalidateLayout();
+
+ g_pExpressionTool->LayoutItems( true );
+ g_pExpressionTool->redraw();
+ g_pGestureTool->redraw();
+ g_pRampTool->redraw();
+ g_pSceneRampTool->redraw();
+}
+
+void CChoreoView::OnModelChanged()
+{
+ InvalidateTrackLookup();
+ // OnCheckSequenceLengths();
+}
+
+void CChoreoView::SetShowCloseCaptionData( bool show )
+{
+ m_bShowCloseCaptionData = show;
+}
+
+bool CChoreoView::GetShowCloseCaptionData( void ) const
+{
+ return m_bShowCloseCaptionData;
+}
+
+void CChoreoView::OnToggleCloseCaptionTags()
+{
+ m_bShowCloseCaptionData = !m_bShowCloseCaptionData;
+ InvalidateLayout();
+}
+
+
+
+static bool EventStartTimeLessFunc( CChoreoEvent * const &p1, CChoreoEvent * const &p2 )
+{
+ CChoreoEvent *w1;
+ CChoreoEvent *w2;
+
+ w1 = const_cast< CChoreoEvent * >( p1 );
+ w2 = const_cast< CChoreoEvent * >( p2 );
+
+ return w1->GetStartTime() < w2->GetStartTime();
+}
+
+bool CChoreoView::GenerateCombinedFile( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted )
+{
+ CUtlVector< CombinerEntry > work;
+
+ char actualfile[ 512 ];
+ soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) );
+ if ( Q_strlen( actualfile ) <= 0 )
+ {
+ return false;
+ }
+
+ int i = sorted.FirstInorder();
+ if ( i != sorted.InvalidIndex() )
+ {
+ CChoreoEvent *e = sorted[ i ];
+
+ float startoffset = e->GetStartTime();
+
+ do
+ {
+ e = sorted[ i ];
+
+ float curoffset = e->GetStartTime();
+
+ CombinerEntry ce;
+ Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) );
+ ce.startoffset = curoffset - startoffset;
+
+ work.AddToTail( ce );
+
+ i = sorted.NextInorder( i );
+ }
+ while ( i != sorted.InvalidIndex() );
+ }
+
+ bool ok = soundcombiner->CombineSoundFiles( filesystem, actualfile, work );
+ if ( !ok )
+ {
+ Con_ErrorPrintf( "Failed to create combined sound '%s':'%s'\n", cctoken, actualfile );
+ return false;
+ }
+ Con_Printf( "Created combined sound '%s':'%s'\n", cctoken, actualfile );
+ return true;
+}
+
+bool CChoreoView::ValidateCombinedFileCheckSum( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted )
+{
+ CUtlVector< CombinerEntry > work;
+
+ char actualfile[ 512 ];
+ soundemitter->GenderExpandString( gender, outfilename, actualfile, sizeof( actualfile ) );
+ if ( Q_strlen( actualfile ) <= 0 )
+ {
+ return false;
+ }
+
+ int i = sorted.FirstInorder();
+ if ( i != sorted.InvalidIndex() )
+ {
+ CChoreoEvent *e = sorted[ i ];
+
+ float startoffset = e->GetStartTime();
+
+ do
+ {
+ e = sorted[ i ];
+
+ float curoffset = e->GetStartTime();
+
+ CombinerEntry ce;
+ Q_snprintf( ce.wavefile, sizeof( ce.wavefile ), "sound/%s", FacePoser_TranslateSoundNameGender( e->GetParameters(), gender ) );
+ ce.startoffset = curoffset - startoffset;
+
+ work.AddToTail( ce );
+
+ i = sorted.NextInorder( i );
+ }
+ while ( i != sorted.InvalidIndex() );
+ }
+
+ return soundcombiner->IsCombinedFileChecksumValid( filesystem, actualfile, work );
+}
+
+void SuggestCaption( char *dest, int destlen, CUtlVector< CChoreoEvent * >& events )
+{
+ // Walk through events and concatenate current captions, or raw wav data if have any
+ dest[ 0 ] = 0;
+
+ int c = events.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CChoreoEvent *e = events[ i ];
+
+ bool found = false;
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( e->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ wchar_t *localized = g_pLocalize->Find( tok );
+ if ( localized )
+ {
+ found = true;
+
+ char ansi[ 1024 ];
+ g_pLocalize->ConvertUnicodeToANSI( localized, ansi, sizeof( ansi ) );
+ Q_strncat( dest, ansi, destlen, COPY_ALL_CHARACTERS );
+ }
+ }
+
+ if ( !found )
+ {
+ // See if the wav file has data...
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( e ) ) );
+ if ( wave )
+ {
+ CSentence *sentence = wave->GetSentence();
+ if ( sentence )
+ {
+ Q_strncat( dest, sentence->GetText(), destlen, COPY_ALL_CHARACTERS );
+ found = true;
+ }
+ }
+ }
+
+ if ( found && Q_strlen( dest ) > 0 && i != c - 1 )
+ {
+ Q_strncat( dest, " ", destlen, COPY_ALL_CHARACTERS );
+ }
+ }
+}
+
+void CChoreoView::OnCombineSpeakEvents()
+{
+ if ( !m_pScene )
+ return;
+
+ CChoreoChannel *firstChannel = NULL;
+
+ CUtlVector< CChoreoEvent * > selected;
+ GetSelectedEvents( selected );
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ Con_ErrorPrintf( "Can't combine events, all events must be SPEAK events.\n" );
+ return;
+ }
+
+ if ( !firstChannel )
+ {
+ firstChannel = e->GetChannel();
+ }
+ else if ( e->GetChannel() != firstChannel )
+ {
+ Con_ErrorPrintf( "Can't combine events, all events must reside in the same channel.\n" );
+ return;
+ }
+ }
+
+ if ( selected.Count() < 2 )
+ {
+ Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" );
+ return;
+ }
+
+ // Let the user pick a CC phrase
+ CCloseCaptionLookupParams params;
+ Q_strncpy( params.m_szDialogTitle, "Choose Close Caption Token", sizeof( params.m_szDialogTitle ) );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+
+ char playbacktoken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( !selected[0]->GetPlaybackCloseCaptionToken( playbacktoken, sizeof( playbacktoken ) ) )
+ {
+ return;
+ }
+
+ if ( !Q_stristr( playbacktoken, "_cc" ) )
+ {
+ Q_strncpy( params.m_szCCToken, va( "%s_cc", playbacktoken ), sizeof( params.m_szCCToken ) );
+ }
+ else
+ {
+ Q_strncpy( params.m_szCCToken, va( "%s", playbacktoken ), sizeof( params.m_szCCToken ) );
+ }
+
+ // User hit okay and value actually changed?
+ if ( !CloseCaptionLookup( &params ) &&
+ params.m_szCCToken[0] != 0 )
+ {
+ return;
+ }
+
+ // See if the token exists?
+ StringIndex_t stringIndex = g_pLocalize->FindIndex( params.m_szCCToken );
+ if ( INVALID_LOCALIZE_STRING_INDEX == stringIndex )
+ {
+ // Add token to closecaption_english file.
+ // Guess at string and ask user to confirm.
+ CInputParams ip;
+ memset( &ip, 0, sizeof( ip ) );
+
+ Q_strncpy( ip.m_szDialogTitle, "Add Close Caption", sizeof( ip.m_szDialogTitle ) );
+ Q_snprintf( ip.m_szPrompt, sizeof( ip.m_szPrompt ), "Token (%s):", params.m_szCCToken );
+
+ char suggested[ 2048 ];
+
+ SuggestCaption( suggested, sizeof( suggested ), selected );
+
+ Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "%s", suggested );
+
+ if ( !InputProperties( &ip ) )
+ {
+ Con_Printf( "Combining of sound events cancelled\n" );
+ return;
+ }
+
+ if ( Q_strlen( ip.m_szInputText ) == 0 )
+ {
+ Q_snprintf( ip.m_szInputText, sizeof( ip.m_szInputText ), "!!!%s", params.m_szCCToken );
+ }
+
+ char const *captionFile = "resource/closecaption_english.txt";
+
+ if ( !filesystem->IsFileWritable( captionFile, "GAME" ) )
+ {
+ Warning( "Forcing %s to be writable!!!\n", captionFile );
+ MakeFileWriteable( captionFile );
+ }
+
+ wchar_t unicode[ 2048 ];
+ g_pLocalize->ConvertANSIToUnicode( ip.m_szInputText, unicode, sizeof( unicode ) );
+
+ g_pLocalize->AddString( params.m_szCCToken, unicode, captionFile );
+ g_pLocalize->SaveToFile( captionFile );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Combine Sound Events" );
+
+ c = selected.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ selected[ i ]->SetCloseCaptionToken( params.m_szCCToken );
+ }
+
+ PushRedo( "Combine Sound Events" );
+
+ // Redraw
+ InvalidateLayout();
+
+ Con_Printf( "Changed %i events to use close caption token '%s'\n", c, params.m_szCCToken );
+
+ // Sort the sounds by start time
+
+ CUtlRBTree< CChoreoEvent * > sorted( 0, 0, EventStartTimeLessFunc );
+
+ // Sort items
+ c = selected.Count();
+ bool genderwildcard = false;
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoEvent *e = selected[ i ];
+ sorted.Insert( e );
+
+ // Get the sound entry name and use it to look up the gender info
+ // Look up the sound level from the soundemitter system
+ if ( !genderwildcard )
+ {
+ genderwildcard = soundemitter->IsUsingGenderToken( e->GetParameters() );
+ }
+ }
+
+
+ char outfilename[ 512 ];
+ Q_memset( outfilename, 0, sizeof( outfilename ) );
+
+ CChoreoEvent *e = sorted[ sorted.FirstInorder() ];
+
+ // Update whether we use the $gender token
+ e->SetCombinedUsingGenderToken( genderwildcard );
+
+ if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) )
+ {
+ Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound\n" );
+ return;
+ }
+
+ int soundindex = soundemitter->GetSoundIndex( e->GetParameters() );
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ if ( !scriptfile || !scriptfile[0] )
+ {
+ Con_ErrorPrintf( "Unable to find existing script to use for new combined sound entry.\n" );
+ return;
+ }
+
+ // Create a new sound entry for this sound
+ CAddSoundParams asp;
+ Q_memset( &asp, 0, sizeof( asp ) );
+ Q_strncpy( asp.m_szDialogTitle, "Add Combined Sound Entry", sizeof( asp.m_szDialogTitle ) );
+ Q_strncpy( asp.m_szWaveFile, outfilename + Q_strlen( "sound/"), sizeof( asp.m_szWaveFile ) );
+ Q_strncpy( asp.m_szScriptName, scriptfile, sizeof( asp.m_szScriptName ) );
+ Q_strncpy( asp.m_szSoundName, params.m_szCCToken, sizeof( asp.m_szSoundName ) );
+
+ asp.m_bAllowExistingSound = true;
+ asp.m_bReadOnlySoundName = true;
+
+ if ( !AddSound( &asp, (HWND)g_MDLViewer->getHandle() ) )
+ {
+ return;
+ }
+
+ if ( genderwildcard )
+ {
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_MALE, sorted );
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_FEMALE, sorted );
+ }
+ else
+ {
+ GenerateCombinedFile( outfilename, params.m_szCCToken, GENDER_NONE, sorted );
+ }
+}
+
+bool CChoreoView::ValidateCombinedSoundCheckSum( CChoreoEvent *e )
+{
+ if ( !e || e->GetType() != CChoreoEvent::SPEAK )
+ return false;
+
+ bool genderwildcard = e->IsCombinedUsingGenderToken();
+ char outfilename[ 512 ];
+ Q_memset( outfilename, 0, sizeof( outfilename ) );
+ if ( !e->ComputeCombinedBaseFileName( outfilename, sizeof( outfilename ), genderwildcard ) )
+ {
+ Con_ErrorPrintf( "Unable to regenerate wav file name for combined sound (%s)\n", e->GetCloseCaptionToken() );
+ return false;
+ }
+
+ bool checksumvalid = false;
+
+ CUtlRBTree< CChoreoEvent * > eventList( 0, 0, EventStartTimeLessFunc );
+
+ if ( !e->GetChannel()->GetSortedCombinedEventList( e->GetCloseCaptionToken(), eventList ) )
+ {
+ Con_ErrorPrintf( "Unable to generated combined event list (%s)\n", e->GetCloseCaptionToken() );
+ return false;
+ }
+
+
+ if ( genderwildcard )
+ {
+ checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_MALE, eventList );
+ checksumvalid &= ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_FEMALE, eventList );
+ }
+ else
+ {
+ checksumvalid = ValidateCombinedFileCheckSum( outfilename, e->GetCloseCaptionToken(), GENDER_NONE, eventList );
+ }
+
+ return checksumvalid;
+}
+
+void CChoreoView::OnRemoveSpeakEventFromGroup()
+{
+ if ( !m_pScene )
+ return;
+
+ int i, c;
+
+ CUtlVector< CChoreoEvent * > selected;
+ CUtlVector< CChoreoEvent * > processlist;
+ if ( GetSelectedEvents( selected ) > 0 )
+ {
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ m_pClickedChannel->GetMasterAndSlaves( e, processlist );
+ }
+ }
+ else
+ {
+ m_pClickedChannel->GetMasterAndSlaves( m_pClickedChannel->GetCaptionClickedEvent(), processlist );
+ }
+
+ if ( selected.Count() < 1 )
+ {
+ Con_ErrorPrintf( "No eligible SPEAK event selected.\n" );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Remove speak event(s)" );
+
+ c = processlist.Count();
+ for ( i = 0 ; i < c; ++i )
+ {
+ processlist[ i ]->SetCloseCaptionToken( "" );
+ processlist[ i ]->SetCloseCaptionType( CChoreoEvent::CC_MASTER );
+ processlist[ i ]->SetUsingCombinedFile( false );
+ processlist[ i ]->SetRequiredCombinedChecksum( 0 );
+ processlist[ i ]->SetNumSlaves( 0 );
+ processlist[ i ]->SetLastSlaveEndTime( 0.0f );
+ }
+
+ PushRedo( "Remove speak event(s)" );
+
+ // Redraw
+ InvalidateLayout();
+ Con_Printf( "Reverted %i events to use default close caption token\n", c );
+}
+
+bool CChoreoView::AreSelectedEventsCombinable()
+{
+ CUtlVector< CChoreoEvent * > events;
+ if ( GetSelectedEvents( events ) <= 0 )
+ return false;
+
+ CChoreoChannel *firstChannel = NULL;
+
+ CUtlVector< CChoreoEvent * > selected;
+ GetSelectedEvents( selected );
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ return false;
+ }
+
+ if ( !firstChannel )
+ {
+ firstChannel = e->GetChannel();
+ }
+ else if ( e->GetChannel() != firstChannel )
+ {
+ return false;
+ }
+ }
+ return selected.Count() >= 2 ? true : false;
+}
+
+bool CChoreoView::AreSelectedEventsInSpeakGroup()
+{
+ CUtlVector< CChoreoEvent * > selected;
+ if ( GetSelectedEvents( selected ) <= 0 )
+ {
+ if ( m_pClickedChannel )
+ {
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+ if ( e && e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER &&
+ e->GetNumSlaves() >= 1 )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int c = selected.Count();
+ // Find the appropriate event by iterating across all actors and channels
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CChoreoEvent *e = selected[ i ];
+ if ( e->GetType() != CChoreoEvent::SPEAK )
+ {
+ selected.Remove( i );
+ continue;
+ }
+
+ if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ {
+ selected.Remove( i );
+ continue;
+ }
+ }
+
+ return selected.Count() >= 1 ? true : false;
+
+}
+
+void CChoreoView::OnChangeCloseCaptionToken( CChoreoEvent *e )
+{
+ CCloseCaptionLookupParams params;
+ Q_strncpy( params.m_szDialogTitle, "Close Caption Token Lookup", sizeof( params.m_szDialogTitle ) );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ // strcpy( params.m_szPrompt, "Choose model:" );
+
+ Q_strncpy( params.m_szCCToken, e->GetCloseCaptionToken(), sizeof( params.m_szCCToken ) );
+
+ // User hit okay and value actually changed?
+ if ( CloseCaptionLookup( &params ) &&
+ Q_stricmp( e->GetCloseCaptionToken(), params.m_szCCToken ) )
+ {
+ char oldToken[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ Q_strncpy( oldToken, e->GetCloseCaptionToken(), sizeof( oldToken ) );
+
+ CUtlVector< CChoreoEvent * > events;
+ m_pClickedChannel->GetMasterAndSlaves( e, events );
+
+ if ( events.Count() < 2 )
+ {
+ Con_ErrorPrintf( "Can't combine events, must have at least two events selected.\n" );
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Change closecaption token" );
+
+ // Make the change...
+ int c = events.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ events[i]->SetCloseCaptionToken( params.m_szCCToken );
+ }
+
+ PushRedo( "Change closecaption token" );
+
+ InvalidateLayout();
+
+ Con_Printf( "Close Caption token for '%s' changed to '%s'\n", e->GetName(), params.m_szCCToken );
+ }
+}
+
+void CChoreoView::OnToggleCloseCaptionsForEvent()
+{
+ if ( !m_pClickedChannel )
+ {
+ return;
+ }
+
+ CChoreoEvent *e = m_pClickedChannel->GetCaptionClickedEvent();
+
+ if ( !e )
+ {
+ return;
+ }
+
+ CChoreoEvent::CLOSECAPTION newType = CChoreoEvent::CC_MASTER;
+ // Can't mess with slave
+ switch ( e->GetCloseCaptionType() )
+ {
+ default:
+ case CChoreoEvent::CC_SLAVE:
+ return;
+ case CChoreoEvent::CC_MASTER:
+ newType = CChoreoEvent::CC_DISABLED;
+ break;
+ case CChoreoEvent::CC_DISABLED:
+ newType = CChoreoEvent::CC_MASTER;
+ break;
+ }
+
+ SetDirty( true );
+
+ PushUndo( "Enable/disable captions" );
+
+ // Make the change...
+ e->SetCloseCaptionType( newType );
+
+ PushRedo( "Enable/disable captions" );
+
+ InvalidateLayout();
+
+ Con_Printf( "Close Caption type for '%s' changed to '%s'\n", e->GetName(), CChoreoEvent::NameForCCType( newType ) );
+
+}
+
+void CChoreoView::StopScene()
+{
+ SetScrubTargetTime( m_flScrub );
+ FinishSimulation();
+ sound->Flush();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+template <class T>
+void DeleteAllAndPurge( T &tree )
+{
+ T::IndexType_t i;
+
+ for ( i = tree.FirstInorder(); i != T::InvalidIndex(); i = tree.NextInorder( i ) )
+ {
+ delete tree[i];
+ }
+
+ tree.Purge();
+}
+
+void CChoreoView::OnPlaceNextSpeakEvent()
+{
+ CUtlVector< CChoreoEvent * > list;
+ GetSelectedEvents( list );
+ if ( list.Count() != 1 )
+ {
+ Warning( "Can't place sound event, nothing selected\n" );
+ return;
+ }
+
+ CChoreoEvent *ev = list[ 0 ];
+ if ( ev->GetType() != CChoreoEvent::SPEAK )
+ {
+ Warning( "Can't place sound event, no previous sound event selected\n" );
+ return;
+ }
+
+ CChoreoChannelWidget *widget = FindChannelForEvent( ev );
+ if ( !widget )
+ {
+ Warning( "Can't place sound event, can't find channel widget for event\n" );
+ return;
+ }
+
+ CChoreoChannel *channel = widget->GetChannel();
+ if ( !channel )
+ {
+ Warning( "Can't place sound event, can't find channel for new event\n" );
+ return;
+ }
+
+ CUtlRBTree< char const *, int > m_SortedNames( 0, 0, NameLessFunc );
+
+ int c = soundemitter->GetSoundCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ char const *name = soundemitter->GetSoundName( i );
+ if ( name && name[ 0 ] )
+ {
+ m_SortedNames.Insert( strdup( name ) );
+ }
+ }
+
+ int idx = m_SortedNames.Find( ev->GetParameters() );
+ if ( idx == m_SortedNames.InvalidIndex() )
+ {
+ Warning( "Can't place sound event, can't find '%s' in sound list\n", ev->GetParameters() );
+ DeleteAllAndPurge( m_SortedNames );
+ return;
+ }
+
+ int nextIdx = m_SortedNames.NextInorder( idx );
+ if ( nextIdx == m_SortedNames.InvalidIndex() )
+ {
+ Warning( "Can't place sound event, can't next sound after '%s' in sound list\n", ev->GetParameters() );
+ DeleteAllAndPurge( m_SortedNames );
+ return;
+ }
+
+ DeselectAll();
+
+ SetDirty( true );
+
+ PushUndo( "Place Next Speak Event" );
+
+ CChoreoEvent *event = m_pScene->AllocEvent();
+ Assert( event );
+ if ( event )
+ {
+ // Copy everything for source event
+ *event = *ev;
+
+ event->SetParameters( m_SortedNames[ nextIdx ] );
+ // Start it at the end time...
+ event->SetStartTime( event->GetEndTime() );
+ event->SetResumeCondition( false );
+ event->ClearAllRelativeTags();
+ event->ClearAllTimingTags();
+ event->ClearAllAbsoluteTags( CChoreoEvent::PLAYBACK );
+ event->ClearAllAbsoluteTags( CChoreoEvent::ORIGINAL );
+
+ event->SetChannel( channel );
+ event->SetActor( channel->GetActor() );
+
+ // Try and load wav to get length
+ CAudioSource *wave = sound->LoadSound( va( "sound/%s", FacePoser_TranslateSoundName( event ) ) );
+ if ( wave )
+ {
+ event->SetEndTime( event->GetStartTime() + wave->GetRunningLength() );
+ delete wave;
+ }
+
+ DeleteSceneWidgets();
+
+ // Add to appropriate channel
+ channel->AddEvent( event );
+
+ CreateSceneWidgets();
+
+ CChoreoEventWidget *eventWidget = FindWidgetForEvent( event );
+ if ( eventWidget )
+ {
+ eventWidget->SetSelected( true );
+ }
+
+ // Redraw
+ InvalidateLayout();
+ }
+
+ PushRedo( "Place Next Speak Event" );
+
+ DeleteAllAndPurge( m_SortedNames );
+}
+
+enum
+{
+ FM_LEFT = 0,
+ FM_RIGHT,
+ FM_SMALLESTWIDE,
+ FM_LARGESTWIDE
+};
+
+static int FindMetric( int type, CUtlVector< CChoreoEvent * > &list, float& value )
+{
+ float bestVal = 999999.0f;
+ int bestIndex = -1;
+ bool greater = true;
+
+ switch ( type )
+ {
+ default:
+ case FM_LEFT:
+ case FM_SMALLESTWIDE:
+ greater = false;
+ break;
+ case FM_RIGHT:
+ case FM_LARGESTWIDE:
+ bestVal = -bestVal;
+ greater = true;
+ break;
+ }
+ int c = list.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CChoreoEvent *e = list[ i ];
+ if ( type != FM_LEFT &&
+ !e->HasEndTime() )
+ continue;
+
+ float val;
+ switch ( type )
+ {
+ default:
+ case FM_LEFT:
+ val = e->GetStartTime();
+ break;
+ case FM_RIGHT:
+ val = e->GetEndTime();
+ break;
+ case FM_SMALLESTWIDE:
+ case FM_LARGESTWIDE:
+ val = e->GetDuration();
+ break;
+ }
+
+ if ( greater )
+ {
+ if ( val <= bestVal )
+ continue;
+ }
+ else
+ {
+ if ( val >= bestVal )
+ continue;
+ }
+
+ bestVal = val;
+ bestIndex = i;
+ }
+
+ value = bestVal;
+ return bestIndex;
+}
+
+void CChoreoView::OnAlign( bool left )
+{
+ CUtlVector< CChoreoEvent * > list;
+ GetSelectedEvents( list );
+
+ if ( left )
+ {
+ for ( int i = 0; i < m_SceneGlobalEvents.Size(); i++ )
+ {
+ CChoreoGlobalEventWidget *event = m_SceneGlobalEvents[ i ];
+ if ( !event || !event->IsSelected() )
+ continue;
+
+ list.AddToTail( event->GetEvent() );
+ }
+ }
+
+ int numSel = list.Count();
+ if ( numSel < 2 )
+ {
+ Warning( "Can't align, must have at least two events selected\n" );
+ return;
+ }
+
+ float value;
+ int idx = FindMetric( left ? FM_LEFT : FM_RIGHT, list, value );
+ if ( idx == -1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 128 ];
+ Q_snprintf( undotext, sizeof( undotext ), "Align %s", left ? "Left" : "Right" );
+ PushUndo( undotext );
+
+ for ( int i = 0; i < numSel; ++i )
+ {
+ if ( i == idx )
+ continue;
+
+ CChoreoEvent *e = list[ i ];
+
+ float newStartTime = left ? value : ( value - e->GetDuration() );
+ float offset = newStartTime - e->GetStartTime();
+ e->OffsetTime( offset );
+ }
+
+ PushRedo( undotext );
+
+ InvalidateLayout();
+}
+
+void CChoreoView::OnMakeSameSize( bool smallest )
+{
+ CUtlVector< CChoreoEvent * > list;
+ int numSel = GetSelectedEvents( list );
+ if ( numSel < 2 )
+ {
+ Warning( "Can't align, must have at least two events selected\n" );
+ return;
+ }
+
+ float value;
+ int idx = FindMetric( smallest ? FM_SMALLESTWIDE : FM_LARGESTWIDE, list, value );
+ if ( idx == -1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ char undotext[ 128 ];
+ Q_snprintf( undotext, sizeof( undotext ), "Size to %s", smallest ? "Smallest" : "Largest" );
+ PushUndo( undotext );
+
+ for ( int i = 0; i < numSel; ++i )
+ {
+ if ( i == idx )
+ continue;
+
+ list[ i ]->SetEndTime( list[ i ]->GetStartTime() + value );
+ }
+
+ PushRedo( undotext );
+
+ InvalidateLayout();
+}
+
+void CChoreoView::SelectAllEventsInActor( CChoreoActorWidget *actor )
+{
+ TraverseWidgets( &CChoreoView::SelectInActor, actor );
+ redraw();
+}
+
+void CChoreoView::SelectAllEventsInChannel( CChoreoChannelWidget *channel )
+{
+ TraverseWidgets( &CChoreoView::SelectInChannel, channel );
+ redraw();
+}
+
+void CChoreoView::SelectInActor( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget );
+ if ( !ev )
+ return;
+
+ if ( ev->IsSelected() )
+ return;
+
+ CChoreoChannel *ch = ev->GetEvent()->GetChannel();
+ if ( !ch )
+ return;
+ CChoreoActor *actor = ch->GetActor();
+ if ( !actor )
+ return;
+
+ CChoreoActorWidget *actorw = dynamic_cast< CChoreoActorWidget * >( param1 );
+ if ( !actorw )
+ return;
+
+ if ( actorw->GetActor() != actor )
+ return;
+
+ widget->SetSelected( true );
+}
+
+void CChoreoView::SelectInChannel( CChoreoWidget *widget, CChoreoWidget *param1 )
+{
+ CChoreoEventWidget *ev = dynamic_cast< CChoreoEventWidget * >( widget );
+ if ( !ev )
+ return;
+
+ if ( ev->IsSelected() )
+ return;
+
+ CChoreoChannel *ch = ev->GetEvent()->GetChannel();
+ if ( !ch )
+ return;
+
+ CChoreoChannelWidget *chw = dynamic_cast< CChoreoChannelWidget * >( param1 );
+ if ( !chw )
+ return;
+
+ if ( chw->GetChannel() != ch )
+ return;
+
+ widget->SetSelected( true );
+}
+
diff --git a/utils/hlfaceposer/choreoview.h b/utils/hlfaceposer/choreoview.h
new file mode 100644
index 0000000..bd741c1
--- /dev/null
+++ b/utils/hlfaceposer/choreoview.h
@@ -0,0 +1,832 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOVIEW_H
+#define CHOREOVIEW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include <mxtk/mx.h>
+#include <mxtk/mxWindow.h>
+#include "mxBitmapButton.h"
+#include "utlvector.h"
+#include "ChoreoWidget.h"
+#include "ichoreoeventcallback.h"
+#include "faceposertoolwindow.h"
+#include "ChoreoEvent.h"
+#include "mathlib/mathlib.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+#define IDC_CV_CC_LANGUAGESTART 5300
+
+#define IDC_STOPSCENE 5000
+#define IDC_PLAYSCENE 5001
+#define IDC_PAUSESCENE 5002
+#define IDC_CHOREOVSCROLL 5003
+#define IDC_CHOREOHSCROLL 5004
+
+#define IDC_ADDACTOR 5005
+#define IDC_DELETEACTOR 5006
+#define IDC_MOVEACTORUP 5007
+#define IDC_MOVEACTORDOWN 5008
+#define IDC_EDITACTOR 5009
+
+#define IDC_EDITEVENT 5010
+#define IDC_DELETEEVENT 5011
+
+#define IDC_ADDEVENT_EXPRESSION 5012
+#define IDC_ADDEVENT_GESTURE 5013
+#define IDC_ADDEVENT_LOOKAT 5014
+#define IDC_ADDEVENT_MOVETO 5015
+#define IDC_ADDEVENT_SPEAK 5016
+#define IDC_ADDEVENT_FACE 5017
+#define IDC_ADDEVENT_FIRETRIGGER 5018
+#define IDC_ADDEVENT_SEQUENCE 5019
+#define IDC_ADDEVENT_GENERIC 5020
+
+#define IDC_CV_CHANGESCALE 5021
+
+#define IDC_EDITGLOBALEVENT 5022
+#define IDC_DELETEGLOBALEVENT 5023
+#define IDC_ADDEVENT_PAUSE 5024
+
+#define IDC_ADDCHANNEL 5025
+#define IDC_EDITCHANNEL 5026
+#define IDC_DELETECHANNEL 5027
+#define IDC_MOVECHANNELUP 5028
+#define IDC_MOVECHANNELDOWN 5029
+
+#define IDC_CHANNELOPEN 5030
+#define IDC_CHANNELCLOSE 5031
+#define IDC_CBACTORACTIVE 5032
+#define IDC_DELETERELATIVETAG 5033
+#define IDC_ADDTIMINGTAG 5034
+
+#define IDC_SELECTALL 5035
+#define IDC_DESELECTALL 5036
+#define IDC_MOVETOBACK 5037
+
+#define IDC_UNDO 5038
+#define IDC_REDO 5039
+
+#define IDC_EXPRESSIONTOOL 5040
+#define IDC_ASSOCIATEBSP 5041
+
+#define IDC_ADDEVENT_FLEXANIMATION 5042
+
+#define IDC_COPYEVENTS 5043
+#define IDC_PASTEEVENTS 5044
+
+#define IDC_IMPORTEVENTS 5045
+#define IDC_EXPORTEVENTS 5046
+
+#define IDC_ADDEVENT_SUBSCENE 5047
+#define IDC_PLAYSCENE_BACKWARD 5048
+
+#define IDC_ASSOCIATEMODEL 5049
+#define IDC_CHOREO_PLAYBACKRATE 5050
+
+#define IDC_CV_CHECKSEQLENGTHS 5051
+#define IDC_CV_PROCESSSEQUENCES 5052
+
+#define IDC_GESTURETOOL 5053
+
+#define IDC_ADDEVENT_LOOP 5054
+#define IDC_CV_TOGGLERAMPONLY 5055
+
+#define IDC_ADDEVENT_INTERRUPT 5056
+#define IDC_ADDEVENT_STOPPOINT 5067
+
+#define SCENE_ACTION_UNKNOWN 0
+#define SCENE_ACTION_CANCEL 1
+#define SCENE_ACTION_RESUME 2
+
+#define SCENE_ANIMLAYER_SEQUENCE 0
+#define SCENE_ANIMLAYER_GESTURE 1
+
+#define IDC_ADDEVENT_NULLGESTURE 5068
+
+#define IDC_SELECTEVENTS_ALL_BEFORE 5069
+#define IDC_SELECTEVENTS_ALL_AFTER 5070
+#define IDC_SELECTEVENTS_ACTIVE_BEFORE 5071
+#define IDC_SELECTEVENTS_ACTIVE_AFTER 5072
+#define IDC_SELECTEVENTS_CHANNEL_BEFORE 5073
+#define IDC_SELECTEVENTS_CHANNEL_AFTER 5074
+
+#define IDC_INSERT_TIME 5075
+#define IDC_DELETE_TIME 5076
+
+#define IDC_EXPORT_VCD 5077
+#define IDC_IMPORT_VCD 5078
+
+#define IDC_ADDEVENT_PERMITRESPONSES 5079
+
+#define IDC_CV_CC_SHOW 5080
+#define IDC_CV_COMBINESPEAKEVENTS 5081
+#define IDC_CV_REMOVESPEAKEVENTFROMGROUP 5082
+#define IDC_CV_CHANGECLOSECAPTIONTOKEN 5083
+#define IDC_CV_TOGGLECLOSECAPTIONS 5084
+
+#define IDC_CV_ALIGN_LEFT 5085
+#define IDC_CV_ALIGN_RIGHT 5086
+#define IDC_CV_SAMESIZE_SMALLEST 5087
+#define IDC_CV_SAMESIZE_LARGEST 5088
+
+#define IDC_CV_ALLEVENTS_CHANNEL 5089
+#define IDC_CV_ALLEVENTS_ACTOR 5090
+
+#define IDC_CV_ENABLEEVENTS 5091
+#define IDC_CV_DISABLEEVENTS 5092
+
+/////////////////////////////////////////////////////////////////////////////
+// CChoreoView window
+class CChoreoScene;
+class CChoreoEvent;
+class CChoreoActor;
+class CChoreoChannel;
+
+class CChoreoActorWidget;
+class CChoreoChannelWidget;
+class CChoreoEventWidget;
+class CChoreoGlobalEventWidget;
+class CChoreoWidgetDrawHelper;
+
+class PhonemeEditor;
+class CExpression;
+class CExpClass;
+class StudioModel;
+
+//-----------------------------------------------------------------------------
+// Purpose: The main view of the choreography data for a scene
+//-----------------------------------------------------------------------------
+class CChoreoView : public mxWindow, public IFacePoserToolWindow, public IChoreoEventCallback
+{
+// Construction
+public:
+ CChoreoView( mxWindow *parent, int x, int y, int w, int h, int id );
+ virtual ~CChoreoView();
+
+ virtual void OnModelChanged();
+
+ virtual void OnDelete();
+ virtual bool CanClose();
+
+ void InvalidateTrackLookup( void );
+
+ // Implements IChoreoEventCallback
+ virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual void EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual void ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual bool CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+
+ void SetChoreoFile( char const *filename );
+ char const *GetChoreoFile( void ) const;
+
+ // Scene load/unload
+ void LoadSceneFromFile( const char *filename );
+ CChoreoScene *LoadScene( char const *filename );
+ void UnloadScene( void );
+
+ // UI
+ void New( void );
+ void Save( void );
+ void SaveAs( void );
+ void Load( void );
+ void LoadNext( void );
+ bool Close( void );
+
+ // Drag/drop from expression thumbnail
+
+ bool CreateExpressionEvent( int mx, int my, CExpClass *cl, CExpression *exp );
+ bool CreateAnimationEvent( int mx, int my, char const *animationname );
+
+ void SelectAll( void );
+ void DeselectAll( void );
+
+ struct SelectionParams_t
+ {
+ enum
+ {
+ SP_CHANNEL = 0,
+ SP_ACTIVE,
+ SP_ALL
+ };
+
+ float time;
+ bool forward;
+ int type;
+ };
+
+ void SelectEvents( SelectionParams_t& params );
+
+ // Channel/actor right click menu
+ void NewChannel( void );
+ void DeleteChannel( CChoreoChannel *channel );
+ void MoveChannelUp( CChoreoChannel *channel );
+ void MoveChannelDown( CChoreoChannel *channel );
+ void EditChannel( CChoreoChannel *channel );
+ void AddEvent( int type, int subtype = 0, char const *defaultparameters = NULL );
+
+ // Actor right click menu
+ void NewActor( void );
+ void DeleteActor( CChoreoActor *actor );
+ void MoveActorUp( CChoreoActor *actor );
+ void MoveActorDown( CChoreoActor *actor );
+ void EditActor( CChoreoActor *actor );
+
+ // Event menu
+ void EditEvent( CChoreoEvent *event );
+ void DeleteSelectedEvents( void );
+ void EnableSelectedEvents( bool state );
+ void DeleteEventRelativeTag( CChoreoEvent *event, int tagnum );
+ void AddEventRelativeTag( void );
+ CChoreoEventWidget *FindWidgetForEvent( CChoreoEvent *event );
+ CChoreoChannelWidget *FindChannelForEvent( CChoreoEvent *event );
+
+ void MoveEventToBack( CChoreoEvent *event );
+ void OnExpressionTool( void );
+ void OnGestureTool( void );
+
+ // Global event ( pause ) menu
+ void EditGlobalEvent( CChoreoEvent *event );
+ void DeleteGlobalEvent( CChoreoEvent *event );
+ void AddGlobalEvent( CChoreoEvent::EVENTTYPE type );
+
+ void AssociateBSP( void );
+ void AssociateModel( void );
+ void AssociateModelToActor( CChoreoActor *actor, int modelindex );
+
+ // UI Layout
+ void CreateSceneWidgets( void );
+ void DeleteSceneWidgets( void );
+ void LayoutScene( void );
+ void InvalidateLayout( void );
+ void ForceScrollBarsToRecompute( bool resetthumb );
+
+ // Layout data that children should obey
+ int GetLabelWidth( void );
+ int GetStartRow( void );
+ int GetRowHeight( void );
+ int GetFontSize( void );
+ int GetEndRow( void );
+
+ // Simulation
+ void PlayScene( bool forward );
+ void PauseScene( void );
+
+ bool IsPlayingScene( void );
+ void ResetTargetSettings( void );
+ void UpdateCurrentSettings( void );
+
+ virtual void Think( float dt );
+ virtual bool IsScrubbing( void ) const;
+ virtual bool IsProcessing( void );
+
+
+ bool ShouldProcessSpeak( void );
+
+ void SceneThink( float time );
+ void PauseThink( void );
+ void FinishSimulation( void );
+ void StopScene();
+
+ float GetStartTime( void );
+ float GetEndTime( void );
+ void SetStartTime( float time );
+
+ void ProcessExpression( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessFlexAnimation( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessLookat( CChoreoScene *scene, CChoreoEvent *event );
+ bool GetTarget( CChoreoScene *scene, CChoreoEvent *event, Vector &vecTarget, QAngle &vecAngle );
+ void ProcessFace( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessGesture( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessSequence( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessMoveto( CChoreoScene *scene, CChoreoEvent *event );
+ int GetMovetoSequence( CChoreoScene *scene, CChoreoEvent *event, StudioModel *model );
+ void ProcessSubscene( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessPause( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessSpeak( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessLoop( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessInterrupt( CChoreoScene *scene, CChoreoEvent *event );
+ void ProcessPermitResponses( CChoreoScene *scene, CChoreoEvent *event );
+
+ float GetPixelsPerSecond( void );
+
+ // mxWindow overrides
+ virtual void redraw();
+ virtual bool PaintBackground( void );
+ virtual int handleEvent( mxEvent *event );
+
+ // Draw helpers
+ void DrawSceneABTicks( CChoreoWidgetDrawHelper& drawHelper );
+ void DrawTimeLine( CChoreoWidgetDrawHelper& drawHelper, RECT& rc, float left, float right );
+ void DrawBackground( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+ void DrawRelativeTagLines( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+
+ // Remap click position to/from time value
+ float GetTimeValueForMouse( int mx, bool clip = false );
+ int GetPixelForTimeValue( float time, bool *clipped = NULL );
+ float GetTimeDeltaForMouseDelta( int mx, int origmx );
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+
+ // Dirty flag for file save prompting
+
+ bool GetDirty( void );
+ void SetDirty( bool dirty, bool clearundo = true );
+
+ void ShowContextMenu( int mx, int my );
+
+ // Caller must first translate mouse into screen coordinates
+ bool IsMouseOverTimeline( int mx, int my );
+
+ CChoreoActorWidget *GetActorUnderCursorPos( int mx, int my );
+ CChoreoChannelWidget *GetChannelUnderCursorPos( int mx, int my );
+ CChoreoEventWidget *GetEventUnderCursorPos( int mx, int my );
+ CChoreoGlobalEventWidget *GetGlobalEventUnderCursorPos( int mx, int my );
+ int GetTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my );
+ CEventAbsoluteTag *GetAbsoluteTagUnderCursorPos( CChoreoEventWidget *event, int mx, int my );
+
+
+ void GetObjectsUnderMouse( int mx, int my,
+ CChoreoActorWidget **actor,
+ CChoreoChannelWidget **channel,
+ CChoreoEventWidget **event,
+ CChoreoGlobalEventWidget **globalevent,
+ int* clickedTag,
+ CEventAbsoluteTag **absolutetag,
+ int *clickedCCButton );
+
+ void OnDoubleClicked( void );
+
+ void ApplyBounds( int& mx, int& my );
+ void CalcBounds( int movetype );
+
+ void MouseStartDrag( mxEvent *event, int mx, int my );
+ void MouseContinueDrag( mxEvent *event, int mx, int my );
+ void MouseFinishDrag( mxEvent *event, int mx, int my );
+ void MouseMove( int mx, int my );
+
+ //void StartDraggingGlobalEvent( int mx, int my );
+ void StartDraggingEvent( int mx, int my );
+ //void FinishDraggingGlobalEvent( int mx, int my );
+ void FinishDraggingEvent( mxEvent *event, int mx, int my );
+
+ // Draw focus rect while mouse dragging is going on
+ void DrawFocusRect( void );
+ int ComputeEventDragType( int mx, int my );
+
+ void SetCurrentWaveFile( const char *filename, CChoreoEvent *event );
+
+ void RecomputeWaves();
+
+ typedef void (CChoreoView::*CVMEMBERFUNC)( CChoreoWidget *widget, CChoreoWidget *param1 );
+
+ void TraverseWidgets( CVMEMBERFUNC pfn, CChoreoWidget *param1 );
+
+ int CountSelectedEvents( void );
+ int CountSelectedGlobalEvents( void );
+ int GetSelectedEvents( CUtlVector< CChoreoEvent * >& events );
+ int GetSelectedEventWidgets( CUtlVector< CChoreoEventWidget * >& events );
+ int GetEarliestEventIndex( CUtlVector< CChoreoEventWidget * >& events );
+ int GetLatestEventIndex( CUtlVector< CChoreoEventWidget * >& events );
+
+ // Traversal functions
+ void Deselect( CChoreoWidget *widget, CChoreoWidget *param1 );
+ void Select( CChoreoWidget *widget, CChoreoWidget *param1 );
+ void SelectInActor( CChoreoWidget *widget, CChoreoWidget *param1 );
+ void SelectInChannel( CChoreoWidget *widget, CChoreoWidget *param1 );
+
+ void SelectAllEvents( CChoreoWidget *widget, CChoreoWidget *param1 );
+
+ void SelectAllEventsInActor( CChoreoActorWidget *actor );
+ void SelectAllEventsInChannel( CChoreoChannelWidget *channel );
+
+ // Adjust scroll bars
+ void RepositionVSlider( void );
+ void RepositionHSlider( void );
+
+ void UpdateStatusArea( int mx, int my );
+ void ClearStatusArea( void );
+ void RedrawStatusArea( CChoreoWidgetDrawHelper& drawHelper, RECT& rcStatus );
+
+ void GetUndoLevels( int& current, int& number );
+
+ // Undo/Redo
+ void Undo( void );
+ void Redo( void );
+
+ bool CanUndo();
+ bool CanRedo();
+
+ // Do push before changes
+ void PushUndo( const char *description );
+ // Do this push after changes, must match pushundo 1for1
+ void PushRedo( const char *description );
+
+ void WipeUndo( void );
+ void WipeRedo( void );
+
+ const char *GetUndoDescription( void );
+ const char *GetRedoDescription( void );
+
+ CChoreoScene *GetScene( void );
+
+ void ReportSceneClearToTools( void );
+
+ void CopyEvents( void );
+ void PasteEvents( void );
+ void ImportEvents( void );
+ void ExportEvents( void );
+
+ void ExportVCD();
+ void ImportVCD();
+
+ void ExportVCDFile( char const *filename );
+ void ImportVCDFile( char const *filename );
+
+ bool CanPaste( void );
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ bool IsMouseOverScrubArea( mxEvent *event );
+
+ void GetScrubHandleRect( RECT& rcHandle, bool clipped = false );
+ void GetScrubAreaRect( RECT& rcArea );
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper );
+ void DrawScrubHandle( void );
+ void ScrubThink( float dt, bool scrubbing, IFacePoserToolWindow *invoker );
+
+ void ClampTimeToSelectionInterval( float& timeval );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+
+ void OnCheckSequenceLengths( void );
+
+ bool IsRampOnly( void ) const;
+
+ void SetTimeZoom( const char *tool, int tz, bool preserveFocus );
+ int GetTimeZoom( const char *tool );
+ template< class T >
+ void SetPreservedTimeZoom( T *other, int tz );
+ template< class T >
+ int HandleZoomKey( T *other, int keyCode );
+
+ void OnInsertTime();
+ void OnDeleteTime();
+
+ bool GetShowCloseCaptionData( void ) const;
+ bool ValidateCombinedSoundCheckSum( CChoreoEvent *e );
+
+ void OnPlaceNextSpeakEvent();
+
+private:
+
+ void CheckInsertTime( CChoreoEvent *e, float dt, float starttime, float endtime );
+ void CheckDeleteTime( CChoreoEvent *d, float dt, float starttime, float endtime, bool& deleteEvent );
+
+ bool FixupSequenceDurations( CChoreoScene *scene, bool checkonly );
+ bool CheckSequenceLength( CChoreoEvent *e, bool checkonly );
+ bool CheckGestureLength( CChoreoEvent *e, bool checkonly );
+ bool DefaultGestureLength( CChoreoEvent *e, bool checkonly );
+ bool AutoaddGestureKeys( CChoreoEvent *e, bool checkonly );
+
+ void InvalidateTrackLookup_R( CChoreoScene *scene );
+
+ void ShowButtons( bool show );
+
+ // Compute full size of data in pixels, for setting up scroll bars
+ int ComputeVPixelsNeeded( void );
+ int ComputeHPixelsNeeded( void );
+ void PositionControls();
+
+ void OnChangeScale();
+
+ float FindNextEventTime( CChoreoEvent::EVENTTYPE type, CChoreoChannel *channel, CChoreoEvent *e, bool forward );
+
+ bool ShouldSelectEvent( SelectionParams_t &params, CChoreoEvent *event );
+
+ bool IsMouseOverSceneEndTime( int mx );
+
+ void StartDraggingSceneEndTime( int mx, int my );
+ void FinishDraggingSceneEndTime( mxEvent *event, int mx, int my );
+
+ void SetShowCloseCaptionData( bool show );
+
+ void OnToggleCloseCaptionTags();
+ void OnCombineSpeakEvents();
+ void OnRemoveSpeakEventFromGroup();
+ void OnChangeCloseCaptionToken( CChoreoEvent *e );
+
+ bool AreSelectedEventsCombinable();
+ bool AreSelectedEventsInSpeakGroup();
+
+ void OnToggleCloseCaptionsForEvent();
+
+ bool GenerateCombinedFile( char const *outfilename, const char *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted );
+ bool ValidateCombinedFileCheckSum( char const *outfilename, char const *cctoken, gender_t gender, CUtlRBTree< CChoreoEvent * >& sorted );
+
+ void RememberSelectedEvents( CUtlVector< CChoreoEvent * >& list );
+ void ReselectEvents( CUtlVector< CChoreoEvent * >& list );
+
+ void OnAlign( bool left );
+ void OnMakeSameSize( bool smallest );
+
+ bool IsMouseOverEventEdge( CChoreoEventWidget *ew, bool bLeftEdge, int mx, int my );
+ bool IsMouseOverEvent( CChoreoEventWidget *ew, int mx, int my );
+ typedef struct
+ {
+ bool active;
+ float time;
+ } SCENEAB;
+
+ void PlaceABPoint( int mx );
+ void ClearABPoints( void );
+
+ SCENEAB m_rgABPoints[ 2 ];
+ int m_nCurrentABPoint;
+
+ bool m_bForward;
+
+ // The underlying scene we are editing
+ CChoreoScene *m_pScene;
+
+ enum
+ {
+ MAX_ACTORS = 32
+ };
+
+ typedef struct
+ {
+ bool expanded;
+ } ACTORSTATE;
+
+ ACTORSTATE m_ActorExpanded[ MAX_ACTORS ];
+ // The scene's ui actors
+ CUtlVector < CChoreoActorWidget * > m_SceneActors;
+ // The scenes segment markers
+ CUtlVector < CChoreoGlobalEventWidget * > m_SceneGlobalEvents;
+
+ // How many pixels per second we are showing in the UI
+ float m_flPixelsPerSecond;
+
+ // Do we need to move controls?
+ bool m_bLayoutIsValid;
+
+ // Starting row of first actor
+ int m_nStartRow;
+ // How wide the actor/channel name area is
+ int m_nLabelWidth;
+ // Height between channel/actor names
+ int m_nRowHeight;
+ // Font size for drawing event labels
+ int m_nFontSize;
+
+ // Height/width of scroll bars
+ int m_nScrollbarHeight;
+ // Height off info area for flyover info / help
+ int m_nInfoHeight;
+
+ // Simulation info
+ float m_flStartTime;
+ float m_flEndTime;
+
+ float m_flFrameTime;
+ bool m_bSimulating;
+ bool m_bPaused;
+ float m_flLastSpeedScale;
+ bool m_bResetSpeedScale;
+ bool m_bAutomated;
+ int m_nAutomatedAction;
+ float m_flAutomationDelay;
+ float m_flAutomationTime;
+
+ // Some rectangles for the UI
+ RECT m_rcTitles;
+ RECT m_rcTimeLine;
+
+ // Play/pause buttons for simulation
+ mxBitmapButton *m_btnPlay;
+ mxBitmapButton *m_btnPause;
+ mxBitmapButton *m_btnStop;
+
+ mxSlider *m_pPlaybackRate;
+ float m_flPlaybackRate;
+
+ // The scroll bars
+ mxScrollbar *m_pVertScrollBar;
+ mxScrollbar *m_pHorzScrollBar;
+ int m_nLastHPixelsNeeded;
+ int m_nLastVPixelsNeeded;
+
+ // Current sb values
+ int m_nTopOffset;
+ float m_flLeftOffset;
+
+ // Need save?
+ bool m_bDirty;
+ // Currently loaded scene file
+ char m_szChoreoFile[ 256 ];
+
+ CChoreoActorWidget *m_pClickedActor;
+ CChoreoChannelWidget *m_pClickedChannel;
+ CChoreoEventWidget *m_pClickedEvent;
+ CChoreoGlobalEventWidget *m_pClickedGlobalEvent;
+ // Relative to the clicked event
+ int m_nClickedTag;
+ CEventAbsoluteTag *m_pClickedAbsoluteTag;
+ int m_nSelectedEvents;
+ int m_nClickedX, m_nClickedY;
+ int m_nClickedChannelCloseCaptionButton;
+
+ // For mouse dragging
+ // How close to right or left edge the mouse has to be before the wider/shorten
+ // cursor shows up
+ enum
+ {
+ DRAG_EVENT_EDGE_TOLERANCE = 5,
+ };
+
+ // When dragging, what time of action is being performed
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_EVENT_MOVE,
+ DRAGTYPE_EVENT_STARTTIME,
+ DRAGTYPE_EVENT_STARTTIME_RESCALE,
+ DRAGTYPE_EVENT_ENDTIME,
+ DRAGTYPE_EVENT_ENDTIME_RESCALE,
+ DRAGTYPE_EVENTTAG_MOVE,
+ DRAGTYPE_EVENTABSTAG_MOVE,
+ DRAGTYPE_SCRUBBER,
+ DRAGTYPE_SCENE_ENDTIME,
+ DRAGTYPE_RESCALELEFT,
+ DRAGTYPE_RESCALERIGHT,
+ };
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ bool m_bDragging;
+ int m_xStart;
+ int m_yStart;
+
+ bool m_bUseBounds;
+ int m_nMinX;
+ int m_nMaxX;
+
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+
+ int m_nDragType;
+ HCURSOR m_hPrevCursor;
+
+ struct FLYOVER
+ {
+ CChoreoActorWidget *a;
+ CChoreoChannelWidget *c;
+ CChoreoEventWidget *e;
+ CChoreoGlobalEventWidget *ge;
+ int tag;
+ CEventAbsoluteTag *at;
+ int ccbutton;
+ };
+
+ FLYOVER m_Flyover;
+
+ bool m_bCanDraw;
+
+ struct CVUndo
+ {
+ CChoreoScene *undo;
+ CChoreoScene *redo;
+ char *udescription;
+ char *rdescription;
+ };
+
+ CUtlVector< CVUndo * > m_UndoStack;
+ int m_nUndoLevel;
+ bool m_bRedoPending;
+
+ bool m_bProcessSequences;
+
+ float m_flLastMouseClickTime;
+
+ bool m_bSuppressLayout;
+
+ bool m_bRampOnly;
+ float m_flScrubberTimeOffset;
+
+ bool m_bShowCloseCaptionData;
+
+ bool m_bForceProcess;
+
+ // cached version of the local directory when a scene is loaded
+ CUtlVector< CUtlString > m_nextFileList;
+};
+
+extern CChoreoView *g_pChoreoView;
+
+template< class T >
+void CChoreoView::SetPreservedTimeZoom( T *other, int tz )
+{
+ POINT pt;
+ ::GetCursorPos( &pt );
+ ::ScreenToClient( (HWND)other->getHandle(), &pt );
+
+ // Now figure out time under cursor at old zoom scale
+ float t = other->GetTimeValueForMouse( pt.x, true );
+
+ // Call CChoreoView's version
+ SetTimeZoom( other->GetToolName(), tz, false );
+
+ // Now figure out tie under pt.x
+ float newT = other->GetTimeValueForMouse( pt.x, true );
+ if ( newT != t )
+ {
+ // We need to scroll over a bit
+ float pps = other->GetPixelsPerSecond();
+ float movePixels = pps * ( newT - t );
+
+ float newOffset = other->m_flLeftOffset - movePixels;
+ if ( newOffset < 0.0f )
+ {
+ newOffset = 0;
+ }
+
+ float ed = other->GetEventEndTime();
+ float flLastPixel = ed * pps;
+ if ( newOffset + other->w2() > flLastPixel )
+ {
+ newOffset = flLastPixel - other->w2();
+ }
+
+ other->m_flLeftOffset = newOffset;
+ other->m_pHorzScrollBar->setValue( (int)( other->m_flLeftOffset ) );
+ }
+
+ other->RepositionHSlider();
+}
+
+template< class T >
+int CChoreoView::HandleZoomKey( T *other, int keyCode )
+{
+ int iret = 1;
+ switch ( keyCode )
+ {
+ default:
+ {
+ iret = 0;
+ }
+ break;
+
+ case VK_HOME:
+ {
+ other->MoveTimeSliderToPos( 0 );
+ }
+ break;
+ case VK_END:
+ {
+ float maxtime = other->GetEventEndTime();
+ int pixels = max( 0, (int)( maxtime * other->GetPixelsPerSecond() ) - other->w2() );
+ other->MoveTimeSliderToPos( pixels );
+ }
+ break;
+ case VK_PRIOR: // PgUp
+ {
+ int window = other->w2();
+ other->m_flLeftOffset = max( other->m_flLeftOffset - (float)window, 0.0f );
+ other->MoveTimeSliderToPos( (int)other->m_flLeftOffset );
+ }
+ break;
+ case VK_NEXT: // PgDown
+ {
+ int window = other->w2();
+ float maxtime = other->GetEventEndTime();
+ int pixels = max( 0, (int)( maxtime * other->GetPixelsPerSecond() ) - other->w2() );
+ other->m_flLeftOffset = min( other->m_flLeftOffset + (float)window, (float)pixels );
+ other->MoveTimeSliderToPos( (int)other->m_flLeftOffset );
+ }
+ break;
+ }
+
+ return iret;
+}
+
+
+float SnapTime( float input, float granularity );
+
+class StudioModel;
+StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a );
+
+#endif // CHOREOVIEW_H
diff --git a/utils/hlfaceposer/choreoviewcolors.h b/utils/hlfaceposer/choreoviewcolors.h
new file mode 100644
index 0000000..1492a93
--- /dev/null
+++ b/utils/hlfaceposer/choreoviewcolors.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CHOREOVIEWCOLORS_H
+#define CHOREOVIEWCOLORS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+// Defines colors for choreo view
+#define COLOR_CHOREO_BACKGROUND RGB( 240, 240, 220 )
+#define COLOR_CHOREO_DARKBACKGROUND RGB( 230, 230, 200 )
+#define COLOR_CHOREO_TEXT RGB( 0, 0, 0 )
+#define COLOR_CHOREO_LIGHTTEXT RGB( 180, 180, 120 )
+
+#define COLOR_CHOREO_EVENT RGB( 250, 100, 100 )
+#define COLOR_CHOREO_EVENT_TRIGGERED RGB( 32, 130, 150 )
+#define COLOR_CHOREO_EVENT_SELECTED RGB( 220, 150, 150 )
+
+#define COLOR_CHOREO_DIVIDER RGB( 63, 63, 63 )
+#define COLOR_CHOREO_ACTORNAME RGB( 80, 150, 150 )
+#define COLOR_CHOREO_ACTORNAME_INACTIVE RGB( 150, 150, 150 )
+#define COLOR_CHOREO_ACTORLINE RGB( 200, 200, 175 )
+#define COLOR_CHOREO_CHANNELNAME RGB( 150, 150, 100 )
+#define COLOR_CHOREO_CHANNELLINE RGB( 63, 63, 31 )
+#define COLOR_CHOREO_SEGMENTDIVIDER RGB( 63, 120, 255 )
+#define COLOR_CHOREO_SEGMENTDIVIDER_BG RGB( 170, 190, 230 )
+
+#define COLOR_CHOREO_LOOPPOINT RGB( 255, 120, 255 )
+#define COLOR_CHOREO_LOOPPOINT_BG RGB( 255, 100, 150 )
+#define COLOR_CHOREO_LOOPPOINT_START_BG RGB( 255, 150, 150 )
+
+#define COLOR_CHOREO_STOPPOINT RGB( 255, 31, 31 )
+#define COLOR_CHOREO_STOPPOINT_BG RGB( 255, 0, 0 )
+
+#define COLOR_CHOREO_PLAYBACKTICK RGB( 180, 31, 31 )
+#define COLOR_CHOREO_TIMELINE RGB( 31, 31, 127 )
+#define COLOR_CHOREO_ENDTIME RGB( 0, 0, 255 )
+#define COLOR_CHOREO_PLAYBACKTICKTEXT RGB( 127, 0, 0 )
+#define COLOR_CHOREO_TICKAB RGB( 31, 120, 31 )
+
+#define COLOR_INFO_BACKGROUND RGB( 240, 240, 240 )
+#define COLOR_INFO_TEXT RGB( 63, 31, 0 )
+#define COLOR_INFO_BORDER RGB( 100, 100, 250 )
+
+#endif // CHOREOVIEWCOLORS_H
diff --git a/utils/hlfaceposer/choreowidget.cpp b/utils/hlfaceposer/choreowidget.cpp
new file mode 100644
index 0000000..4f08097
--- /dev/null
+++ b/utils/hlfaceposer/choreowidget.cpp
@@ -0,0 +1,163 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include "choreowidget.h"
+#include "choreoview.h"
+
+// Static elements
+CChoreoScene *CChoreoWidget::m_pScene = NULL;
+CChoreoView *CChoreoWidget::m_pView = NULL;
+
+static int widgets = 0;
+//-----------------------------------------------------------------------------
+// CChoreoWidget new/delete
+// All fields in the object are all initialized to 0.
+//-----------------------------------------------------------------------------
+void *CChoreoWidget::operator new( size_t stAllocateBlock )
+{
+ widgets++;
+ // call into engine to get memory
+ Assert( stAllocateBlock != 0 );
+ return calloc( 1, stAllocateBlock );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pMem -
+//-----------------------------------------------------------------------------
+void CChoreoWidget::operator delete( void *pMem )
+{
+ widgets--;
+ // set the memory to a known value
+ int size = _msize( pMem );
+ memset( pMem, 0xfe, size );
+
+ // get the engine to free the memory
+ free( pMem );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Construct widget, all widgets clip their children and brethren
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CChoreoWidget::CChoreoWidget( CChoreoWidget *parent )
+{
+ m_bSelected = false;
+ m_pParent = parent;
+ m_bVisible = true;
+
+ m_rcBounds.left = m_rcBounds.right = m_rcBounds.top = m_rcBounds.bottom = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoWidget::~CChoreoWidget( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Default implementation, just return base row height
+//-----------------------------------------------------------------------------
+int CChoreoWidget::GetItemHeight( void )
+{
+ return m_pView->GetRowHeight();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void CChoreoWidget::LocalToScreen( int& mx, int& my )
+{
+ /*
+ HWND wnd = (HWND)getHandle();
+ if ( !wnd )
+ return;
+
+ POINT pt;
+ pt.x = (short)mx;
+ pt.y = (short)my;
+
+ ClientToScreen( wnd, &pt );
+
+ mx = pt.x;
+ my = pt.y;
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CChoreoWidget::IsSelected( void )
+{
+ return m_bSelected;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : selected -
+//-----------------------------------------------------------------------------
+void CChoreoWidget::SetSelected( bool selected )
+{
+ m_bSelected = selected;
+}
+
+void CChoreoWidget::setBounds( int x, int y, int w, int h )
+{
+ m_rcBounds.left = x;
+ m_rcBounds.right = x + w;
+ m_rcBounds.top = y;
+ m_rcBounds.bottom = y + h;
+}
+
+int CChoreoWidget::x( void )
+{
+ return m_rcBounds.left;
+}
+
+int CChoreoWidget::y( void )
+{
+ return m_rcBounds.top;
+}
+
+int CChoreoWidget::w( void )
+{
+ return m_rcBounds.right - m_rcBounds.left;
+}
+
+int CChoreoWidget::h( void )
+{
+ return m_rcBounds.bottom - m_rcBounds.top;
+}
+
+CChoreoWidget *CChoreoWidget::getParent( void )
+{
+ return m_pParent;
+}
+
+void CChoreoWidget::setVisible( bool visible )
+{
+ m_bVisible = visible;
+}
+
+bool CChoreoWidget::getVisible( void )
+{
+ return m_bVisible;
+}
+
+void CChoreoWidget::getBounds( RECT& bounds )
+{
+ bounds = m_rcBounds;
+}
+
+RECT &CChoreoWidget::getBounds( void )
+{
+ return m_rcBounds;
+}
diff --git a/utils/hlfaceposer/choreowidget.h b/utils/hlfaceposer/choreowidget.h
new file mode 100644
index 0000000..275ea40
--- /dev/null
+++ b/utils/hlfaceposer/choreowidget.h
@@ -0,0 +1,79 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CHOREOWIDGET_H
+#define CHOREOWIDGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+
+class CChoreoView;
+class CChoreoScene;
+class CChoreoWidgetDrawHelper;
+
+//-----------------------------------------------------------------------------
+// Purpose: CChoreoWidgets are mxWindows that we show in the Choreography view area
+// so that we can manipulate them with the mouse. The widgets follow the scene
+// hierarchy of actors/channels/events, without having to hang off of the underlying
+// data. They are just for the UI.
+//-----------------------------------------------------------------------------
+class CChoreoWidget
+{
+public:
+ // memory handling, uses calloc so members are zero'd out on instantiation
+ void *operator new( size_t stAllocateBlock );
+ void operator delete( void *pMem );
+
+ CChoreoWidget( CChoreoWidget *parent );
+ virtual ~CChoreoWidget( void );
+
+ // All widgets implement these pure virtuals
+
+ // Called to force a widget to create its children based on the scene data
+ virtual void Create( void ) = 0;
+ // Force widget to redo layout of self and any children
+ virtual void Layout( RECT& rc ) = 0;
+ // Redraw the widget
+ virtual void redraw( CChoreoWidgetDrawHelper& drawHelper ) = 0;
+ // Don't overdraw background
+ virtual bool PaintBackground( void ) { return false; };
+ // Determine height to reserver for widget ( Actors can be expanded or collapsed, e.g. )
+ virtual int GetItemHeight( void );
+
+ virtual void LocalToScreen( int& mx, int& my );
+
+ virtual bool IsSelected( void );
+ virtual void SetSelected( bool selected );
+
+ virtual void setBounds( int x, int y, int w, int h );
+ virtual int x( void );
+ virtual int y( void );
+ virtual int w( void );
+ virtual int h( void );
+ virtual CChoreoWidget *getParent( void );
+ virtual void setVisible( bool visible );
+ virtual bool getVisible( void );
+
+ virtual void getBounds( RECT& bounds );
+ virtual RECT &getBounds( void );
+
+ // Globally accessible scene and view pointers
+ static CChoreoScene *m_pScene;
+ static CChoreoView *m_pView;
+
+private:
+ bool m_bSelected;
+ bool m_bVisible;
+
+ RECT m_rcBounds;
+
+protected:
+ CChoreoWidget *m_pParent;
+};
+
+#endif // CHOREOWIDGET_H
diff --git a/utils/hlfaceposer/choreowidgetdrawhelper.cpp b/utils/hlfaceposer/choreowidgetdrawhelper.cpp
new file mode 100644
index 0000000..86796d2
--- /dev/null
+++ b/utils/hlfaceposer/choreowidgetdrawhelper.cpp
@@ -0,0 +1,1069 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "tier0/dbg.h"
+#include <stdio.h>
+#include "choreoview.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoviewcolors.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget )
+{
+ Init( widget, 0, 0, 0, 0, COLOR_CHOREO_BACKGROUND, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget, COLORREF bgColor )
+{
+ Init( widget, 0, 0, 0, 0, bgColor, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// bounds -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds )
+{
+ Init( widget, bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, COLOR_CHOREO_BACKGROUND, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// bounds -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds, bool noPageFlip )
+{
+ Init( widget, bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, COLOR_CHOREO_BACKGROUND, noPageFlip );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// x -
+// y -
+// w -
+// h -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget, int x, int y, int w, int h, COLORREF bgColor )
+{
+ Init( widget, x, y, w, h, bgColor, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// bounds -
+// bgColor -
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds, COLORREF bgColor )
+{
+ Init( widget, bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, bgColor, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *widget -
+// x -
+// y -
+// w -
+// h -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::Init( mxWindow *widget, int x, int y, int w, int h, COLORREF bgColor, bool noPageFlip )
+{
+ m_bNoPageFlip = noPageFlip;
+
+ m_x = x;
+ m_y = y;
+
+ m_w = w ? w : widget->w2();
+ m_h = h ? h : widget->h2();
+
+ m_hWnd = (HWND)widget->getHandle();
+ Assert( m_hWnd );
+ m_dcReal = GetDC( m_hWnd );
+ m_rcClient.left = m_x;
+ m_rcClient.top = m_y;
+ m_rcClient.right = m_x + m_w;
+ m_rcClient.bottom = m_y + m_h;
+
+ if ( !noPageFlip )
+ {
+ m_dcMemory = CreateCompatibleDC( m_dcReal );
+ m_bmMemory = CreateCompatibleBitmap( m_dcReal, m_w, m_h );
+ m_bmOld = (HBITMAP)SelectObject( m_dcMemory, m_bmMemory );
+ }
+ else
+ {
+ m_dcMemory = m_dcReal;
+ m_x = m_y = 0;
+ }
+
+ m_clrOld = SetBkColor( m_dcMemory, bgColor );
+
+ RECT rcFill = m_rcClient;
+ OffsetRect( &rcFill, -m_rcClient.left, -m_rcClient.top );
+
+ if ( !noPageFlip )
+ {
+ HBRUSH br = CreateSolidBrush( bgColor );
+ FillRect( m_dcMemory, &rcFill, br );
+ DeleteObject( br );
+ }
+
+ m_ClipRegion = (HRGN)0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finish up
+//-----------------------------------------------------------------------------
+CChoreoWidgetDrawHelper::~CChoreoWidgetDrawHelper( void )
+{
+ SelectClipRgn( m_dcMemory, NULL );
+
+ while ( m_ClipRects.Size() > 0 )
+ {
+ StopClipping();
+ }
+
+ if ( !m_bNoPageFlip )
+ {
+ BitBlt( m_dcReal, m_x, m_y, m_w, m_h, m_dcMemory, 0, 0, SRCCOPY );
+
+ SetBkColor( m_dcMemory, m_clrOld );
+
+ SelectObject( m_dcMemory, m_bmOld );
+ DeleteObject( m_bmMemory );
+
+ DeleteObject( m_dcMemory );
+ }
+
+ ReleaseDC( m_hWnd, m_dcReal );
+
+ ValidateRect( m_hWnd, &m_rcClient );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoWidgetDrawHelper::GetWidth( void )
+{
+ return m_w;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoWidgetDrawHelper::GetHeight( void )
+{
+ return m_h;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::GetClientRect( RECT& rc )
+{
+ rc.left = rc.top = 0;
+ rc.right = m_w;
+ rc.bottom = m_h;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : HDC
+//-----------------------------------------------------------------------------
+HDC CChoreoWidgetDrawHelper::GrabDC( void )
+{
+ return m_dcMemory;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// maxwidth -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::CalcTextRect( const char *font, int pointsize, int weight, int maxwidth, RECT& rcText, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, fnt );
+
+ DrawText( m_dcMemory, output, -1, &rcText, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_WORDBREAK | DT_CALCRECT );
+
+ SelectObject( m_dcMemory, oldFont );
+ DeleteObject( fnt );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// *fmt -
+// ... -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoWidgetDrawHelper::CalcTextWidth( const char *font, int pointsize, int weight, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HDC screen = GetDC( NULL );
+
+ HFONT oldFont = (HFONT)SelectObject( screen, fnt );
+
+ RECT rcText;
+ rcText.left = rcText.top = 0;
+ rcText.bottom = pointsize + 5;
+ rcText.right = rcText.left + 2048;
+
+ DrawText( screen, output, -1, &rcText, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT );
+
+ SelectObject( screen, oldFont );
+ DeleteObject( fnt );
+
+ ReleaseDC( NULL, screen );
+
+ return rcText.right;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// *fmt -
+// ... -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoWidgetDrawHelper::CalcTextWidthW( const char *font, int pointsize, int weight, const wchar_t *fmt, ... )
+{
+ va_list args;
+ static wchar_t output[1024];
+
+ va_start( args, fmt );
+ vwprintf( fmt, args );
+ vswprintf( output, fmt, args );
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HDC screen = GetDC( NULL );
+
+ HFONT oldFont = (HFONT)SelectObject( screen, fnt );
+
+ RECT rcText;
+ rcText.left = rcText.top = 0;
+ rcText.bottom = pointsize + 5;
+ rcText.right = rcText.left + 2048;
+
+ DrawTextW( screen, output, -1, &rcText, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT );
+
+ SelectObject( screen, oldFont );
+ DeleteObject( fnt );
+
+ ReleaseDC( NULL, screen );
+
+ return rcText.right;
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : fnt -
+// *fmt -
+// ... -
+// Output : int
+//-----------------------------------------------------------------------------
+int CChoreoWidgetDrawHelper::CalcTextWidth( HFONT fnt, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ HDC screen = GetDC( NULL );
+
+ HFONT oldFont = (HFONT)SelectObject( screen, fnt );
+
+ RECT rcText;
+ rcText.left = rcText.top = 0;
+ rcText.bottom = 1000;
+ rcText.right = rcText.left + 2048;
+
+ DrawText( screen, output, -1, &rcText, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT );
+
+ SelectObject( screen, oldFont );
+
+ ReleaseDC( NULL, screen );
+
+ return rcText.right;
+}
+
+int CChoreoWidgetDrawHelper::CalcTextWidthW( HFONT fnt, const wchar_t *fmt, ... )
+{
+ va_list args;
+ static wchar_t output[1024];
+
+ va_start( args, fmt );
+ vwprintf( fmt, args );
+ vswprintf( output, fmt, args );
+
+ HDC screen = GetDC( NULL );
+
+ HFONT oldFont = (HFONT)SelectObject( screen, fnt );
+
+ RECT rcText;
+ rcText.left = rcText.top = 0;
+ rcText.bottom = 1000;
+ rcText.right = rcText.left + 2048;
+
+ DrawTextW( screen, output, -1, &rcText, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT );
+
+ SelectObject( screen, oldFont );
+
+ ReleaseDC( NULL, screen );
+
+ return rcText.right;
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredText( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vsprintf( output, fmt, args );
+ va_end( args );
+
+ DrawColoredTextCharset( font, pointsize, weight, ANSI_CHARSET, clr, rcText, output );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredTextW( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, const wchar_t *fmt, ... )
+{
+ va_list args;
+ static wchar_t output[1024];
+
+ va_start( args, fmt );
+ vswprintf( output, fmt, args );
+ va_end( args );
+
+ DrawColoredTextCharsetW( font, pointsize, weight, ANSI_CHARSET, clr, rcText, output );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : font -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredText( HFONT font, COLORREF clr, RECT& rcText, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vsprintf( output, fmt, args );
+ va_end( args );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, font );
+ COLORREF oldColor = SetTextColor( m_dcMemory, clr );
+ int oldMode = SetBkMode( m_dcMemory, TRANSPARENT );
+
+ RECT rcTextOffset = rcText;
+ OffsetSubRect( rcTextOffset );
+
+ DrawText( m_dcMemory, output, -1, &rcTextOffset, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS );
+
+ SetBkMode( m_dcMemory, oldMode );
+
+ SetTextColor( m_dcMemory, oldColor );
+
+ SelectObject( m_dcMemory, oldFont );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : font -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredTextW( HFONT font, COLORREF clr, RECT& rcText, const wchar_t *fmt, ... )
+{
+ va_list args;
+ static wchar_t output[1024];
+
+ va_start( args, fmt );
+ vswprintf( output, fmt, args );
+ va_end( args );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, font );
+ COLORREF oldColor = SetTextColor( m_dcMemory, clr );
+ int oldMode = SetBkMode( m_dcMemory, TRANSPARENT );
+
+ RECT rcTextOffset = rcText;
+ OffsetSubRect( rcTextOffset );
+
+ DrawTextW( m_dcMemory, output, -1, &rcTextOffset, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS );
+
+ SetBkMode( m_dcMemory, oldMode );
+
+ SetTextColor( m_dcMemory, oldColor );
+
+ SelectObject( m_dcMemory, oldFont );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredTextCharset( const char *font, int pointsize, int weight, DWORD charset, COLORREF clr, RECT& rcText, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vsprintf( output, fmt, args );
+ va_end( args );
+
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ charset,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, fnt );
+ COLORREF oldColor = SetTextColor( m_dcMemory, clr );
+ int oldMode = SetBkMode( m_dcMemory, TRANSPARENT );
+
+ RECT rcTextOffset = rcText;
+ OffsetSubRect( rcTextOffset );
+
+ DrawText( m_dcMemory, output, -1, &rcTextOffset, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS );
+
+ SetBkMode( m_dcMemory, oldMode );
+
+ SetTextColor( m_dcMemory, oldColor );
+
+ SelectObject( m_dcMemory, oldFont );
+ DeleteObject( fnt );
+}
+
+void CChoreoWidgetDrawHelper::DrawColoredTextCharsetW( const char *font, int pointsize, int weight, DWORD charset, COLORREF clr, RECT& rcText, const wchar_t *fmt, ... )
+{
+ va_list args;
+ static wchar_t output[1024];
+
+ va_start( args, fmt );
+ vswprintf( output, fmt, args );
+ va_end( args );
+
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ charset,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, fnt );
+ COLORREF oldColor = SetTextColor( m_dcMemory, clr );
+ int oldMode = SetBkMode( m_dcMemory, TRANSPARENT );
+
+ RECT rcTextOffset = rcText;
+ OffsetSubRect( rcTextOffset );
+
+ DrawTextW( m_dcMemory, output, -1, &rcTextOffset, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS );
+
+ SetBkMode( m_dcMemory, oldMode );
+
+ SetTextColor( m_dcMemory, oldColor );
+
+ SelectObject( m_dcMemory, oldFont );
+ DeleteObject( fnt );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *font -
+// pointsize -
+// weight -
+// clr -
+// rcText -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredTextMultiline( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ HFONT fnt = CreateFont(
+ -pointsize,
+ 0,
+ 0,
+ 0,
+ weight,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ ANTIALIASED_QUALITY,
+ DEFAULT_PITCH,
+ font );
+
+ HFONT oldFont = (HFONT)SelectObject( m_dcMemory, fnt );
+ COLORREF oldColor = SetTextColor( m_dcMemory, clr );
+ int oldMode = SetBkMode( m_dcMemory, TRANSPARENT );
+
+ RECT rcTextOffset = rcText;
+ OffsetSubRect( rcTextOffset );
+
+ DrawText( m_dcMemory, output, -1, &rcTextOffset, DT_LEFT | DT_NOPREFIX | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS );
+
+ SetBkMode( m_dcMemory, oldMode );
+
+ SetTextColor( m_dcMemory, oldColor );
+
+ SelectObject( m_dcMemory, oldFont );
+ DeleteObject( fnt );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : r -
+// g -
+// b -
+// style -
+// width -
+// x1 -
+// y1 -
+// x2 -
+// y2 -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredLine( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2 )
+{
+ HPEN pen = CreatePen( style, width, clr );
+ HPEN oldPen = (HPEN)SelectObject( m_dcMemory, pen );
+ MoveToEx( m_dcMemory, x1-m_x, y1-m_y, NULL );
+ LineTo( m_dcMemory, x2-m_x, y2-m_y );
+ SelectObject( m_dcMemory, oldPen );
+ DeleteObject( pen );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : clr -
+// style -
+// width -
+// count -
+// *pts -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawColoredPolyLine( COLORREF clr, int style, int width, CUtlVector< POINT >& points )
+{
+ int c = points.Count();
+ if ( c < 2 )
+ return;
+
+ HPEN pen = CreatePen( style, width, clr );
+ HPEN oldPen = (HPEN)SelectObject( m_dcMemory, pen );
+
+ POINT *temp = (POINT *)_alloca( c * sizeof( POINT ) );
+ Assert( temp );
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ POINT *pt = &points[ i ];
+
+ temp[ i ].x = pt->x - m_x;
+ temp[ i ].y = pt->y - m_y;
+ }
+
+ Polyline( m_dcMemory, temp, c );
+
+ SelectObject( m_dcMemory, oldPen );
+ DeleteObject( pen );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : r -
+// g -
+// b -
+// style -
+// width -
+// x1 -
+// y1 -
+// x2 -
+// y2 -
+//-----------------------------------------------------------------------------
+POINTL CChoreoWidgetDrawHelper::DrawColoredRamp( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2, float rate, float sustain )
+{
+ HPEN pen = CreatePen( style, width, clr );
+ HPEN oldPen = (HPEN)SelectObject( m_dcMemory, pen );
+ MoveToEx( m_dcMemory, x1-m_x, y1-m_y, NULL );
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ POINTL p;
+ p.x = 0L;
+ p.y = 0L;
+ for (float i = 0.1f; i <= 1.09f; i += 0.1f)
+ {
+ float j = 3.0f * i * i - 2.0f * i * i * i;
+ p.x = x1+(int)(dx*i*(1.0f-rate))-m_x;
+ p.y = y1+(int)(dy*sustain*j)-m_y;
+ LineTo( m_dcMemory, p.x, p.y );
+ }
+ SelectObject( m_dcMemory, oldPen );
+ DeleteObject( pen );
+
+ return p;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a filled rect
+// Input : clr -
+// x1 -
+// y1 -
+// x2 -
+// y2 -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawFilledRect( COLORREF clr, RECT& rc )
+{
+ RECT rcCopy = rc;
+
+ HBRUSH br = CreateSolidBrush( clr );
+ OffsetSubRect( rcCopy );
+ FillRect( m_dcMemory, &rcCopy, br );
+ DeleteObject( br );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a filled rect
+// Input : clr -
+// x1 -
+// y1 -
+// x2 -
+// y2 -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawFilledRect( COLORREF clr, int x1, int y1, int x2, int y2 )
+{
+ HBRUSH br = CreateSolidBrush( clr );
+ RECT rc;
+ rc.left = x1;
+ rc.right = x2;
+ rc.top = y1;
+ rc.bottom = y2;
+ OffsetSubRect( rc );
+ FillRect( m_dcMemory, &rc, br );
+ DeleteObject( br );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : clr -
+// style -
+// width -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawOutlinedRect( COLORREF clr, int style, int width, RECT& rc )
+{
+ DrawOutlinedRect( clr, style, width, rc.left, rc.top, rc.right, rc.bottom );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw an outlined rect
+// Input : clr -
+// style -
+// width -
+// x1 -
+// y1 -
+// x2 -
+// y2 -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawOutlinedRect( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2 )
+{
+ HPEN oldpen, pen;
+ HBRUSH oldbrush, brush;
+
+ pen = CreatePen( PS_SOLID, width, clr );
+ oldpen = (HPEN)SelectObject( m_dcMemory, pen );
+
+ brush = (HBRUSH)GetStockObject( NULL_BRUSH );
+ oldbrush = (HBRUSH)SelectObject( m_dcMemory, brush );
+
+ RECT rc;
+ rc.left = x1;
+ rc.right = x2;
+ rc.top = y1;
+ rc.bottom = y2;
+ OffsetSubRect( rc);
+
+ Rectangle( m_dcMemory, rc.left, rc.top, rc.right, rc.bottom );
+
+ SelectObject( m_dcMemory, oldbrush );
+ DeleteObject( brush );
+ SelectObject( m_dcMemory, oldpen );
+ DeleteObject( pen );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x1 -
+// y1 -
+// x2 -
+// y2 -
+// clr -
+// thickness -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawLine( int x1, int y1, int x2, int y2, COLORREF clr, int thickness )
+{
+ HPEN oldpen, pen;
+ HBRUSH oldbrush, brush;
+
+ pen = CreatePen( PS_SOLID, thickness, clr );
+ oldpen = (HPEN)SelectObject( m_dcMemory, pen );
+
+ brush = (HBRUSH)GetStockObject( NULL_BRUSH );
+ oldbrush = (HBRUSH)SelectObject( m_dcMemory, brush );
+
+ // Offset
+ x1 -= m_x;
+ x2 -= m_x;
+ y1 -= m_y;
+ y2 -= m_y;
+
+ MoveToEx( m_dcMemory, x1, y1, NULL );
+ LineTo( m_dcMemory, x2, y2 );
+
+ SelectObject( m_dcMemory, oldbrush );
+ DeleteObject( brush );
+ SelectObject( m_dcMemory, oldpen );
+ DeleteObject( pen );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+// fillr -
+// fillg -
+// fillb -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawTriangleMarker( RECT& rc, COLORREF fill, bool inverted /*= false*/ )
+{
+ POINT region[3];
+ int cPoints = 3;
+
+ if ( !inverted )
+ {
+ region[ 0 ].x = rc.left - m_x;
+ region[ 0 ].y = rc.top - m_y;
+
+ region[ 1 ].x = rc.right - m_x;
+ region[ 1 ].y = rc.top - m_y;
+
+ region[ 2 ].x = ( ( rc.left + rc.right ) / 2 ) - m_x;
+ region[ 2 ].y = rc.bottom - m_y;
+ }
+ else
+ {
+ region[ 0 ].x = rc.left - m_x;
+ region[ 0 ].y = rc.bottom - m_y;
+
+ region[ 1 ].x = rc.right - m_x;
+ region[ 1 ].y = rc.bottom - m_y;
+
+ region[ 2 ].x = ( ( rc.left + rc.right ) / 2 ) - m_x;
+ region[ 2 ].y = rc.top - m_y;
+ }
+
+ HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
+
+ int oldPF = SetPolyFillMode( m_dcMemory, ALTERNATE );
+
+ HBRUSH brFace = CreateSolidBrush( fill );
+
+ FillRgn( m_dcMemory, rgn, brFace );
+
+ DeleteObject( brFace );
+
+ SetPolyFillMode( m_dcMemory, oldPF );
+
+ DeleteObject( rgn );
+}
+
+void CChoreoWidgetDrawHelper::StartClipping( RECT& clipRect )
+{
+ RECT fixed = clipRect;
+ OffsetSubRect( fixed );
+
+ m_ClipRects.AddToTail( fixed );
+
+ ClipToRects();
+}
+
+void CChoreoWidgetDrawHelper::StopClipping( void )
+{
+ Assert( m_ClipRects.Size() > 0 );
+ if ( m_ClipRects.Size() <= 0 )
+ return;
+
+ m_ClipRects.Remove( m_ClipRects.Size() - 1 );
+
+ ClipToRects();
+}
+
+void CChoreoWidgetDrawHelper::ClipToRects( void )
+{
+ SelectClipRgn( m_dcMemory, NULL );
+ if ( m_ClipRegion )
+ {
+ DeleteObject( m_ClipRegion );
+ m_ClipRegion = HRGN( 0 );
+ }
+
+ if ( m_ClipRects.Size() > 0 )
+ {
+ RECT rc = m_ClipRects[ 0 ];
+ m_ClipRegion = CreateRectRgn( rc.left, rc.top, rc.right, rc.bottom );
+ for ( int i = 1; i < m_ClipRects.Size(); i++ )
+ {
+ RECT add = m_ClipRects[ i ];
+
+ HRGN addIn = CreateRectRgn( add.left, add.top, add.right, add.bottom );
+ HRGN result = CreateRectRgn( 0, 0, 100, 100 );
+
+ CombineRgn( result, m_ClipRegion, addIn, RGN_AND );
+
+ DeleteObject( m_ClipRegion );
+ DeleteObject( addIn );
+
+ m_ClipRegion = result;
+ }
+ }
+
+ SelectClipRgn( m_dcMemory, m_ClipRegion );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::OffsetSubRect( RECT& rc )
+{
+ OffsetRect( &rc, -m_x, -m_y );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : br -
+// rc -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawFilledRect( HBRUSH br, RECT& rc )
+{
+ RECT rcFill = rc;
+ OffsetSubRect( rcFill );
+ FillRect( m_dcMemory, &rcFill, br );
+}
+
+void CChoreoWidgetDrawHelper::DrawCircle( COLORREF clr, int x, int y, int radius, bool filled /*= true*/ )
+{
+ RECT rc;
+ int ihalfradius = radius >> 1;
+
+ rc.left = x - ihalfradius;
+ rc.right = rc.left + 2 * ihalfradius;
+ rc.top = y - ihalfradius;
+ rc.bottom = y + 2 * ihalfradius - 1;
+
+ OffsetSubRect( rc );
+
+ HPEN pen = CreatePen( PS_SOLID, 1, clr );
+ HBRUSH br = CreateSolidBrush( clr );
+
+ HPEN oldPen = (HPEN)SelectObject( m_dcMemory, pen );
+ HBRUSH oldBr = (HBRUSH)SelectObject( m_dcMemory, br );
+
+ if ( filled )
+ {
+ Ellipse( m_dcMemory, rc.left, rc.top, rc.right, rc.bottom );
+ }
+ else
+ {
+ Arc( m_dcMemory, rc.left, rc.top, rc.right, rc.bottom,
+ rc.left, rc.top, rc.left, rc.top );
+ }
+
+ SelectObject( m_dcMemory, oldPen );
+ SelectObject( m_dcMemory, oldBr );
+
+ DeleteObject( pen );
+ DeleteObject( br );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+// clr1 -
+// clr2 -
+// vertical -
+//-----------------------------------------------------------------------------
+void CChoreoWidgetDrawHelper::DrawGradientFilledRect( RECT& rc, COLORREF clr1, COLORREF clr2, bool vertical )
+{
+ RECT rcDraw = rc;
+ OffsetRect( &rcDraw, -m_x, -m_y );
+
+ TRIVERTEX vert[2] ;
+ GRADIENT_RECT gradient_rect;
+ vert[0].x = rcDraw.left;
+ vert[0].y = rcDraw.top;
+ vert[0].Red = GetRValue( clr1 ) << 8;
+ vert[0].Green = GetGValue( clr1 ) << 8;
+ vert[0].Blue = GetBValue( clr1 ) << 8;
+ vert[0].Alpha = 0x0000;
+
+ vert[1].x = rcDraw.right;
+ vert[1].y = rcDraw.bottom;
+ vert[1].Red = GetRValue( clr2 ) << 8;
+ vert[1].Green = GetGValue( clr2 ) << 8;
+ vert[1].Blue = GetBValue( clr2 ) << 8;
+ vert[1].Alpha = 0x0000;
+
+ gradient_rect.UpperLeft = 0;
+ gradient_rect.LowerRight = 1;
+
+ GradientFill(
+ m_dcMemory,
+ vert, 2,
+ &gradient_rect, 1,
+ vertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H );
+}
diff --git a/utils/hlfaceposer/choreowidgetdrawhelper.h b/utils/hlfaceposer/choreowidgetdrawhelper.h
new file mode 100644
index 0000000..ca6b06b
--- /dev/null
+++ b/utils/hlfaceposer/choreowidgetdrawhelper.h
@@ -0,0 +1,122 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CHOREOWIDGETDRAWHELPER_H
+#define CHOREOWIDGETDRAWHELPER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "choreowidget.h"
+#include "utlvector.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper class that automagically sets up and destroys a memory device-
+// context for flicker-free refershes
+//-----------------------------------------------------------------------------
+class CChoreoWidgetDrawHelper
+{
+public:
+ // Construction/destruction
+ CChoreoWidgetDrawHelper( mxWindow *widget);
+ CChoreoWidgetDrawHelper( mxWindow *widget, COLORREF bgColor );
+ CChoreoWidgetDrawHelper( mxWindow *widget, int x, int y, int w, int h, COLORREF bgColor );
+ CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds );
+ CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds, COLORREF bgColor );
+
+ CChoreoWidgetDrawHelper( mxWindow *widget, RECT& bounds, bool noPageFlip );
+
+ virtual ~CChoreoWidgetDrawHelper( void );
+
+ // Allow caller to draw onto the memory dc, too
+ HDC GrabDC( void );
+
+ // Compute text size
+ static int CalcTextWidth( const char *font, int pointsize, int weight, PRINTF_FORMAT_STRING const char *fmt, ... );
+ static int CalcTextWidth( HFONT font, PRINTF_FORMAT_STRING const char *fmt, ... );
+
+ static int CalcTextWidthW( const char *font, int pointsize, int weight, PRINTF_FORMAT_STRING const wchar_t *fmt, ... );
+ static int CalcTextWidthW( HFONT font, PRINTF_FORMAT_STRING const wchar_t *fmt, ... );
+
+ void DrawColoredTextW( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const wchar_t *fmt, ... );
+ void DrawColoredTextW( HFONT font, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const wchar_t *fmt, ... );
+ void DrawColoredTextCharsetW( const char *font, int pointsize, int weight, DWORD charset, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const wchar_t *fmt, ... );
+
+ void CalcTextRect( const char *font, int pointsize, int weight, int maxwidth, RECT& rcText, PRINTF_FORMAT_STRING const char *fmt, ... );
+
+ // Draw text
+ void DrawColoredText( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const char *fmt, ... );
+ void DrawColoredText( HFONT font, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const char *fmt, ... );
+ void DrawColoredTextCharset( const char *font, int pointsize, int weight, DWORD charset, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const char *fmt, ... );
+ void DrawColoredTextMultiline( const char *font, int pointsize, int weight, COLORREF clr, RECT& rcText, PRINTF_FORMAT_STRING const char *fmt, ... );
+ // Draw a line
+ void DrawColoredLine( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2 );
+ void DrawColoredPolyLine( COLORREF clr, int style, int width, CUtlVector< POINT >& points );
+
+ // Draw a blending ramp
+ POINTL DrawColoredRamp( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2, float rate, float sustain );
+ // Draw a filled rect
+ void DrawFilledRect( COLORREF clr, int x1, int y1, int x2, int y2 );
+ // Draw an outlined rect
+ void DrawOutlinedRect( COLORREF clr, int style, int width, int x1, int y1, int x2, int y2 );
+ void DrawOutlinedRect( COLORREF clr, int style, int width, RECT& rc );
+
+ void DrawFilledRect( HBRUSH br, RECT& rc );
+ void DrawFilledRect( COLORREF clr, RECT& rc );
+
+ void DrawGradientFilledRect( RECT& rc, COLORREF clr1, COLORREF clr2, bool vertical );
+
+ void DrawLine( int x1, int y1, int x2, int y2, COLORREF clr, int thickness );
+
+ // Draw a triangle
+ void DrawTriangleMarker( RECT& rc, COLORREF fill, bool inverted = false );
+
+ void DrawCircle( COLORREF clr, int x, int y, int radius, bool filled = true );
+
+ // Get width/height of draw area
+ int GetWidth( void );
+ int GetHeight( void );
+
+ // Get client rect for drawing
+ void GetClientRect( RECT& rc );
+
+ void StartClipping( RECT& clipRect );
+ void StopClipping( void );
+
+ // Remap rect if we're using a clipped viewport
+ void OffsetSubRect( RECT& rc );
+
+private:
+ // Internal initializer
+ void Init( mxWindow *widget, int x, int y, int w, int h, COLORREF bgColor, bool noPageFlip );
+
+ void ClipToRects( void );
+
+ // The window we are drawing on
+ HWND m_hWnd;
+ // The final DC
+ HDC m_dcReal;
+ // The working DC
+ HDC m_dcMemory;
+ // Client area and offsets
+ RECT m_rcClient;
+ int m_x, m_y;
+ int m_w, m_h;
+ // Bitmap for drawing in the memory DC
+ HBITMAP m_bmMemory;
+ HBITMAP m_bmOld;
+ // Remember the original default color
+ COLORREF m_clrOld;
+
+ CUtlVector < RECT > m_ClipRects;
+ HRGN m_ClipRegion;
+
+ bool m_bNoPageFlip;
+};
+
+#endif // CHOREOWIDGETDRAWHELPER_H
diff --git a/utils/hlfaceposer/controlpanel.cpp b/utils/hlfaceposer/controlpanel.cpp
new file mode 100644
index 0000000..e97cc7b
--- /dev/null
+++ b/utils/hlfaceposer/controlpanel.cpp
@@ -0,0 +1,976 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#include "ControlPanel.h"
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "IStudioRender.h"
+#include "MatSysWin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include <mxtk/mxBmp.h>
+#include "FlexPanel.h"
+#include "mxExpressionTray.h"
+#include "PhonemeEditor.h"
+#include "hlfaceposer.h"
+#include "expclass.h"
+#include "mxExpressionTab.h"
+#include "ExpressionTool.h"
+#include "MDLViewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "faceposer_models.h"
+#include "ifaceposerworkspace.h"
+#include "choreoview.h"
+#include "GestureTool.h"
+#include "RampTool.h"
+#include "SceneRampTool.h"
+#include "phonemeextractor/PhonemeExtractor.h"
+#include "tier1/KeyValues.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+extern char g_appTitle[];
+
+ControlPanel *g_pControlPanel = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose: A simple subclass so we can paint the window background
+//-----------------------------------------------------------------------------
+class CControlPanelTabWindow : public mxWindow
+{
+public:
+ CControlPanelTabWindow( mxWindow *parent, int x, int y, int w, int h ) :
+ mxWindow( parent, x, y, w, h )
+ {
+ FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+ };
+
+ virtual bool PaintBackground( void )
+ {
+ CChoreoWidgetDrawHelper drawHelper( this );
+
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ drawHelper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rc );
+ return false;
+ }
+};
+
+ControlPanel::ControlPanel (mxWindow *parent)
+: IFacePoserToolWindow( "ControlPanel", "Control Panel" ), mxWindow( parent, 0, 0, 0, 0 ), tab( 0 )
+{
+ // create tabcontrol with subdialog windows
+ tab = new mxTab (this, 0, 0, 0, 0, IDC_TAB);
+
+ CControlPanelTabWindow *wRender = new CControlPanelTabWindow (this, 0, 0, 0, 0);
+ tab->add (wRender, "Render");
+ cRenderMode = new mxChoice (wRender, 5, 5, 100, 22, IDC_RENDERMODE);
+ cRenderMode->add ("Wireframe");
+ cRenderMode->add ("Flatshaded");
+ cRenderMode->add ("Smoothshaded");
+ cRenderMode->add ("Textured");
+ cRenderMode->select (3);
+ mxToolTip::add (cRenderMode, "Select Render Mode");
+
+ slModelGap = new mxSlider( wRender, 220, 5, 140, 20, IDC_MODELSPACING );
+ slModelGap->setRange( 0.0f, 64.0f, 256 );
+ slModelGap->setValue( 16 );
+ mxToolTip::add (slModelGap, "Select Model Spacing" );
+ new mxLabel (wRender, 220, 25, 140, 20, "Model Spacing");
+
+ cbAllWindowsDriveSpeech = new mxCheckBox( wRender, 220, 45, 140, 20, "All tools drive mouth", IDC_TOOLSDRIVEMOUTH );
+ cbAllWindowsDriveSpeech->setChecked( g_viewerSettings.faceposerToolsDriveMouth );
+
+ cbGround = new mxCheckBox (wRender, 110, 5, 100, 20, "Ground", IDC_GROUND);
+ cbGround->setEnabled( true );
+ cbMovement = new mxCheckBox (wRender, 110, 25, 100, 20, "Movement", IDC_MOVEMENT);
+ cbMovement->setEnabled( false );
+ cbBackground = new mxCheckBox (wRender, 110, 45, 100, 20, "Background", IDC_BACKGROUND);
+ cbBackground->setEnabled( false );
+ new mxCheckBox (wRender, 110, 65, 100, 20, "Hit Boxes", IDC_HITBOXES);
+ new mxCheckBox (wRender, 5, 65, 100, 20, "Bones", IDC_BONES);
+ mxCheckBox *cbAttachments = new mxCheckBox (wRender, 5, 45, 100, 20, "Attachments", IDC_ATTACHMENTS);
+ cbAttachments->setEnabled( false );
+
+ CControlPanelTabWindow *wSequence = new CControlPanelTabWindow (this, 0, 0, 0, 0);
+ tab->add (wSequence, "Sequence");
+ cSequence = new mxChoice (wSequence, 5, 5, 200, 22, IDC_SEQUENCE);
+ mxToolTip::add (cSequence, "Select Sequence");
+
+
+ slSpeedScale = new mxSlider (wSequence, 5, 32, 200, 18, IDC_SPEEDSCALE);
+ slSpeedScale->setRange (0.0, 5.0 );
+ slSpeedScale->setValue (0.0);
+ mxToolTip::add (slSpeedScale, "Speed Scale");
+ lSpeedScale = new mxLabel( wSequence, 5, 50, 200, 18 );
+ lSpeedScale->setLabel( "Speed scale" );
+
+ CControlPanelTabWindow *wBody = new CControlPanelTabWindow (this, 0, 0, 0, 0);
+ tab->add (wBody, "Body");
+ cBodypart = new mxChoice (wBody, 5, 5, 100, 22, IDC_BODYPART);
+ mxToolTip::add (cBodypart, "Choose a bodypart");
+ cSubmodel = new mxChoice (wBody, 110, 5, 100, 22, IDC_SUBMODEL);
+ mxToolTip::add (cSubmodel, "Choose a submodel of current bodypart");
+ cController = new mxChoice (wBody, 5, 30, 100, 22, IDC_CONTROLLER);
+ mxToolTip::add (cController, "Choose a bone controller");
+ slController = new mxSlider (wBody, 105, 32, 100, 18, IDC_CONTROLLERVALUE);
+ slController->setRange (0, 45);
+ mxToolTip::add (slController, "Change current bone controller value");
+ lModelInfo1 = new mxLabel (wBody, 220, 5, 120, 100, "No Model.");
+ lModelInfo2 = new mxLabel (wBody, 340, 5, 120, 100, "");
+ cSkin = new mxChoice (wBody, 5, 55, 100, 22, IDC_SKINS);
+ mxToolTip::add (cSkin, "Choose a skin family");
+}
+
+ControlPanel::~ControlPanel()
+{
+}
+
+bool ControlPanel::CanClose( void )
+{
+ workspacefiles->StartStoringFiles( IWorkspaceFiles::EXPRESSION );
+ for ( int i = 0 ; i < expressions->GetNumClasses(); i++ )
+ {
+ CExpClass *cl = expressions->GetClass( i );
+ if ( cl )
+ {
+ workspacefiles->StoreFile( IWorkspaceFiles::EXPRESSION, cl->GetFileName() );
+ }
+ }
+ workspacefiles->FinishStoringFiles( IWorkspaceFiles::EXPRESSION );
+ // Now close them all, or abort exit if user doesn't want to close any that have changed
+ return Closeall();
+}
+
+void ControlPanel::OnDelete()
+{
+}
+
+void ControlPanel::PositionControls( int width, int height )
+{
+ if ( tab )
+ {
+ tab->setBounds( 0, GetCaptionHeight(), width, height );
+ }
+}
+
+void ControlPanel::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
+ HandleToolRedraw( helper );
+
+ BaseClass::redraw();
+}
+
+int
+ControlPanel::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ PositionControls( event->width, event->height );
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch (event->action)
+ {
+ case IDC_TOOLSDRIVEMOUTH:
+ {
+ g_viewerSettings.faceposerToolsDriveMouth = ((mxCheckBox *)event->widget)->isChecked();
+ }
+ break;
+ case IDC_TAB:
+ {
+ g_viewerSettings.showTexture = (tab->getSelectedIndex() == 3);
+ }
+ break;
+
+ case IDC_RENDERMODE:
+ {
+ int index = cRenderMode->getSelectedIndex();
+ if (index >= 0)
+ {
+ setRenderMode (index);
+ }
+ }
+ break;
+
+ case IDC_GROUND:
+ setShowGround (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_MOVEMENT:
+ setShowMovement (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_BACKGROUND:
+ setShowBackground (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_HITBOXES:
+ g_viewerSettings.showHitBoxes = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_PHYSICSMODEL:
+ g_viewerSettings.showPhysicsModel = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_BONES:
+ g_viewerSettings.showBones = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_ATTACHMENTS:
+ g_viewerSettings.showAttachments = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_SEQUENCE:
+ {
+ int index = cSequence->getSelectedIndex();
+ if (index >= 0)
+ {
+ setSequence ( index );
+ }
+ }
+ break;
+
+ case IDC_SPEEDSCALE:
+ {
+ g_viewerSettings.speedScale = ((mxSlider *) event->widget)->getValue();
+ lSpeedScale->setLabel( va( "Speed scale %.2f", g_viewerSettings.speedScale ) );
+ }
+ break;
+
+ case IDC_PRIMARYBLEND:
+ {
+ setBlend( 0, ((mxSlider *) event->widget)->getValue() );
+ }
+ break;
+
+ case IDC_SECONDARYBLEND:
+ {
+ setBlend( 1, ((mxSlider *) event->widget)->getValue() );
+ }
+ break;
+
+ case IDC_BODYPART:
+ {
+ int index = cBodypart->getSelectedIndex();
+ if (index >= 0)
+ {
+ setBodypart (index);
+
+ }
+ }
+ break;
+
+ case IDC_SUBMODEL:
+ {
+ int index = cSubmodel->getSelectedIndex();
+ if (index >= 0)
+ {
+ setSubmodel (index);
+
+ }
+ }
+ break;
+
+ case IDC_CONTROLLER:
+ {
+ int index = cController->getSelectedIndex();
+ if (index >= 0)
+ setBoneController (index);
+ }
+ break;
+
+ case IDC_CONTROLLERVALUE:
+ {
+ int index = cController->getSelectedIndex();
+ if (index >= 0)
+ setBoneControllerValue (index, slController->getValue());
+ }
+ break;
+
+ case IDC_SKINS:
+ {
+ int index = cSkin->getSelectedIndex();
+ if (index >= 0)
+ {
+ models->GetActiveStudioModel()->SetSkin (index);
+ g_viewerSettings.skin = index;
+ g_pMatSysWindow->redraw();
+ }
+ }
+ break;
+ default:
+ iret = 0;
+ break;
+ }
+ }
+ }
+
+ return iret;
+}
+
+void ControlPanel::dumpModelInfo() { }
+
+void ControlPanel::ChangeModel( const char *filename )
+{
+ HCURSOR hPrevCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );
+
+ // init all the selection tabs based on the current model
+ initSequenceChoices();
+ initBodypartChoices();
+ initBoneControllerChoices();
+ initSkinChoices();
+
+ setModelInfo();
+
+ SetCloseCaptionLanguageId( g_viewerSettings.cclanguageid, true );
+
+ g_viewerSettings.m_iEditAttachment = -1;
+
+ g_viewerSettings.enableIK = true;
+ g_viewerSettings.enableTargetIK = false;
+
+ setSequence( models->GetActiveStudioModel()->GetSequence() );
+ setSpeed( g_viewerSettings.speedScale );
+
+ mx_setcwd (mx_getpath (filename));
+
+ g_pFlexPanel->initFlexes();
+
+ // centerView();
+ // CenterOnFace();
+
+ IFacePoserToolWindow::ModelChanged();
+
+ CExpClass *cl = expressions->GetActiveClass();
+ if ( cl )
+ {
+ cl->SelectExpression( cl->GetSelectedExpression() );
+ }
+
+ SetSuffix( va( " - %s.mdl", models->GetActiveModelName() ) );
+ redraw();
+
+ SetCursor( hPrevCursor );
+}
+
+
+
+void
+ControlPanel::setRenderMode (int mode)
+{
+ g_viewerSettings.renderMode = mode;
+ g_pMatSysWindow->redraw();
+}
+
+
+void
+ControlPanel::setHighlightBone( int index )
+{
+ g_viewerSettings.highlightPhysicsBone = index;
+}
+
+void
+ControlPanel::setShowGround (bool b)
+{
+ g_viewerSettings.showGround = b;
+ cbGround->setChecked (b);
+}
+
+
+
+void
+ControlPanel::setShowMovement (bool b)
+{
+ g_viewerSettings.showMovement = b;
+ cbMovement->setChecked (b);
+}
+
+
+
+void
+ControlPanel::setShowBackground (bool b)
+{
+ g_viewerSettings.showBackground = b;
+ cbBackground->setChecked (b);
+}
+
+
+
+void
+ControlPanel::initSequenceChoices()
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ cSequence->removeAll();
+ for (int i = 0; i < hdr->GetNumSeq(); i++)
+ {
+ cSequence->add (hdr->pSeqdesc(i).pszLabel());
+ }
+
+ cSequence->select (0);
+ }
+}
+
+
+
+void
+ControlPanel::setSequence (int index)
+{
+ cSequence->select (index);
+ models->GetActiveStudioModel()->SetSequence(index);
+
+ initPoseParameters( );
+}
+
+
+void
+ControlPanel::setSpeed( float value )
+{
+ g_viewerSettings.speedScale = value;
+ slSpeedScale->setValue( value );
+}
+
+
+void ControlPanel::setBlend(int index, float value )
+{
+ models->GetActiveStudioModel()->SetPoseParameter( index, value );
+}
+
+
+void
+ControlPanel::initBodypartChoices()
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ int i;
+ mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);
+
+ cBodypart->removeAll();
+ if (hdr->numbodyparts() > 0)
+ {
+ for (i = 0; i < hdr->numbodyparts(); i++)
+ cBodypart->add (pbodyparts[i].pszName());
+
+ cBodypart->select (0);
+
+ cSubmodel->removeAll();
+ for (i = 0; i < pbodyparts[0].nummodels; i++)
+ {
+ char str[64];
+ sprintf (str, "Submodel %d", i + 1);
+ cSubmodel->add (str);
+ }
+ cSubmodel->select (0);
+ }
+ }
+}
+
+
+
+void
+ControlPanel::setBodypart (int index)
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ //cBodypart->setEn
+ cBodypart->select (index);
+ if (index < hdr->numbodyparts())
+ {
+ mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);
+ cSubmodel->removeAll();
+
+ for (int i = 0; i < pbodyparts[index].nummodels; i++)
+ {
+ char str[64];
+ sprintf (str, "Submodel %d", i + 1);
+ cSubmodel->add (str);
+ }
+ cSubmodel->select (0);
+ //models->GetActiveStudioModel()->SetBodygroup (index, 0);
+ }
+ }
+}
+
+
+
+void
+ControlPanel::setSubmodel (int index)
+{
+ models->GetActiveStudioModel()->SetBodygroup (cBodypart->getSelectedIndex(), index);
+}
+
+
+
+void
+ControlPanel::initBoneControllerChoices()
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ cController->setEnabled (hdr->numbonecontrollers() > 0);
+ slController->setEnabled (hdr->numbonecontrollers() > 0);
+ cController->removeAll();
+
+ for (int i = 0; i < hdr->numbonecontrollers(); i++)
+ {
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(i);
+ char str[32];
+ sprintf (str, "Controller %d", pbonecontroller->inputfield);
+ cController->add (str);
+ }
+
+ if (hdr->numbonecontrollers() > 0)
+ {
+ mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(0);
+ cController->select (0);
+ slController->setRange (pbonecontrollers->start, pbonecontrollers->end);
+ slController->setValue (0);
+ }
+ }
+}
+
+
+
+void
+ControlPanel::setBoneController (int index)
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(index);
+ slController->setRange ( pbonecontroller->start, pbonecontroller->end);
+ slController->setValue (0);
+ }
+}
+
+
+
+void
+ControlPanel::setBoneControllerValue (int index, float value)
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(index);
+ models->GetActiveStudioModel()->SetController (pbonecontrollers->inputfield, value);
+ }
+}
+
+
+
+void
+ControlPanel::initPoseParameters()
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ for (int i = 0; i < hdr->GetNumPoseParameters(); i++)
+ {
+ setBlend( i, 0.0 );
+ }
+ }
+}
+
+void
+ControlPanel::initSkinChoices()
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ cSkin->setEnabled (hdr->numskinfamilies() > 0);
+ cSkin->removeAll();
+
+ for (int i = 0; i < hdr->numskinfamilies(); i++)
+ {
+ char str[32];
+ sprintf (str, "Skin %d", i + 1);
+ cSkin->add (str);
+ }
+
+ cSkin->select (0);
+ models->GetActiveStudioModel()->SetSkin (0);
+ g_viewerSettings.skin = 0;
+ }
+}
+
+
+
+void
+ControlPanel::setModelInfo()
+{
+ static char str[2048];
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+
+ if (!hdr)
+ return;
+
+ int hbcount = 0;
+ for ( int s = 0; s < hdr->numhitboxsets(); s++ )
+ {
+ hbcount += hdr->iHitboxCount( s );
+ }
+
+ sprintf (str,
+ "Bones: %d\n"
+ "Bone Controllers: %d\n"
+ "Hit Boxes: %d in %d sets\n"
+ "Sequences: %d\n",
+ hdr->numbones(),
+ hdr->numbonecontrollers(),
+ hbcount,
+ hdr->numhitboxsets(),
+ hdr->GetNumSeq()
+ );
+
+ lModelInfo1->setLabel (str);
+
+ sprintf (str,
+ "Textures: %d\n"
+ "Skin Families: %d\n"
+ "Bodyparts: %d\n"
+ "Attachments: %d\n",
+ hdr->numtextures(),
+ hdr->numskinfamilies(),
+ hdr->numbodyparts(),
+ hdr->GetNumAttachments());
+
+ lModelInfo2->setLabel (str);
+}
+
+
+void ControlPanel::CenterOnFace( void )
+{
+ if ( !models->GetActiveStudioModel() )
+ return;
+
+ StudioModel *mdl = models->GetActiveStudioModel();
+ if ( !mdl )
+ return;
+
+ CStudioHdr *hdr = mdl->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ setSpeed( 1.0f );
+
+ int oldSeq = models->GetActiveStudioModel()->GetSequence();
+
+ int seq = models->GetActiveStudioModel()->LookupSequence( "idle_suble" );
+ if ( seq == -1 )
+ seq = 0;
+
+ if ( seq != oldSeq )
+ {
+ Con_Printf( "Centering changed model sequence # to %d\n", seq );
+ }
+
+ setSequence( seq );
+ initPoseParameters( );
+
+ mdl->m_angles.Init();
+ mdl->m_origin.Init();
+
+ Vector size;
+ VectorSubtract( hdr->hull_max(), hdr->hull_min(), size );
+
+ float eyeheight = hdr->hull_min().z + 0.9 * size.z;
+
+ if ( hdr->GetNumAttachments() > 0 )
+ {
+ for (int i = 0; i < hdr->GetNumAttachments(); i++)
+ {
+ const mstudioattachment_t &attachment = hdr->pAttachment( i );
+ int iBone = hdr->GetAttachmentBone( i );
+
+ if ( Q_stricmp( attachment.pszName(), "eyes" ) )
+ continue;
+
+ mstudiobone_t *bone = hdr->pBone( iBone );
+ if ( !bone )
+ continue;
+
+ matrix3x4_t boneToPose;
+ MatrixInvert( bone->poseToBone, boneToPose );
+
+ matrix3x4_t attachmentPoseToLocal;
+ ConcatTransforms( boneToPose, attachment.local, attachmentPoseToLocal );
+
+ Vector localSpaceEyePosition;
+ VectorITransform( vec3_origin, attachmentPoseToLocal, localSpaceEyePosition );
+
+ // Not sure why this must be negative?
+ eyeheight = -localSpaceEyePosition.z + hdr->hull_min().z;
+ break;
+ }
+ }
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( mdl->GetFileName( ), mdl->GetKeyValueText( seq ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ float flEyeheight = pkvAllFaceposer->GetFloat( "eye_height", -9999.0f );
+ if ( flEyeheight != -9999.0f )
+ {
+ eyeheight = flEyeheight;
+ }
+ }
+ }
+
+ seqKeyValues->deleteThis();
+
+ mdl->m_origin.x = size.z * .65f;
+ mdl->m_origin.z += eyeheight;
+
+ CUtlVector< StudioModel * > modellist;
+
+ modellist.AddToTail( models->GetActiveStudioModel() );
+
+ int i;
+ if ( models->CountVisibleModels() > 0 )
+ {
+ modellist.RemoveAll();
+ for ( i = 0; i < models->Count(); i++ )
+ {
+ if ( models->IsModelShownIn3DView( i ) )
+ {
+ modellist.AddToTail( models->GetStudioModel( i ) );
+ }
+ }
+ }
+
+ int modelcount = modellist.Count();
+ int countover2 = modelcount / 2;
+ int ydelta = GetModelGap();
+ int yoffset = -countover2 * ydelta;
+ for ( i = 0 ; i < modelcount; i++ )
+ {
+ if ( models->GetStudioHeader( i ) == hdr )
+ {
+ mdl->m_origin.y = -yoffset;
+ }
+ yoffset += ydelta;
+ }
+
+ g_pMatSysWindow->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float ControlPanel::GetModelGap( void )
+{
+ return slModelGap->getValue();
+}
+
+void
+ControlPanel::centerView()
+{
+ StudioModel *pModel = models->GetActiveStudioModel();
+ if ( !pModel )
+ return;
+
+ Vector min, max;
+ models->GetActiveStudioModel()->ExtractBbox (min, max);
+
+ float dx = max[0] - min[0];
+ float dy = max[1] - min[1];
+ float dz = max[2] - min[2];
+ float d = dx;
+ if (dy > d)
+ d = dy;
+ if (dz > d)
+ d = dz;
+ pModel->m_origin[0] = d * 1.0f;
+ pModel->m_origin[1] = 0;
+ pModel->m_origin[2] = min[2] + dz / 2;
+ pModel->m_angles[0] = 0.0f;
+ pModel->m_angles[1] = 0.0f;
+ pModel->m_angles[2] = 0.0f;
+ g_viewerSettings.lightrot.x = 0.f;
+ g_viewerSettings.lightrot.y = -180.0f;
+ g_viewerSettings.lightrot.z = 0.0f;
+ g_pMatSysWindow->redraw();
+}
+
+bool ControlPanel::Close()
+{
+ int index = g_pExpressionClass->getSelectedIndex();
+ CExpClass *cl = expressions->GetClass( index );
+ if ( !cl )
+ return true;
+
+ return expressions->CloseClass( cl );
+
+}
+
+bool ControlPanel::Closeall()
+{
+ bool retval = true;
+
+ while ( expressions->GetNumClasses() > 0 )
+ {
+ CExpClass *cl = expressions->GetClass( 0 );
+ if ( !cl )
+ break;
+
+ if ( !expressions->CloseClass( cl ) )
+ {
+ return false;
+ }
+ }
+ return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::Copy( void )
+{
+ g_pFlexPanel->CopyControllerSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::Paste( void )
+{
+ g_pFlexPanel->PasteControllerSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::Undo( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+ int index = active->GetSelectedExpression();
+ if ( index != -1 )
+ {
+ UndoExpression( index );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::Redo( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+ int index = active->GetSelectedExpression();
+ if ( index != -1 )
+ {
+ RedoExpression( index );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::UndoExpression( int index )
+{
+ if ( index == -1 )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( exp )
+ {
+ exp->Undo();
+ // Show the updated data
+ active->SelectExpression( index );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ControlPanel::RedoExpression( int index )
+{
+ if ( index == -1 )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( exp )
+ {
+ exp->Redo();
+ // Show the updated data
+ active->SelectExpression( index );
+ }
+}
+
+void ControlPanel::DeleteExpression( int index )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( exp )
+ {
+ Con_Printf( "Deleting expression %s : %s\n", exp->name, exp->description );
+
+ g_pFlexPanel->DeleteExpression( index );
+
+ active->SelectExpression( max( 0, index - 1 ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void ControlPanel::Think( float dt )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ControlPanel::AllToolsDriveSpeech( void )
+{
+ return cbAllWindowsDriveSpeech->isChecked();
+}
diff --git a/utils/hlfaceposer/controlpanel.h b/utils/hlfaceposer/controlpanel.h
new file mode 100644
index 0000000..7382051
--- /dev/null
+++ b/utils/hlfaceposer/controlpanel.h
@@ -0,0 +1,161 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CONTROLPANEL_H
+#define CONTROLPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+
+#include "faceposertoolwindow.h"
+
+#define IDC_TAB 1901
+#define IDC_RENDERMODE 2001
+#define IDC_GROUND 2003
+#define IDC_MOVEMENT 2004
+#define IDC_BACKGROUND 2005
+#define IDC_HITBOXES 2006
+#define IDC_BONES 2007
+#define IDC_ATTACHMENTS 2008
+#define IDC_PHYSICSMODEL 2009
+#define IDC_PHYSICSHIGHLIGHT 2010
+#define IDC_MODELSPACING 2011
+#define IDC_TOOLSDRIVEMOUTH 2012
+
+#define IDC_SEQUENCE 3001
+#define IDC_SPEEDSCALE 3002
+#define IDC_PRIMARYBLEND 3003
+#define IDC_SECONDARYBLEND 3004
+
+#define IDC_BODYPART 4001
+#define IDC_SUBMODEL 4002
+#define IDC_CONTROLLER 4003
+#define IDC_CONTROLLERVALUE 4004
+#define IDC_SKINS 4005
+
+#define IDC_EXPRESSIONCLASS 5001
+#define IDC_EXPRESSIONTRAY 5002
+#define IDC_ANIMATIONBROWSER 5003
+
+class mxTab;
+class mxChoice;
+class mxCheckBox;
+class mxSlider;
+class mxLineEdit;
+class mxLabel;
+class mxButton;
+class MatSysWindow;
+class TextureWindow;
+class mxExpressionTray;
+class FlexPanel;
+class PhonemeEditor;
+class mxExpressionTab;
+class mxExpressionSlider;
+class ExpressionTool;
+class CChoreoView;
+
+
+class ControlPanel : public mxWindow, public IFacePoserToolWindow
+{
+ typedef mxWindow BaseClass;
+
+ mxTab *tab;
+ mxChoice *cRenderMode;
+ mxCheckBox *cbGround, *cbMovement, *cbBackground;
+ mxChoice *cSequence;
+ mxSlider *slSpeedScale;
+ mxLabel *lSpeedScale;
+ mxChoice *cBodypart, *cController, *cSubmodel;
+ mxSlider *slController;
+ mxChoice *cSkin;
+ mxLabel *lModelInfo1, *lModelInfo2;
+
+ mxLineEdit *leMeshScale, *leBoneScale;
+ mxSlider *slModelGap;
+
+ mxCheckBox *cbAllWindowsDriveSpeech;
+
+public:
+ // CREATORS
+ ControlPanel (mxWindow *parent);
+ virtual ~ControlPanel ();
+
+ // MANIPULATORS
+
+ virtual int handleEvent (mxEvent *event);
+ virtual void redraw();
+
+ virtual void OnDelete();
+ virtual bool CanClose();
+
+ virtual void Think( float dt );
+
+ void dumpModelInfo ();
+ void ChangeModel( const char *filename );
+
+ void setRenderMode (int mode);
+ void setShowGround (bool b);
+ void setShowMovement (bool b);
+ void setShowBackground (bool b);
+ void setHighlightBone( int index );
+
+ void initSequenceChoices( );
+ void setSequence (int index);
+
+ void setSpeed( float value );
+
+ void initPoseParameters ();
+ void setBlend(int index, float value );
+
+ void initBodypartChoices();
+ void setBodypart (int index);
+ void setSubmodel (int index);
+
+ void initBoneControllerChoices();
+ void setBoneController (int index);
+ void setBoneControllerValue (int index, float value);
+
+ void initSkinChoices();
+
+ void setModelInfo ();
+
+ void centerView ();
+
+ void fullscreen ();
+
+ void CenterOnFace( void );
+
+ void PositionControls( int width, int height );
+
+ bool CloseClass( int classindex );
+
+ bool Close();
+ bool Closeall();
+
+ void Copy( void );
+ void Paste( void );
+
+ void Undo( void );
+ void Redo( void );
+
+ void UndoExpression( int index );
+ void RedoExpression( int index );
+
+ void DeleteExpression( int index );
+
+ float GetModelGap( void );
+
+ bool AllToolsDriveSpeech( void );
+
+};
+
+extern ControlPanel *g_pControlPanel;
+
+#endif // CONTROLPANEL_H
diff --git a/utils/hlfaceposer/curveeditorhelpers.h b/utils/hlfaceposer/curveeditorhelpers.h
new file mode 100644
index 0000000..7a3e22e
--- /dev/null
+++ b/utils/hlfaceposer/curveeditorhelpers.h
@@ -0,0 +1,370 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef CURVEEDITORHELPERS_H
+#define CURVEEDITORHELPERS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxtk/mx.h"
+
+struct CExpressionSample;
+
+template< class T >
+class CCurveEditorHelper
+{
+public:
+ CCurveEditorHelper( T *outer );
+
+ int GetBestCurveTypeForSelectedSamples( bool reflect );
+ int CountSelected( bool reflect );
+ void ChangeCurveType( bool forward, bool shiftdown, bool altdown );
+ void SetCurveTypeForSelectedSamples( bool reflect, int curvetype );
+ void SetCurveTypeForSample( int curvetype, CExpressionSample *sample );
+ void ToggleHoldTypeForSelectedSamples( bool reflect );
+ void ToggleHoldTypeForSample( CExpressionSample *sample );
+
+ bool HelperHandleEvent( mxEvent *event );
+
+private:
+ T *GetOuter();
+
+private:
+
+ T *m_pOuter;
+};
+
+template< class T >
+CCurveEditorHelper<T>::CCurveEditorHelper( T *pOuter ) :
+ m_pOuter( pOuter )
+{
+ Assert( pOuter );
+}
+
+template< class T >
+T *CCurveEditorHelper<T>::GetOuter()
+{
+ return m_pOuter;
+}
+
+template< class T >
+int CCurveEditorHelper<T>::GetBestCurveTypeForSelectedSamples( bool reflect )
+{
+ int numSelected = CountSelected( reflect );
+ if ( !numSelected )
+ return CURVE_DEFAULT;
+
+ CUtlMap< int, int > counts( 0, 0, DefLessFunc( int ) );
+
+ CUtlVector< T * > workList;
+ GetOuter()->GetWorkList( reflect, workList );
+
+ for ( int w = 0; w < workList.Count(); ++w )
+ {
+ int numSamples = workList[ w ]->NumSamples();
+ if ( !numSamples )
+ continue;
+
+ for ( int i = numSamples - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = workList[ w ]->GetSample( i );
+ if ( !sample->selected )
+ continue;
+
+ int curveType = sample->GetCurveType();
+ int idx = counts.Find( curveType );
+ if ( idx == counts.InvalidIndex() )
+ {
+ idx = counts.Insert( curveType, 0 );
+ }
+
+ counts[ idx ]++;
+ }
+ }
+
+ int maxType = CURVE_DEFAULT;
+ int maxCount = -1;
+
+ for ( int i = counts.FirstInorder(); i != counts.InvalidIndex(); i = counts.NextInorder( i ) )
+ {
+ if ( counts[ i ] > maxType )
+ {
+ maxCount = counts[ i ];
+ maxType = counts.Key( i );
+ }
+ }
+
+ return maxType;
+}
+
+template< class T >
+int CCurveEditorHelper<T>::CountSelected( bool reflect )
+{
+ int numSelected = 0;
+
+ CUtlVector< T * > workList;
+ GetOuter()->GetWorkList( reflect, workList );
+
+ for ( int w = 0; w < workList.Count(); ++w )
+ {
+ int numSamples = workList[ w ]->NumSamples();
+ if ( !numSamples )
+ continue;
+
+ for ( int i = 0 ; i < numSamples; ++i )
+ {
+ CExpressionSample *sample = workList[ w ]->GetSample( i );
+ if ( !sample || !sample->selected )
+ continue;
+
+ ++numSelected;
+ }
+ }
+
+ return numSelected;
+}
+
+template< class T >
+void CCurveEditorHelper<T>::ChangeCurveType( bool forward, bool shiftdown, bool altdown )
+{
+ // If holding ctrl and shift, only do inbound
+ bool inbound = shiftdown;
+ // if holding ctrl, shift + alt, do both inbound and outbound
+ bool outbound = !shiftdown || altdown;
+ // if holding ctrl + alt, do outbound
+ // if holding just ctrl, do outbound
+
+ int numSelected = CountSelected( false );
+ if ( !numSelected )
+ return;
+
+ int curveType = GetBestCurveTypeForSelectedSamples( false );
+
+ int sides[ 2 ];
+ Interpolator_CurveInterpolatorsForType( curveType, sides[ 0 ], sides[ 1 ] );
+
+ int dir = forward ? 1 : -1;
+ for ( int i = 0; i < 2; ++i )
+ {
+ if ( i == 0 && !inbound )
+ continue;
+ if ( i == 1 && !outbound )
+ continue;
+
+ sides[ i ] += dir;
+ if ( sides[ i ] < 0 )
+ {
+ sides[ i ] = NUM_INTERPOLATE_TYPES - 1;
+ }
+ else if ( sides[ i ] >= NUM_INTERPOLATE_TYPES )
+ {
+ sides[ i ] = INTERPOLATE_DEFAULT;
+ }
+ }
+
+ curveType = MAKE_CURVE_TYPE( sides[ 0 ], sides[ 1 ] );
+ SetCurveTypeForSelectedSamples( false, curveType );
+}
+
+template< class T >
+void CCurveEditorHelper<T>::SetCurveTypeForSelectedSamples( bool reflect, int curvetype )
+{
+ int numSelected = CountSelected( reflect );
+ if ( !numSelected )
+ return;
+
+ GetOuter()->PreDataChanged( "Set curve type" );
+
+ CUtlVector< T * > workList;
+ GetOuter()->GetWorkList( reflect, workList );
+
+ for ( int w = 0; w < workList.Count(); ++w )
+ {
+ int numSamples = workList[ w ]->NumSamples();
+
+ for ( int i = 0 ; i < numSamples; ++i )
+ {
+ CExpressionSample *sample = workList[ w ]->GetSample( i );
+ if ( !sample->selected )
+ continue;
+
+ sample->SetCurveType( curvetype );
+ }
+ }
+
+ GetOuter()->PostDataChanged( "Set curve type" );
+}
+
+template< class T >
+void CCurveEditorHelper<T>::SetCurveTypeForSample( int curvetype, CExpressionSample *sample )
+{
+ GetOuter()->PreDataChanged( "Set curve type" );
+
+ sample->SetCurveType( curvetype );
+
+ GetOuter()->PostDataChanged( "Set curve type" );
+}
+
+template< class T >
+void CCurveEditorHelper<T>::ToggleHoldTypeForSelectedSamples( bool reflect )
+{
+ int numSelected = CountSelected( reflect );
+ if ( !numSelected )
+ return;
+
+ GetOuter()->PreDataChanged( "Set hold out value" );
+
+ CUtlVector< T * > workList;
+ GetOuter()->GetWorkList( reflect, workList );
+
+ for ( int w = 0; w < workList.Count(); ++w )
+ {
+ int numSamples = workList[ w ]->NumSamples();
+
+ int newValue = -1;
+
+ for ( int i = 0 ; i < numSamples; ++i )
+ {
+ CExpressionSample *sample = workList[ w ]->GetSample( i );
+ if ( !sample->selected )
+ continue;
+
+ // First one controls setting
+ int l, r;
+ Interpolator_CurveInterpolatorsForType( sample->GetCurveType(), l, r );
+
+ if ( newValue == -1 )
+ {
+ newValue = ( r == INTERPOLATE_HOLD ) ? 0 : 1;
+ }
+
+ int newCurveType = MAKE_CURVE_TYPE( l, newValue == 1 ? INTERPOLATE_HOLD : l );
+ sample->SetCurveType( newCurveType );
+ }
+ }
+
+ GetOuter()->PostDataChanged( "Set hold out value" );
+}
+
+template< class T >
+void CCurveEditorHelper<T>::ToggleHoldTypeForSample( CExpressionSample *sample )
+{
+ GetOuter()->PreDataChanged( "Set hold out value" );
+
+ int l, r;
+ Interpolator_CurveInterpolatorsForType( sample->GetCurveType(), l, r );
+
+ if ( r == INTERPOLATE_HOLD )
+ {
+ r = l;
+ }
+ else
+ {
+ r = INTERPOLATE_HOLD;
+ }
+
+ int newCurveType = MAKE_CURVE_TYPE( l, r );
+ sample->SetCurveType( newCurveType );
+
+ GetOuter()->PostDataChanged( "Set hold out value" );
+}
+
+template< class T >
+bool CCurveEditorHelper<T>::HelperHandleEvent( mxEvent *event )
+{
+ bool handled = false;
+
+ switch ( event->event )
+ {
+ case mxEvent::KeyDown:
+ {
+ switch ( event->key )
+ {
+ default:
+ // Hotkey pressed
+ if ( event->key >= '0' &&
+ event->key <= '9' )
+ {
+ bool shiftdown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
+
+ handled = true;
+ // Get curve type
+ int curveType = Interpolator_CurveTypeForHotkey( event->key );
+ if ( curveType >= 0 )
+ {
+ if ( CountSelected( shiftdown ) <= 0 )
+ {
+ GetOuter()->SetMousePositionForEvent( event );
+
+ CExpressionSample *hover = GetOuter()->GetSampleUnderMouse( event->x, event->y, 0.0f );
+
+ // Deal with highlighted item
+ if ( hover )
+ {
+ SetCurveTypeForSample( curveType, hover );
+ }
+ }
+ else
+ {
+ SetCurveTypeForSelectedSamples( shiftdown, curveType );
+ }
+ }
+ }
+ break;
+ case 'H':
+ {
+ handled = true;
+
+ bool shiftdown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
+
+ if ( CountSelected( shiftdown ) <= 0 )
+ {
+ GetOuter()->SetMousePositionForEvent( event );
+
+ CExpressionSample *hover = GetOuter()->GetSampleUnderMouse( event->x, event->y, 0.0f );
+
+ // Deal with highlighted item
+ if ( hover )
+ {
+ ToggleHoldTypeForSample( hover );
+ }
+ }
+ else
+ {
+ ToggleHoldTypeForSelectedSamples( shiftdown );
+ }
+ }
+ break;
+ case VK_UP:
+ {
+ bool shiftdown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
+ bool altdown = GetAsyncKeyState( VK_MENU ) ? true : false;
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ ChangeCurveType( false, shiftdown, altdown );
+ }
+ }
+ break;
+ case VK_DOWN:
+ {
+ bool shiftdown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
+ bool altdown = GetAsyncKeyState( VK_MENU ) ? true : false;
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ ChangeCurveType( true, shiftdown, altdown );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return handled;
+}
+
+#endif // CURVEEDITORHELPERS_H
+
diff --git a/utils/hlfaceposer/eventproperties.cpp b/utils/hlfaceposer/eventproperties.cpp
new file mode 100644
index 0000000..a85b73c
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties.cpp
@@ -0,0 +1,657 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "mathlib/mathlib.h"
+#include "choreochannel.h"
+#include "choreoactor.h"
+#include "filesystem.h"
+#include "scriplib.h"
+
+#include "eventproperties_expression.h"
+#include "eventproperties_face.h"
+#include "eventproperties_firetrigger.h"
+#include "eventproperties_flexanimation.h"
+#include "eventproperties_generic.h"
+#include "eventproperties_gesture.h"
+#include "eventproperties_interrupt.h"
+#include "eventproperties_lookat.h"
+#include "eventproperties_moveto.h"
+#include "eventproperties_permitresponses.h"
+#include "eventproperties_sequence.h"
+#include "eventproperties_speak.h"
+#include "eventproperties_subscene.h"
+
+void CBaseEventPropertiesDialog::PopulateTagList( CEventParams *params )
+{
+ CChoreoScene *scene = params->m_pScene;
+ if ( !scene )
+ return;
+
+ HWND control = GetControl( IDC_TAGS );
+ if ( control )
+ {
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)va( "\"%s\" \"%s\"", params->m_szTagName, params->m_szTagWav ) );
+
+ for ( int i = 0; i < scene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannel *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0 ; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEvent *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ if ( e->GetNumRelativeTags() <= 0 )
+ continue;
+
+ // add each tag to combo box
+ for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
+ {
+ CEventRelativeTag *tag = e->GetRelativeTag( t );
+ if ( !tag )
+ continue;
+
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
+ }
+ }
+ }
+ }
+ }
+}
+
+#include "mapentities.h"
+#include "utldict.h"
+
+struct CMapEntityData
+{
+ CMapEntityData()
+ {
+ origin.Init();
+ angles.Init();
+ }
+
+ Vector origin;
+ QAngle angles;
+};
+
+class CMapEntities : public IMapEntities
+{
+public:
+ CMapEntities();
+ ~CMapEntities();
+
+ virtual void CheckUpdateMap( char const *mapname );
+ virtual bool LookupOrigin( char const *name, Vector& origin, QAngle& angles )
+ {
+ int idx = FindNamedEntity( name );
+ if ( idx == -1 )
+ {
+ origin.Init();
+ angles.Init();
+ return false;
+ }
+
+ CMapEntityData *e = &m_Entities[ idx ];
+ Assert( e );
+ origin = e->origin;
+ angles = e->angles;
+ return true;
+ }
+
+ virtual int Count( void );
+ virtual char const *GetName( int number );
+
+ int FindNamedEntity( char const *name );
+
+private:
+ char m_szCurrentMap[ 1024 ];
+
+ CUtlDict< CMapEntityData, int > m_Entities;
+};
+
+static CMapEntities g_MapEntities;
+// Expose to rest of tool
+IMapEntities *mapentities = &g_MapEntities;
+
+CMapEntities::CMapEntities()
+{
+ m_szCurrentMap[ 0 ] = 0;
+}
+
+CMapEntities::~CMapEntities()
+{
+ m_Entities.RemoveAll();
+}
+
+int CMapEntities::FindNamedEntity( char const *name )
+{
+ char lowername[ 128 ];
+ strcpy( lowername, name );
+ _strlwr( lowername );
+
+ int index = m_Entities.Find( lowername );
+ if ( index == m_Entities.InvalidIndex() )
+ return -1;
+
+ return index;
+}
+
+#include "bspfile.h"
+
+void CMapEntities::CheckUpdateMap( char const *mapname )
+{
+ if ( !mapname || !mapname[ 0 ] )
+ return;
+
+ if ( !stricmp( mapname, m_szCurrentMap ) )
+ return;
+
+ // Latch off the name of the map
+ Q_strncpy( m_szCurrentMap, mapname, sizeof( m_szCurrentMap ) );
+
+ // Load names from map
+ m_Entities.RemoveAll();
+
+ FileHandle_t hfile = filesystem->Open( mapname, "rb" );
+ if ( hfile == FILESYSTEM_INVALID_HANDLE )
+ return;
+
+ dheader_t header;
+ filesystem->Read( &header, sizeof( header ), hfile );
+
+ // Check the header
+ if ( header.ident != IDBSPHEADER ||
+ header.version < MINBSPVERSION || header.version > BSPVERSION )
+ {
+ Con_ErrorPrintf( "BSP file %s is wrong version (%i), expected (%i)\n",
+ mapname,
+ header.version,
+ BSPVERSION );
+
+ filesystem->Close( hfile );
+ return;
+ }
+
+ // Find the LUMP_PAKFILE offset
+ lump_t *entlump = &header.lumps[ LUMP_ENTITIES ];
+ if ( entlump->filelen <= 0 )
+ {
+ Con_ErrorPrintf( "BSP file %s is missing entity lump\n", mapname );
+
+ // It's empty or only contains a file header ( so there are no entries ), so don't add to search paths
+ filesystem->Close( hfile );
+ return;
+ }
+
+ // Seek to correct position
+ filesystem->Seek( hfile, entlump->fileofs, FILESYSTEM_SEEK_HEAD );
+
+ char *buffer = new char[ entlump->filelen + 1 ];
+ Assert( buffer );
+
+ filesystem->Read( buffer, entlump->filelen, hfile );
+
+ filesystem->Close( hfile );
+
+ buffer[ entlump->filelen ] = 0;
+
+ // Now we have entity buffer, now parse it
+ ParseFromMemory( buffer, entlump->filelen );
+
+ while ( 1 )
+ {
+ if (!GetToken (true))
+ break;
+
+ if (Q_stricmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ char name[ 256 ];
+ char origin[ 256 ];
+ char angles[ 256 ];
+
+ name[ 0 ] = 0;
+ origin[ 0 ] = 0;
+ angles[ 0 ] = 0;
+
+ do
+ {
+ char key[ 256 ];
+ char value[ 256 ];
+
+ if (!GetToken (true))
+ {
+ Error ("ParseEntity: EOF without closing brace");
+ }
+
+ if (!Q_stricmp (token, "}") )
+ break;
+
+ Q_strncpy( key, token, sizeof( key ) );
+
+ GetToken (false);
+
+ Q_strncpy( value, token, sizeof( value ) );
+
+ // Con_Printf( "Parsed %s -- %s\n", key, value );
+
+ if ( !Q_stricmp( key, "name" ) )
+ {
+ Q_strncpy( name, value, sizeof( name ) );
+ }
+ if ( !Q_stricmp( key, "targetname" ) )
+ {
+ Q_strncpy( name, value, sizeof( name ) );
+ }
+ if ( !Q_stricmp( key, "origin" ) )
+ {
+ Q_strncpy( origin, value, sizeof( origin ) );
+ }
+ if ( !Q_stricmp( key, "angles" ) )
+ {
+ Q_strncpy( angles, value, sizeof( angles ) );
+ }
+
+ } while (1);
+
+ if ( name[ 0 ] )
+ {
+ if ( FindNamedEntity( name ) == - 1 )
+ {
+ CMapEntityData ent;
+
+ float org[3];
+ if ( origin[ 0 ] )
+ {
+ if ( 3 == sscanf( origin, "%f %f %f", &org[ 0 ], &org[ 1 ], &org[ 2 ] ) )
+ {
+ ent.origin = Vector( org[ 0 ], org[ 1 ], org[ 2 ] );
+
+ // Con_Printf( "read %f %f %f for entity %s\n", org[0], org[1], org[2], name );
+ }
+ }
+ if ( angles[ 0 ] )
+ {
+ if ( 3 == sscanf( angles, "%f %f %f", &org[ 0 ], &org[ 1 ], &org[ 2 ] ) )
+ {
+ ent.angles = QAngle( org[ 0 ], org[ 1 ], org[ 2 ] );
+
+ // Con_Printf( "read %f %f %f for entity %s\n", org[0], org[1], org[2], name );
+ }
+ }
+
+ m_Entities.Insert( name, ent );
+ }
+ }
+ }
+
+ delete[] buffer;
+}
+
+int CMapEntities::Count( void )
+{
+ return m_Entities.Count();
+}
+
+char const *CMapEntities::GetName( int number )
+{
+ if ( number < 0 || number >= (int)m_Entities.Count() )
+ return NULL;
+
+ return m_Entities.GetElementName( number );
+}
+
+bool NameLessFunc( const char *const& name1, const char *const& name2 )
+{
+ if ( Q_stricmp( name1, name2 ) < 0 )
+ return true;
+ return false;
+}
+
+void CBaseEventPropertiesDialog::SetDialogTitle( CEventParams *params, char const *eventname, char const *desc )
+{
+ char sz[ 256 ];
+ Q_snprintf( sz, sizeof( sz ), " : %s", eventname );
+ Q_strncat( params->m_szDialogTitle, sz, sizeof( params->m_szDialogTitle ), COPY_ALL_CHARACTERS );
+ Q_snprintf( sz, sizeof( sz ), "%s:", desc );
+
+ // Set dialog title
+ SetWindowText( m_hDialog, params->m_szDialogTitle );
+ // Set type name field
+ SetDlgItemText( m_hDialog, IDC_TYPENAME, sz );
+ // Set event name
+ SetDlgItemText( m_hDialog, IDC_EVENTNAME, params->m_szName );
+}
+
+void CBaseEventPropertiesDialog::ShowControlsForEventType( CEventParams *params )
+{
+ // Special processing for various settings
+ if ( !params->m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+
+ if ( params->m_bFixedLength )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ ShowWindow( GetControl( IDC_CHECK_ENDTIME ), SW_HIDE );
+ }
+}
+
+void CBaseEventPropertiesDialog::InitControlData( CEventParams *params )
+{
+ SetDlgItemText( m_hDialog, IDC_STARTTIME, va( "%f", params->m_flStartTime ) );
+ SetDlgItemText( m_hDialog, IDC_ENDTIME, va( "%f", params->m_flEndTime ) );
+
+ SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_SETCHECK,
+ ( WPARAM ) params->m_bHasEndTime ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ if ( GetControl( IDC_CHECK_RESUMECONDITION ) != (HWND)0 )
+ {
+ SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_SETCHECK,
+ ( WPARAM ) params->m_bResumeCondition ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+ }
+
+ SendMessage( GetControl( IDC_CHECK_DISABLED ), BM_SETCHECK,
+ ( WPARAM ) params->m_bDisabled ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateTagList( params );
+}
+
+BOOL CBaseEventPropertiesDialog::InternalHandleMessage( CEventParams *params, HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& handled )
+{
+ handled = false;
+ switch(uMsg)
+ {
+ default:
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ default:
+ break;
+ case IDC_CHECK_DISABLED:
+ {
+ params->m_bDisabled = SendMessage( GetControl( IDC_CHECK_DISABLED ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ handled = true;
+ return TRUE;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void CBaseEventPropertiesDialog::PopulateNamedActorList( HWND wnd, CEventParams *params )
+{
+ int i;
+
+ char const *mapname = NULL;
+ if ( params->m_pScene )
+ {
+ mapname = params->m_pScene->GetMapname();
+ }
+
+ CUtlRBTree< char const *, int > m_SortedNames( 0, 0, NameLessFunc );
+
+ if ( mapname )
+ {
+ g_MapEntities.CheckUpdateMap( mapname );
+
+ for ( i = 0; i < g_MapEntities.Count(); i++ )
+ {
+ char const *name = g_MapEntities.GetName( i );
+ if ( name && name[ 0 ] )
+ {
+ m_SortedNames.Insert( name );
+ }
+ }
+ }
+
+ for ( i = 0 ; i < params->m_pScene->GetNumActors() ; i++ )
+ {
+ CChoreoActor *actor = params->m_pScene->GetActor( i );
+ if ( actor && actor->GetName() && actor->GetName()[0] )
+ {
+ if ( m_SortedNames.Find( actor->GetName() ) == m_SortedNames.InvalidIndex() )
+ {
+ m_SortedNames.Insert( actor->GetName() );
+ }
+ }
+ }
+
+ i = m_SortedNames.FirstInorder();
+ while ( i != m_SortedNames.InvalidIndex() )
+ {
+ char const *name = m_SortedNames[ i ];
+ if ( name && name[ 0 ] )
+ {
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)name );
+ }
+
+ i = m_SortedNames.NextInorder( i );
+ }
+
+ /*
+ // Note have to do this here, after posting data to the control, since we are storing a raw string pointer in m_SortedNames!!!
+ if ( allActors )
+ {
+ allActors->deleteThis();
+ }
+ */
+
+ // These events can also be directed at another player or named target, too
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!player" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!enemy" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!self" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!friend" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!speechtarget" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target1" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target2" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target3" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target4" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target5" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target6" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target7" );
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)"!target8" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static void
+//-----------------------------------------------------------------------------
+void CBaseEventPropertiesDialog::ParseTags( CEventParams *params )
+{
+ strcpy( params->m_szTagName, "" );
+ strcpy( params->m_szTagWav, "" );
+
+ if ( params->m_bUsesTag )
+ {
+ // Parse out the two tokens
+ char selectedText[ 512 ];
+ selectedText[ 0 ] = 0;
+ HWND control = GetControl( IDC_TAGS );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( selectedText ), (LPARAM)selectedText );
+ }
+
+ ParseFromMemory( selectedText, strlen( selectedText ) );
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ char tagname[ 256 ];
+ strcpy( tagname, token );
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ char wavename[ 256 ];
+ strcpy( wavename, token );
+
+ // Valid
+ strcpy( params->m_szTagName, tagname );
+ strcpy( params->m_szTagWav, wavename );
+
+ }
+ else
+ {
+ params->m_bUsesTag = false;
+ }
+ }
+ else
+ {
+ params->m_bUsesTag = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static void
+//-----------------------------------------------------------------------------
+void CBaseEventPropertiesDialog::UpdateTagRadioButtons( CEventParams *params )
+{
+ if ( params->m_bUsesTag )
+ {
+ SendMessage( GetControl( IDC_RELATIVESTART ), BM_SETCHECK, ( WPARAM )BST_CHECKED, (LPARAM)0 );
+ SendMessage( GetControl( IDC_ABSOLUTESTART ), BM_SETCHECK, ( WPARAM )BST_UNCHECKED, (LPARAM)0 );
+ }
+ else
+ {
+ SendMessage( GetControl( IDC_ABSOLUTESTART ), BM_SETCHECK, ( WPARAM )BST_CHECKED, (LPARAM)0 );
+ SendMessage( GetControl( IDC_RELATIVESTART ), BM_SETCHECK, ( WPARAM )BST_UNCHECKED, (LPARAM)0 );
+ }
+}
+
+void CBaseEventPropertiesDialog::GetSplineRect( HWND placeholder, RECT& rcOut )
+{
+ GetWindowRect( placeholder, &rcOut );
+ RECT rcDlg;
+ GetWindowRect( m_hDialog, &rcDlg );
+
+ OffsetRect( &rcOut, -rcDlg.left, -rcDlg.top );
+}
+
+void CBaseEventPropertiesDialog::DrawSpline( HDC hdc, HWND placeholder, CChoreoEvent *e )
+{
+ RECT rcOut;
+
+ GetSplineRect( placeholder, rcOut );
+
+ HBRUSH bg = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ) );
+ FillRect( hdc, &rcOut, bg );
+ DeleteObject( bg );
+
+ if ( !e )
+ return;
+
+ // Draw spline
+ float range = ( float )( rcOut.right - rcOut.left );
+ if ( range <= 1.0f )
+ return;
+
+ float height = ( float )( rcOut.bottom - rcOut.top );
+
+ HPEN pen = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_BTNTEXT ) );
+ HPEN oldPen = (HPEN)SelectObject( hdc, pen );
+
+ float duration = e->GetDuration();
+ float starttime = e->GetStartTime();
+
+ for ( int i = 0; i < (int)range; i++ )
+ {
+ float frac = ( float )i / ( range - 1 );
+
+ float scale = 1.0f - e->GetIntensity( starttime + frac * duration );
+
+ int h = ( int ) ( scale * ( height - 1 ) );
+
+ if ( i == 0 )
+ {
+ MoveToEx( hdc, rcOut.left + i, rcOut.top + h, NULL );
+ }
+ else
+ {
+ LineTo( hdc, rcOut.left + i, rcOut.top + h );
+ }
+ }
+
+ SelectObject( hdc, oldPen );
+
+ HBRUSH frame = CreateSolidBrush( GetSysColor( COLOR_BTNSHADOW ) );
+ InflateRect( &rcOut, 1, 1 );
+ FrameRect( hdc, &rcOut, frame );
+ DeleteObject( frame );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties( CEventParams *params )
+{
+ int iret = 1;
+ switch ( params->m_nType )
+ {
+ default:
+ break;
+ case CChoreoEvent::EXPRESSION:
+ return EventProperties_Expression( params );
+ case CChoreoEvent::LOOKAT:
+ return EventProperties_LookAt( params );
+ case CChoreoEvent::MOVETO:
+ return EventProperties_MoveTo( params );
+ case CChoreoEvent::SPEAK:
+ return EventProperties_Speak( params );
+ case CChoreoEvent::GESTURE:
+ return EventProperties_Gesture( params );
+ case CChoreoEvent::SEQUENCE:
+ return EventProperties_Sequence( params );
+ case CChoreoEvent::FACE:
+ return EventProperties_Face( params );
+ case CChoreoEvent::FIRETRIGGER:
+ return EventProperties_FireTrigger( params );
+ case CChoreoEvent::FLEXANIMATION:
+ return EventProperties_FlexAnimation( params );
+ case CChoreoEvent::SUBSCENE:
+ return EventProperties_SubScene( params );
+ case CChoreoEvent::INTERRUPT:
+ return EventProperties_Interrupt( params );
+ case CChoreoEvent::PERMIT_RESPONSES:
+ return EventProperties_PermitResponses( params );
+ case CChoreoEvent::GENERIC:
+ return EventProperties_Generic( params );
+ }
+
+ return iret;
+}
diff --git a/utils/hlfaceposer/eventproperties.h b/utils/hlfaceposer/eventproperties.h
new file mode 100644
index 0000000..24974ab
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties.h
@@ -0,0 +1,105 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_H
+#define EVENTPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+
+class CChoreoScene;
+class CChoreoEvent;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEventParams : public CBaseDialogParams
+{
+public:
+ // e.g. CChoreoEvent::GESTURE
+ int m_nType;
+
+ // Event descriptive name
+ char m_szName[ 256 ];
+
+ // Expression name/wav name/gesture name/look at name
+ char m_szParameters[ 256 ];
+ char m_szParameters2[ 256 ];
+ char m_szParameters3[ 256 ];
+
+ CChoreoScene *m_pScene;
+
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bHasEndTime;
+
+ CChoreoEvent *m_pEvent;
+
+ bool m_bDisabled;
+ bool m_bFixedLength;
+
+ bool m_bResumeCondition;
+
+ bool m_bLockBodyFacing;
+ float m_flDistanceToTarget;
+
+ bool m_bForceShortMovement;
+
+ bool m_bSyncToFollowingGesture;
+
+ bool m_bPlayOverScript;
+
+ bool m_bUsesTag;
+ char m_szTagName[ 256 ];
+ char m_szTagWav[ 256 ];
+
+ // For Lookat events
+ int pitch;
+ int yaw;
+ bool usepitchyaw;
+
+ // For speak
+ bool m_bCloseCaptionNoAttenuate;
+
+};
+
+int EventProperties( CEventParams *params );
+
+class CBaseEventPropertiesDialog
+{
+public:
+ virtual void InitDialog( HWND hwndDlg ) = 0;
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) = 0;
+ virtual void SetTitle() = 0;
+
+ HWND GetControl( int id ) { return GetDlgItem( m_hDialog, id ); }
+
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+protected:
+ virtual BOOL InternalHandleMessage( CEventParams *params, HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& handled );
+
+ void SetDialogTitle( CEventParams *params, char const *eventname, char const *desc );
+
+ void UpdateTagRadioButtons( CEventParams *params );
+ void PopulateTagList( CEventParams *params );
+ void ParseTags( CEventParams *params );
+
+ void PopulateNamedActorList( HWND wnd, CEventParams *params );
+
+ void GetSplineRect( HWND placeholder, RECT& rcOut );
+ void DrawSpline( HDC hdc, HWND placeholder, CChoreoEvent *e );
+
+protected:
+
+ HWND m_hDialog;
+};
+
+#endif // EVENTPROPERTIES_H
diff --git a/utils/hlfaceposer/eventproperties_expression.cpp b/utils/hlfaceposer/eventproperties_expression.cpp
new file mode 100644
index 0000000..b35097f
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_expression.cpp
@@ -0,0 +1,298 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+#include "expressions.h"
+#include "expclass.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesExpressionDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+private:
+
+ void PopulateExpressionList( HWND wnd );
+ void PopulateExpressionClass( HWND control, CEventParams *params );
+};
+
+void CEventPropertiesExpressionDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Expression", "Expression" );
+}
+
+void CEventPropertiesExpressionDialog::PopulateExpressionList( HWND wnd )
+{
+ for ( int i = 0 ; i < expressions->GetNumClasses() ; i++ )
+ {
+ CExpClass *cl = expressions->GetClass( i );
+ if( !cl )
+ continue;
+
+ // add text to combo box
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)cl->GetName() );
+ }
+}
+
+void CEventPropertiesExpressionDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ HWND choices2 = GetControl( IDC_EVENTCHOICES2 );
+ SendMessage( choices2, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices2, WM_SETTEXT , 0, (LPARAM)params->m_szParameters2 );
+
+ PopulateExpressionList( choices1 );
+ SendMessage( GetControl( IDC_CHOICES2PROMPT ), WM_SETTEXT, 0, (LPARAM)"Name:" );
+ PopulateExpressionClass( choices2, params );
+}
+
+void CEventPropertiesExpressionDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesExpressionDialog g_EventPropertiesExpressionDialog;
+
+void CEventPropertiesExpressionDialog::PopulateExpressionClass( HWND control, CEventParams *params )
+{
+ // Find parameter 1
+ for ( int c = 0; c < expressions->GetNumClasses(); c++ )
+ {
+ CExpClass *cl = expressions->GetClass( c );
+ if ( !cl )
+ continue;
+
+ if ( Q_stricmp( cl->GetName(), params->m_szParameters ) )
+ continue;
+
+ for ( int i = 0 ; i < cl->GetNumExpressions() ; i++ )
+ {
+ CExpression *exp = cl->GetExpression( i );
+ if ( exp )
+ {
+ // add text to combo box
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)exp->name );
+ }
+ }
+
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesExpressionDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesExpressionDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesExpressionDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesExpressionDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "%s/%s", g_Params.m_szParameters, g_Params.m_szParameters2 );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+
+ PopulateExpressionClass( GetControl( IDC_EVENTCHOICES2 ), &g_Params );
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES2:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ if ( g_Params.m_nType != CChoreoEvent::MOVETO )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters2 ), (LPARAM)g_Params.m_szParameters2 );
+ }
+ else
+ {
+ char buf1[ 256 ];
+
+ SendMessage( GetControl( IDC_EVENTCHOICES2 ), WM_GETTEXT, (WPARAM)sizeof( buf1 ), (LPARAM)buf1 );
+
+ Q_snprintf( g_Params.m_szParameters2, sizeof( g_Params.m_szParameters2 ), "%s", buf1 );
+ }
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Expression( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_EXPRESSION ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesExpressionDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_expression.h b/utils/hlfaceposer/eventproperties_expression.h
new file mode 100644
index 0000000..ad52561
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_expression.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_EXPRESSION_H
+#define EVENTPROPERTIES_EXPRESSION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Expression( CEventParams *params );
+
+#endif // EVENTPROPERTIES_EXPRESSION_H
diff --git a/utils/hlfaceposer/eventproperties_face.cpp b/utils/hlfaceposer/eventproperties_face.cpp
new file mode 100644
index 0000000..54e6038
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_face.cpp
@@ -0,0 +1,234 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesFaceDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+};
+
+void CEventPropertiesFaceDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Face", "Face Actor" );
+}
+
+void CEventPropertiesFaceDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ PopulateNamedActorList( choices1, params );
+
+ SendMessage( GetControl( IDC_CHECK_LOCKBODYFACING ), BM_SETCHECK,
+ ( WPARAM ) params->m_bLockBodyFacing ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+
+}
+
+void CEventPropertiesFaceDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesFaceDialog g_EventPropertiesFaceDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesFaceDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesFaceDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesFaceDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesFaceDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "Face %s", g_Params.m_szParameters );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_CHECK_LOCKBODYFACING:
+ {
+ g_Params.m_bLockBodyFacing = SendMessage( GetControl( IDC_CHECK_LOCKBODYFACING ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Face( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_FACE ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesFaceDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_face.h b/utils/hlfaceposer/eventproperties_face.h
new file mode 100644
index 0000000..95f827b
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_face.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_FACE_H
+#define EVENTPROPERTIES_FACE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Face( CEventParams *params );
+
+#endif // EVENTPROPERTIES_FACE_H
diff --git a/utils/hlfaceposer/eventproperties_firetrigger.cpp b/utils/hlfaceposer/eventproperties_firetrigger.cpp
new file mode 100644
index 0000000..78e8894
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_firetrigger.cpp
@@ -0,0 +1,240 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesFireTriggerDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void PopulateTriggerList( HWND wnd );
+};
+
+void CEventPropertiesFireTriggerDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "FireTrigger", "Scene Trigger" );
+}
+
+void CEventPropertiesFireTriggerDialog::PopulateTriggerList( HWND wnd )
+{
+ for ( int i = 0 ; i < 16; i++ )
+ {
+ char szName[256];
+
+ sprintf( szName, "%d", i + 1 );
+
+ // add text to combo box
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)szName );
+ }
+}
+
+void CEventPropertiesFireTriggerDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ PopulateTriggerList( choices1 );
+}
+
+void CEventPropertiesFireTriggerDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesFireTriggerDialog g_EventPropertiesFireTriggerDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesFireTriggerDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesFireTriggerDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesFireTriggerDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesFireTriggerDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "Firetrigger %s", g_Params.m_szParameters );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_FireTrigger( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_FIRETRIGGER ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesFireTriggerDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_firetrigger.h b/utils/hlfaceposer/eventproperties_firetrigger.h
new file mode 100644
index 0000000..24b19c9
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_firetrigger.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_FIRETRIGGER_H
+#define EVENTPROPERTIES_FIRETRIGGER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_FireTrigger( CEventParams *params );
+
+#endif // EVENTPROPERTIES_FIRETRIGGER_H
diff --git a/utils/hlfaceposer/eventproperties_flexanimation.cpp b/utils/hlfaceposer/eventproperties_flexanimation.cpp
new file mode 100644
index 0000000..08c21ae
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_flexanimation.cpp
@@ -0,0 +1,203 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesFlexAnimationDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+};
+
+void CEventPropertiesFlexAnimationDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "FlexAnimation", "Flex Controller Animation" );
+}
+
+void CEventPropertiesFlexAnimationDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+}
+
+void CEventPropertiesFlexAnimationDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesFlexAnimationDialog g_EventPropertiesFlexAnimationDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesFlexAnimationDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesFlexAnimationDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesFlexAnimationDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesFlexAnimationDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, "Facial Animation", sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_FlexAnimation( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_FLEXANIMATION ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesFlexAnimationDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_flexanimation.h b/utils/hlfaceposer/eventproperties_flexanimation.h
new file mode 100644
index 0000000..ecf86e5
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_flexanimation.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_FLEXANIMATION_H
+#define EVENTPROPERTIES_FLEXANIMATION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_FlexAnimation( CEventParams *params );
+
+#endif // EVENTPROPERTIES_FLEXANIMATION_H
diff --git a/utils/hlfaceposer/eventproperties_generic.cpp b/utils/hlfaceposer/eventproperties_generic.cpp
new file mode 100644
index 0000000..6cc5888
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_generic.cpp
@@ -0,0 +1,272 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesGenericDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void PopulateAIGeneric( HWND control, CEventParams *params );
+};
+
+void CEventPropertiesGenericDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Generic", "Generic AI Event (text)" );
+}
+
+void CEventPropertiesGenericDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ HWND choices2 = GetControl( IDC_EVENTCHOICES2 );
+ SendMessage( choices2, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices2, WM_SETTEXT , 0, (LPARAM)params->m_szParameters2 );
+
+ SendMessage( GetControl( IDC_FILENAME ), WM_SETTEXT, sizeof( params->m_szParameters ), (LPARAM)params->m_szParameters );
+
+ PopulateAIGeneric( choices1, params );
+ PopulateNamedActorList( choices2, params );
+}
+
+void CEventPropertiesGenericDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesGenericDialog g_EventPropertiesGenericDialog;
+
+void CEventPropertiesGenericDialog::PopulateAIGeneric( HWND control, CEventParams *params )
+{
+ // FIXME: this should load from a config file
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_BLINK" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_HOLSTER" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_UNHOLSTER" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_AIM" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_RANDOMLOOK" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_RANDOMFACEFLEX" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_RANDOMHEADFLEX" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_IGNORECOLLISION" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"AI_DISABLEAI" );
+
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesGenericDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesGenericDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesGenericDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesGenericDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "%s to %s", g_Params.m_szParameters, g_Params.m_szParameters2 );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES2:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ if ( g_Params.m_nType != CChoreoEvent::MOVETO )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters2 ), (LPARAM)g_Params.m_szParameters2 );
+ }
+ else
+ {
+ char buf1[ 256 ];
+
+ SendMessage( GetControl( IDC_EVENTCHOICES2 ), WM_GETTEXT, (WPARAM)sizeof( buf1 ), (LPARAM)buf1 );
+
+ Q_snprintf( g_Params.m_szParameters2, sizeof( g_Params.m_szParameters2 ), "%s", buf1 );
+ }
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Generic( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_GENERIC ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesGenericDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_generic.h b/utils/hlfaceposer/eventproperties_generic.h
new file mode 100644
index 0000000..01391ff
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_generic.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_GENERIC_H
+#define EVENTPROPERTIES_GENERIC_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Generic( CEventParams *params );
+
+#endif // EVENTPROPERTIES_GENERIC_H
diff --git a/utils/hlfaceposer/eventproperties_gesture.cpp b/utils/hlfaceposer/eventproperties_gesture.cpp
new file mode 100644
index 0000000..f02f320
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_gesture.cpp
@@ -0,0 +1,297 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+#include "StudioModel.h"
+#include "faceposer_models.h"
+#include "KeyValues.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesGestureDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+ void PopulateGestureList( HWND wnd );
+
+ bool CheckSequenceType( StudioModel *model, int iSequence, char *szType );
+};
+
+void CEventPropertiesGestureDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Gesture", "Gesture" );
+}
+
+void CEventPropertiesGestureDialog::PopulateGestureList( HWND wnd )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ int i;
+ for (i = 0; i < hdr->GetNumSeq(); i++)
+ {
+ if (CheckSequenceType( models->GetActiveStudioModel(), i, "gesture" ))
+ {
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)hdr->pSeqdesc(i).pszLabel() );
+ }
+ }
+ for (i = 0; i < hdr->GetNumSeq(); i++)
+ {
+ if (CheckSequenceType( models->GetActiveStudioModel(), i, "posture" ))
+ {
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)hdr->pSeqdesc(i).pszLabel() );
+ }
+ }
+ }
+}
+
+void CEventPropertiesGestureDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ SendMessage( GetControl( IDC_CHECK_SYNCTOFOLLOWINGGESTURE ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bSyncToFollowingGesture ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateGestureList( choices1 );
+}
+
+void CEventPropertiesGestureDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesGestureDialog g_EventPropertiesGestureDialog;
+
+bool CEventPropertiesGestureDialog::CheckSequenceType( StudioModel *model, int iSequence, char *szType )
+{
+ KeyValues *seqKeyValues = new KeyValues("");
+ bool isType = false;
+ if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( iSequence ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ KeyValues *pkvType = pkvAllFaceposer->FindKey("type");
+
+ if (pkvType)
+ {
+ isType = (stricmp( pkvType->GetString(), szType ) == 0) ? true : false;
+ }
+ }
+ }
+
+ seqKeyValues->deleteThis();
+
+ return isType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesGestureDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+
+ // NULL Gesture doesn't have these controls either
+ if ( g_Params.m_nType == CChoreoEvent::GESTURE &&
+ !Q_stricmp( g_Params.m_szName, "NULL" ) )
+ {
+ ShowWindow( GetControl( IDC_EVENTNAME ), SW_HIDE );
+ ShowWindow( GetControl( IDC_TAGS ), SW_HIDE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesGestureDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesGestureDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesGestureDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, g_Params.m_szParameters, sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_CHECK_SYNCTOFOLLOWINGGESTURE:
+ {
+ g_Params.m_bSyncToFollowingGesture = SendMessage( GetControl( IDC_CHECK_SYNCTOFOLLOWINGGESTURE ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Gesture( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_GESTURE ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesGestureDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_gesture.h b/utils/hlfaceposer/eventproperties_gesture.h
new file mode 100644
index 0000000..f0cf6f6
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_gesture.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_GESTURE_H
+#define EVENTPROPERTIES_GESTURE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Gesture( CEventParams *params );
+
+#endif // EVENTPROPERTIES_GESTURE_H
diff --git a/utils/hlfaceposer/eventproperties_interrupt.cpp b/utils/hlfaceposer/eventproperties_interrupt.cpp
new file mode 100644
index 0000000..7280f86
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_interrupt.cpp
@@ -0,0 +1,217 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesInterruptDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+};
+
+void CEventPropertiesInterruptDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Interrupt", "Interrupt" );
+}
+
+void CEventPropertiesInterruptDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+}
+
+void CEventPropertiesInterruptDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesInterruptDialog g_EventPropertiesInterruptDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesInterruptDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesInterruptDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesInterruptDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesInterruptDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, "Interrupt", sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Interrupt( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_INTERRUPT ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesInterruptDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_interrupt.h b/utils/hlfaceposer/eventproperties_interrupt.h
new file mode 100644
index 0000000..d1e2254
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_interrupt.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_INTERRUPT_H
+#define EVENTPROPERTIES_INTERRUPT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Interrupt( CEventParams *params );
+
+#endif // EVENTPROPERTIES_INTERRUPT_H
diff --git a/utils/hlfaceposer/eventproperties_lookat.cpp b/utils/hlfaceposer/eventproperties_lookat.cpp
new file mode 100644
index 0000000..48097b5
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_lookat.cpp
@@ -0,0 +1,327 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include <commctrl.h>
+
+static CEventParams g_Params;
+
+class CEventPropertiesLookAtDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void SetupLookAtControls( CEventParams *params );
+ void SetPitchYawText( CEventParams *params );
+};
+
+void CEventPropertiesLookAtDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "LookAt", "Look At Actor" );
+}
+
+void CEventPropertiesLookAtDialog::SetupLookAtControls( CEventParams *params )
+{
+ SetPitchYawText( params );
+
+ if ( params->pitch != 0 ||
+ params->yaw != 0 )
+ {
+ params->usepitchyaw = true;
+ }
+ else
+ {
+ params->usepitchyaw = false;
+ }
+
+ HWND control = GetControl( IDC_CHECK_LOOKAT );
+ SendMessage( control, BM_SETCHECK, (WPARAM) params->usepitchyaw ? BST_CHECKED : BST_UNCHECKED, 0 );
+
+ // Set up sliders
+ control = GetControl( IDC_SLIDER_PITCH );
+ SendMessage( control, TBM_SETRANGE, 0, (LPARAM)MAKELONG( -100, 100 ) );
+ SendMessage( control, TBM_SETPOS, 1, (LPARAM)(LONG)params->pitch );
+
+ control = GetControl( IDC_SLIDER_YAW );
+ SendMessage( control, TBM_SETRANGE, 0, (LPARAM)MAKELONG( -100, 100 ) );
+ SendMessage( control, TBM_SETPOS, 1, (LPARAM)(LONG)params->yaw );
+}
+
+void CEventPropertiesLookAtDialog::InitControlData( CEventParams *params )
+{
+ SetDlgItemText( m_hDialog, IDC_STARTTIME, va( "%f", g_Params.m_flStartTime ) );
+ SetDlgItemText( m_hDialog, IDC_ENDTIME, va( "%f", g_Params.m_flEndTime ) );
+ SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bHasEndTime ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bResumeCondition ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateTagList( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ SetupLookAtControls( params );
+ PopulateNamedActorList( choices1, params );
+}
+
+void CEventPropertiesLookAtDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesLookAtDialog g_EventPropertiesLookAtDialog;
+
+void CEventPropertiesLookAtDialog::SetPitchYawText( CEventParams *params )
+{
+ HWND control;
+
+ control = GetControl( IDC_STATIC_PITCHVAL );
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)va( "%i", params->pitch ) );
+ control = GetControl( IDC_STATIC_YAWVAL );
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)va( "%i", params->yaw ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesLookAtDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesLookAtDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesLookAtDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesLookAtDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "Look at %s", g_Params.m_szParameters );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_CHECK_LOOKAT:
+ {
+ HWND control = GetControl( IDC_CHECK_LOOKAT );
+ bool checked = SendMessage( control, BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !checked )
+ {
+ g_Params.yaw = 0;
+ g_Params.pitch = 0;
+
+ SetPitchYawText( &g_Params );
+
+ control = GetControl( IDC_SLIDER_PITCH );
+ SendMessage( control, TBM_SETPOS, 1, (LPARAM)(LONG)g_Params.pitch );
+
+ control = GetControl( IDC_SLIDER_YAW );
+ SendMessage( control, TBM_SETPOS, 1, (LPARAM)(LONG)g_Params.yaw );
+ }
+
+ }
+ break;
+ }
+ return TRUE;
+ case WM_HSCROLL:
+ {
+ HWND control = (HWND)lParam;
+ if ( control == GetControl( IDC_SLIDER_YAW ) ||
+ control == GetControl( IDC_SLIDER_PITCH ) )
+ {
+ g_Params.yaw = (float)SendMessage( GetControl( IDC_SLIDER_YAW ), TBM_GETPOS, 0, 0 );
+ g_Params.pitch = (float)SendMessage( GetControl( IDC_SLIDER_PITCH ), TBM_GETPOS, 0, 0 );
+
+ SetPitchYawText( &g_Params );
+
+ control = GetControl( IDC_CHECK_LOOKAT );
+ if ( g_Params.pitch != 0 ||
+ g_Params.yaw != 0 )
+ {
+ g_Params.usepitchyaw = true;
+ }
+ else
+ {
+ g_Params.usepitchyaw = false;
+ }
+
+ SendMessage( control, BM_SETCHECK, (WPARAM) g_Params.usepitchyaw ? BST_CHECKED : BST_UNCHECKED, 0 );
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_LookAt( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_LOOKAT ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesLookAtDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_lookat.h b/utils/hlfaceposer/eventproperties_lookat.h
new file mode 100644
index 0000000..9cef9d8
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_lookat.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_LOOKAT_H
+#define EVENTPROPERTIES_LOOKAT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_LookAt( CEventParams *params );
+
+#endif // EVENTPROPERTIES_LOOKAT_H
diff --git a/utils/hlfaceposer/eventproperties_moveto.cpp b/utils/hlfaceposer/eventproperties_moveto.cpp
new file mode 100644
index 0000000..34d049d
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_moveto.cpp
@@ -0,0 +1,368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+#include "filesystem.h"
+#include <commctrl.h>
+#include "scriplib.h"
+
+
+static CEventParams g_Params;
+
+class CEventPropertiesMoveToDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void PopulateMovementStyle( HWND control, CEventParams *params );
+ void SetDistanceToTargetText( CEventParams *params );
+};
+
+void CEventPropertiesMoveToDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "MoveTo", "Move To Actor" );
+}
+
+void CEventPropertiesMoveToDialog::InitControlData( CEventParams *params )
+{
+ SetDlgItemText( m_hDialog, IDC_STARTTIME, va( "%f", g_Params.m_flStartTime ) );
+ SetDlgItemText( m_hDialog, IDC_ENDTIME, va( "%f", g_Params.m_flEndTime ) );
+ SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bHasEndTime ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bResumeCondition ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateTagList( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ HWND choices2 = GetControl( IDC_EVENTCHOICES2 );
+ SendMessage( choices2, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices2, WM_SETTEXT , 0, (LPARAM)params->m_szParameters2 );
+
+ HWND choices3 = GetControl( IDC_EVENTCHOICES3 );
+ SendMessage( choices3, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices3, WM_SETTEXT , 0, (LPARAM)params->m_szParameters3 );
+
+ HWND control = GetControl( IDC_SLIDER_DISTANCE );
+ SendMessage( control, TBM_SETRANGE, 0, (LPARAM)MAKELONG( 0, 200 ) );
+ SendMessage( control, TBM_SETPOS, 1, (LPARAM)(LONG)params->m_flDistanceToTarget );
+
+ SendMessage( GetControl( IDC_CHECK_FORCESHORTMOVEMENT ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bForceShortMovement ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateNamedActorList( choices1, params );
+
+ SendMessage( GetControl( IDC_CHOICES2PROMPT ), WM_SETTEXT, 0, (LPARAM)"Movement Style:" );
+
+ PopulateMovementStyle( choices2, params );
+
+ if (strlen( params->m_szParameters3 ) != 0)
+ {
+ // make sure blank is a valid choice
+ SendMessage( choices3, CB_ADDSTRING, 0, (LPARAM)"" );
+ }
+ PopulateNamedActorList( choices3, params );
+
+ SetDistanceToTargetText( params );
+}
+
+void CEventPropertiesMoveToDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesMoveToDialog g_EventPropertiesMoveToDialog;
+
+void CEventPropertiesMoveToDialog::PopulateMovementStyle( HWND control, CEventParams *params )
+{
+ char movement_style[ 256 ];
+ char distance_to_target[ 256 ];
+
+ movement_style[0] = 0;
+ distance_to_target[0]= 0;
+
+ ParseFromMemory( params->m_szParameters2, strlen( params->m_szParameters2 ) );
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ strcpy( movement_style, token );
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ strcpy( distance_to_target, token );
+ }
+ }
+
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"Walk" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"Run" );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"CrouchWalk" );
+
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)movement_style );
+}
+
+
+void CEventPropertiesMoveToDialog::SetDistanceToTargetText( CEventParams *params )
+{
+ HWND control;
+
+ control = GetControl( IDC_STATIC_DISTANCEVAL );
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)va( "%i", (int)params->m_flDistanceToTarget ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesMoveToDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesMoveToDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesMoveToDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesMoveToDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_HSCROLL:
+ {
+ HWND control = (HWND)lParam;
+ if ( control == GetControl( IDC_SLIDER_DISTANCE ))
+ {
+ g_Params.m_flDistanceToTarget = (float)SendMessage( GetControl( IDC_SLIDER_DISTANCE ), TBM_GETPOS, 0, 0 );
+
+ SetDistanceToTargetText( &g_Params );
+ return TRUE;
+ }
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_snprintf( g_Params.m_szName, sizeof( g_Params.m_szName ), "Moveto %s", g_Params.m_szParameters );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES2:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ if ( g_Params.m_nType != CChoreoEvent::MOVETO )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters2 ), (LPARAM)g_Params.m_szParameters2 );
+ }
+ else
+ {
+ char buf1[ 256 ];
+ SendMessage( GetControl( IDC_EVENTCHOICES2 ), WM_GETTEXT, (WPARAM)sizeof( buf1 ), (LPARAM)buf1 );
+
+ Q_snprintf( g_Params.m_szParameters2, sizeof( g_Params.m_szParameters2 ), "%s", buf1 );
+ }
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES3:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ if ( g_Params.m_nType != CChoreoEvent::MOVETO )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters3 ), (LPARAM)g_Params.m_szParameters3 );
+ }
+ else
+ {
+ char buf1[ 256 ];
+ SendMessage( GetControl( IDC_EVENTCHOICES3 ), WM_GETTEXT, (WPARAM)sizeof( buf1 ), (LPARAM)buf1 );
+
+ Q_snprintf( g_Params.m_szParameters3, sizeof( g_Params.m_szParameters3 ), "%s", buf1 );
+ }
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_CHECK_FORCESHORTMOVEMENT:
+ {
+ g_Params.m_bForceShortMovement = SendMessage( GetControl( IDC_CHECK_FORCESHORTMOVEMENT ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_MoveTo( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_MOVETO ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesMoveToDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_moveto.h b/utils/hlfaceposer/eventproperties_moveto.h
new file mode 100644
index 0000000..9dc683d
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_moveto.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_MOVETO_H
+#define EVENTPROPERTIES_MOVETO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_MoveTo( CEventParams *params );
+
+#endif // EVENTPROPERTIES_MOVETO_H
diff --git a/utils/hlfaceposer/eventproperties_permitresponses.cpp b/utils/hlfaceposer/eventproperties_permitresponses.cpp
new file mode 100644
index 0000000..c4220a1
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_permitresponses.cpp
@@ -0,0 +1,217 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesPermitResponsesDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+};
+
+void CEventPropertiesPermitResponsesDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Permit Responses", "Permit Responses" );
+}
+
+void CEventPropertiesPermitResponsesDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+}
+
+void CEventPropertiesPermitResponsesDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesPermitResponsesDialog g_EventPropertiesPermitResponsesDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesPermitResponsesDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesPermitResponsesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesPermitResponsesDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesPermitResponsesDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, "Permit Responses", sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_PermitResponses( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_PERMITRESPONSES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesPermitResponsesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_permitresponses.h b/utils/hlfaceposer/eventproperties_permitresponses.h
new file mode 100644
index 0000000..81195d8
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_permitresponses.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_PERMITRESPONSES_H
+#define EVENTPROPERTIES_PERMITRESPONSES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_PermitResponses( CEventParams *params );
+
+#endif // EVENTPROPERTIES_PERMITRESPONSES_H
diff --git a/utils/hlfaceposer/eventproperties_sequence.cpp b/utils/hlfaceposer/eventproperties_sequence.cpp
new file mode 100644
index 0000000..56f1cbf
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_sequence.cpp
@@ -0,0 +1,251 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "StudioModel.h"
+#include "faceposer_models.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesSequenceDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void PopulateSequenceList( HWND wnd );
+};
+
+void CEventPropertiesSequenceDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Sequence", "Sequence" );
+}
+
+void CEventPropertiesSequenceDialog::PopulateSequenceList( HWND wnd )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ for (int i = 0; i < hdr->GetNumSeq(); i++)
+ {
+ SendMessage( wnd, CB_ADDSTRING, 0, (LPARAM)hdr->pSeqdesc(i).pszLabel() );
+ }
+ }
+}
+
+void CEventPropertiesSequenceDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ SendMessage( GetControl( IDC_CHECK_PLAYOVERSCRIPT ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bPlayOverScript ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ PopulateSequenceList( choices1 );
+}
+
+void CEventPropertiesSequenceDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesSequenceDialog g_EventPropertiesSequenceDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesSequenceDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesSequenceDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesSequenceDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesSequenceDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND control = GetControl( IDC_EVENTCHOICES );
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, g_Params.m_szParameters, sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_CHECK_PLAYOVERSCRIPT:
+ {
+ g_Params.m_bPlayOverScript = SendMessage( GetControl( IDC_CHECK_PLAYOVERSCRIPT ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Sequence( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_SEQUENCE ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesSequenceDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_sequence.h b/utils/hlfaceposer/eventproperties_sequence.h
new file mode 100644
index 0000000..ed45e02
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_sequence.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_SEQUENCE_H
+#define EVENTPROPERTIES_SEQUENCE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Sequence( CEventParams *params );
+
+#endif // EVENTPROPERTIES_SEQUENCE_H
diff --git a/utils/hlfaceposer/eventproperties_speak.cpp b/utils/hlfaceposer/eventproperties_speak.cpp
new file mode 100644
index 0000000..363c77f
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_speak.cpp
@@ -0,0 +1,673 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "choreoevent.h"
+#include "filesystem.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "AddSoundEntry.h"
+#include "SoundLookup.h"
+#include "ifaceposersound.h"
+#include "MatSysWin.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesSpeakDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+
+public:
+
+ CEventPropertiesSpeakDialog()
+ {
+ m_bShowAll = false;
+ m_szLastFilter[ 0 ] = 0;
+ m_Timer = 0;
+ m_flLastFilterUpdateTime;
+ }
+
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+
+private:
+
+ void PopulateFilterList( bool resetCurrent );
+ void OnCheckFilterUpdate();
+
+ void AddFilterToHistory( char const *filter );
+
+ void OnSoundSelected( CEventParams *params );
+
+ void PopulateSoundList( char const *current, HWND wnd );
+ void PopulateVolumeLevels( HWND control, CEventParams *params );
+
+ void FindWaveInSoundEntries( CUtlVector< int >& entryList, char const *search );
+ void OnCheckChangedVolumeLevel( CEventParams *params );
+
+ bool m_bShowAll;
+
+ CUtlVector< CUtlSymbol > m_FilterHistory;
+ CUtlSymbolTable m_Symbols;
+
+ enum
+ {
+ TIMER_ID = 100,
+ };
+
+ UINT m_Timer;
+ char m_szLastFilter[ 256 ];
+ float m_flLastFilterUpdateTime;
+};
+
+void CEventPropertiesSpeakDialog::AddFilterToHistory( char const *filter )
+{
+ CUtlSymbol sym = m_Symbols.AddString( filter );
+ // Move it to front of list...
+ m_FilterHistory.FindAndRemove( sym );
+ m_FilterHistory.AddToHead( sym );
+
+ PopulateFilterList( false );
+
+ // Apply filter
+
+ PopulateSoundList( g_Params.m_szParameters, GetControl( IDC_SOUNDLIST ) );
+}
+
+void CEventPropertiesSpeakDialog::PopulateFilterList( bool resetCurrent )
+{
+ HWND control = GetControl( IDC_FILTER );
+
+ char oldf[ 256 ];
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( oldf ), (LPARAM)oldf );
+
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ int c = m_FilterHistory.Count();
+ if ( c == 0 )
+ return;
+
+ for ( int i = 0; i < c; ++i )
+ {
+ char const *str = m_Symbols.String( m_FilterHistory[ i ] );
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)str );
+ }
+
+ char const *first = m_Symbols.String( m_FilterHistory[ 0 ] );
+ if ( resetCurrent && first )
+ {
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)first );
+ SendMessage( control, CB_SETEDITSEL , 0, MAKELPARAM( Q_strlen(first), -1 ) );
+ }
+ else
+ {
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)oldf );
+ SendMessage( control, CB_SETEDITSEL , 0, MAKELPARAM( Q_strlen(oldf), -1 ) );
+ }
+}
+
+void CEventPropertiesSpeakDialog::OnCheckFilterUpdate()
+{
+ char curfilter[ 256 ];
+ HWND control = GetControl( IDC_FILTER );
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( curfilter), (LPARAM)curfilter );
+
+ if ( Q_stricmp( curfilter, m_szLastFilter ) )
+ {
+ Q_strncpy( m_szLastFilter, curfilter, sizeof( m_szLastFilter ) );
+
+ AddFilterToHistory( m_szLastFilter );
+ }
+}
+
+void CEventPropertiesSpeakDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "Speak", "Speak Sound" );
+}
+
+void CEventPropertiesSpeakDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ m_flLastFilterUpdateTime = (float)mx::getTickCount() / 1000.0f;
+
+ m_Timer = SetTimer( m_hDialog, TIMER_ID, 1, 0 );
+
+ HWND choices1 = GetControl( IDC_SOUNDLIST );
+ SendMessage( choices1, LB_RESETCONTENT, 0, 0 );
+
+ HWND choices2 = GetControl( IDC_EVENTCHOICES2 );
+ SendMessage( choices2, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices2, WM_SETTEXT , 0, (LPARAM)params->m_szParameters2 );
+
+ HWND attenuate = GetControl( IDC_CAPTION_ATTENUATION );
+ SendMessage( attenuate, BM_SETCHECK, (WPARAM) params->m_bCloseCaptionNoAttenuate ? BST_CHECKED : BST_UNCHECKED, 0 );
+
+ PopulateSoundList( params->m_szParameters, choices1 );
+
+ OnSoundSelected( params );
+
+ PopulateFilterList( true );
+}
+
+void CEventPropertiesSpeakDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesSpeakDialog g_EventPropertiesSpeakDialog;
+
+void CEventPropertiesSpeakDialog::PopulateVolumeLevels( HWND control, CEventParams *params )
+{
+ SendMessage( control, CB_RESETCONTENT, 0, 0 );
+
+ // Assume uneditable
+ SendMessage( control, CB_ADDSTRING, 0, (LPARAM)"VOL_NORM" );
+
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)"VOL_NORM" );
+
+ bool enabled = false;
+
+ if ( !Q_stristr( params->m_szParameters, ".wav" ) )
+ {
+ // Look up the sound level from the soundemitter system
+ int soundindex = soundemitter->GetSoundIndex( params->m_szParameters );
+ if ( soundindex >= 0 )
+ {
+ // Look up the sound level from the soundemitter system
+ CSoundParametersInternal *params = soundemitter->InternalGetParametersForSound( soundindex );
+ if ( params )
+ {
+ // Found it
+ SendMessage( control, WM_SETTEXT , 0, (LPARAM)params->VolumeToString() );
+
+ //
+ // See if the .txt file is writable
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ if ( scriptfile )
+ {
+ // See if it's writable
+ if ( filesystem->FileExists( scriptfile ) &&
+ filesystem->IsFileWritable( scriptfile ) )
+ {
+ enabled = true;
+ }
+ }
+ }
+ }
+ }
+
+ EnableWindow( control, enabled ? TRUE : FALSE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// Output : static void
+//-----------------------------------------------------------------------------
+void CEventPropertiesSpeakDialog::PopulateSoundList( char const *current, HWND wnd )
+{
+ extern bool NameLessFunc( const char *const& name1, const char *const& name2 );
+
+ 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 ] )
+ {
+ bool add = true;
+ if ( !m_bShowAll )
+ {
+ CSoundParameters params;
+ if ( soundemitter->GetParametersForSound( name, params, GENDER_NONE ) )
+ {
+ if ( params.channel != CHAN_VOICE )
+ {
+ add = false;
+ }
+ }
+ }
+
+ // Apply filter
+ if ( m_szLastFilter[ 0 ] != 0 )
+ {
+ if ( !Q_stristr( name, m_szLastFilter ) )
+ {
+ add = false;
+ }
+ }
+
+ if ( add )
+ {
+ m_SortedNames.Insert( name );
+ }
+ }
+ }
+
+ SendMessage( wnd, WM_SETREDRAW , (WPARAM)FALSE, (LPARAM)0 );
+
+ // Remove all
+ SendMessage( wnd, LB_RESETCONTENT, 0, 0 );
+
+ int selectslot = 0;
+
+ int j = m_SortedNames.FirstInorder();
+ while ( j != m_SortedNames.InvalidIndex() )
+ {
+ char const *name = m_SortedNames[ j ];
+ if ( name && name[ 0 ] )
+ {
+ int temp = SendMessage( wnd, LB_ADDSTRING, 0, (LPARAM)name );
+
+ if ( !Q_stricmp( name, current ) )
+ {
+ selectslot = temp;
+ }
+ }
+
+ j = m_SortedNames.NextInorder( j );
+ }
+
+ SendMessage( wnd, LB_SETCURSEL, (WPARAM)selectslot, 0 );
+
+ SendMessage( wnd, WM_SETREDRAW , (WPARAM)TRUE, (LPARAM)0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesSpeakDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+void CEventPropertiesSpeakDialog::FindWaveInSoundEntries( CUtlVector< int >& entryList, char const *search )
+{
+ int c = soundemitter->GetSoundCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSoundParametersInternal *params = soundemitter->InternalGetParametersForSound( i );
+ if ( !params )
+ continue;
+
+ int waveCount = params->NumSoundNames();
+ for ( int wave = 0; wave < waveCount; wave++ )
+ {
+ char const *waveName = soundemitter->GetWaveName( params->GetSoundNames()[ wave ].symbol );
+
+ if ( !Q_stricmp( waveName, search ) )
+ {
+ entryList.AddToTail( i );
+ break;
+ }
+ }
+ }
+}
+
+void CEventPropertiesSpeakDialog::OnCheckChangedVolumeLevel( CEventParams *params )
+{
+ HWND control = GetControl( IDC_EVENTCHOICES2 );
+ if ( !IsWindowEnabled( control ) )
+ {
+ return;
+ }
+
+ if ( Q_stristr( params->m_szParameters, ".wav" ) )
+ {
+ return;
+ }
+
+ int soundindex = soundemitter->GetSoundIndex( params->m_szParameters );
+ if ( soundindex < 0 )
+ return;
+
+ // Look up the sound level from the soundemitter system
+ CSoundParametersInternal *soundparams = soundemitter->InternalGetParametersForSound( soundindex );
+ if ( !params )
+ {
+ return;
+ }
+
+ // See if it's writable, if not then bail
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ if ( !scriptfile ||
+ !filesystem->FileExists( scriptfile ) ||
+ !filesystem->IsFileWritable( scriptfile ) )
+ {
+ return;
+ }
+
+ // Copy the parameters
+ CSoundParametersInternal newparams;
+ newparams.CopyFrom( *soundparams );
+
+ // Get the value from the control
+ char newvolumelevel[ 256 ];
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( newvolumelevel ), (LPARAM)newvolumelevel );
+
+ newparams.VolumeFromString( newvolumelevel );
+
+ // No change
+ if ( newparams == *soundparams )
+ {
+ return;
+ }
+
+ soundemitter->UpdateSoundParameters( params->m_szParameters , newparams );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesSpeakDialog( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesSpeakDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+void CEventPropertiesSpeakDialog::OnSoundSelected( CEventParams *params )
+{
+ PopulateVolumeLevels( GetControl( IDC_EVENTCHOICES2 ), params );
+ SendMessage( GetControl( IDC_SOUNDNAME ), WM_SETTEXT, 0, (LPARAM)params->m_szParameters );
+
+ // Update script name and wavename fields
+ HWND scriptname = GetControl( IDC_STATIC_SCRIPTFILE );
+ HWND wavename = GetControl( IDC_STATIC_WAVEFILENAME );
+
+ SendMessage( scriptname, WM_SETTEXT, (WPARAM)1, (LPARAM)"" );
+ SendMessage( wavename, WM_SETTEXT, (WPARAM)1, (LPARAM)"" );
+
+ int soundindex = soundemitter->GetSoundIndex( params->m_szParameters );
+ if ( soundindex >= 0 )
+ {
+ char const *script = soundemitter->GetSourceFileForSound( soundindex );
+ if ( script && script [ 0 ] )
+ {
+ SendMessage( scriptname, WM_SETTEXT, (WPARAM)Q_strlen( script ) + 1, (LPARAM)script );
+
+ // Look up the sound level from the soundemitter system
+ CSoundParametersInternal *params = soundemitter->InternalGetParametersForSound( soundindex );
+ if ( params )
+ {
+ // Get wave name
+ char const *w = soundemitter->GetWaveName( params->GetSoundNames()[ 0 ].symbol );
+ if ( w && w[ 0 ] )
+ {
+ SendMessage( wavename, WM_SETTEXT, (WPARAM)Q_strlen( w ) + 1, (LPARAM)w );
+ }
+ }
+ }
+ }
+}
+
+BOOL CEventPropertiesSpeakDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+ case WM_TIMER:
+ {
+ g_pMatSysWindow->Frame();
+
+ float curtime = (float)mx::getTickCount() / 1000.0f;
+ if ( curtime - m_flLastFilterUpdateTime > 0.5f )
+ {
+ m_flLastFilterUpdateTime = curtime;
+ OnCheckFilterUpdate();
+ }
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ char sz[ 512 ];
+ GetDlgItemText( m_hDialog, IDC_SOUNDNAME, sz, sizeof( sz ) );
+
+ Q_FixSlashes( sz );
+
+ // Strip off game directory stuff
+ Q_strncpy( g_Params.m_szParameters, sz, sizeof( g_Params.m_szParameters ) );
+ char *p = Q_strstr( sz, "\\sound\\" );
+ if ( p )
+ {
+ Q_strncpy( g_Params.m_szParameters, p + strlen( "\\sound\\" ), sizeof( g_Params.m_szParameters ) );
+ }
+
+ OnCheckChangedVolumeLevel( &g_Params );
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, sz, sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ KillTimer( m_hDialog, m_Timer );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ {
+ KillTimer( m_hDialog, m_Timer );
+ EndDialog( hwndDlg, 0 );
+ }
+ break;
+ case IDC_CAPTION_ATTENUATION:
+ {
+ HWND control = GetControl( IDC_CAPTION_ATTENUATION );
+ g_Params.m_bCloseCaptionNoAttenuate = SendMessage( control, BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_PLAY_SOUND:
+ {
+ // Get sound name from soundemitter
+ sound->PlaySound(
+ NULL,
+ 1.0f,
+ va( "sound/%s", FacePoser_TranslateSoundName( g_Params.m_szParameters ) ),
+ NULL );
+ }
+ break;
+ case IDC_OPENSOURCE:
+ {
+ // Look up the sound level from the soundemitter system
+ int soundindex = soundemitter->GetSoundIndex( g_Params.m_szParameters );
+ if ( soundindex >= 0 )
+ {
+ // Look up the sound level from the soundemitter system
+ CSoundParametersInternal *params = soundemitter->InternalGetParametersForSound( soundindex );
+ if ( params )
+ {
+ // See if the .txt file is writable
+ char const *scriptfile = soundemitter->GetSourceFileForSound( soundindex );
+ if ( scriptfile )
+ {
+ char relative_path[MAX_PATH];
+ Q_snprintf( relative_path, MAX_PATH, "%s", scriptfile );
+
+ char full_path[MAX_PATH];
+ if ( filesystem->GetLocalPath( relative_path, full_path, MAX_PATH ) )
+ {
+ ShellExecute( NULL, "open", full_path, NULL, NULL, SW_SHOWNORMAL );
+ }
+ }
+ }
+ }
+ }
+ break;
+ case IDC_SHOW_ALL_SOUNDS:
+ {
+ m_bShowAll = SendMessage( GetControl( IDC_SHOW_ALL_SOUNDS ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+
+ PopulateSoundList( g_Params.m_szParameters, GetControl( IDC_EVENTCHOICES ) );
+ }
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_SOUNDLIST:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ int cursel = SendMessage( control, LB_GETCURSEL, 0, 0 );
+ if ( cursel != LB_ERR )
+ {
+ SendMessage( control, LB_GETTEXT, cursel, (LPARAM)g_Params.m_szParameters );
+ OnSoundSelected( &g_Params );
+
+ if ( HIWORD( wParam ) == LBN_DBLCLK )
+ {
+ // Get sound name from soundemitter
+ sound->PlaySound(
+ NULL,
+ 1.0f,
+ va( "sound/%s", FacePoser_TranslateSoundName( g_Params.m_szParameters ) ),
+ NULL );
+ }
+ }
+ }
+ }
+ break;
+ case IDC_EVENTCHOICES2:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters2 ), (LPARAM)g_Params.m_szParameters2 );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_Speak( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_SPEAK ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesSpeakDialog );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_speak.h b/utils/hlfaceposer/eventproperties_speak.h
new file mode 100644
index 0000000..2bac966
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_speak.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_SPEAK_H
+#define EVENTPROPERTIES_SPEAK_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_Speak( CEventParams *params );
+
+#endif // EVENTPROPERTIES_SPEAK_H
diff --git a/utils/hlfaceposer/eventproperties_subscene.cpp b/utils/hlfaceposer/eventproperties_subscene.cpp
new file mode 100644
index 0000000..9acddb4
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_subscene.cpp
@@ -0,0 +1,234 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "EventProperties.h"
+#include "mdlviewer.h"
+#include "filesystem.h"
+#include "filesystem_init.h"
+
+static CEventParams g_Params;
+
+class CEventPropertiesSubSceneDialog : public CBaseEventPropertiesDialog
+{
+ typedef CBaseEventPropertiesDialog BaseClass;
+public:
+ virtual void InitDialog( HWND hwndDlg );
+ virtual BOOL HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ virtual void SetTitle();
+ virtual void ShowControlsForEventType( CEventParams *params );
+ virtual void InitControlData( CEventParams *params );
+};
+
+void CEventPropertiesSubSceneDialog::SetTitle()
+{
+ SetDialogTitle( &g_Params, "SubScene", "Sub-scene" );
+}
+
+void CEventPropertiesSubSceneDialog::InitControlData( CEventParams *params )
+{
+ BaseClass::InitControlData( params );
+
+ HWND choices1 = GetControl( IDC_EVENTCHOICES );
+ SendMessage( choices1, CB_RESETCONTENT, 0, 0 );
+ SendMessage( choices1, WM_SETTEXT , 0, (LPARAM)params->m_szParameters );
+
+ SendMessage( GetControl( IDC_FILENAME ), WM_SETTEXT, sizeof( params->m_szParameters ), (LPARAM)params->m_szParameters );
+}
+
+void CEventPropertiesSubSceneDialog::InitDialog( HWND hwndDlg )
+{
+ m_hDialog = hwndDlg;
+
+ g_Params.PositionSelf( m_hDialog );
+
+ // Set working title for dialog, etc.
+ SetTitle();
+
+ // Show/Hide dialog controls
+ ShowControlsForEventType( &g_Params );
+ InitControlData( &g_Params );
+
+ UpdateTagRadioButtons( &g_Params );
+
+ SetFocus( GetControl( IDC_EVENTNAME ) );
+}
+
+static CEventPropertiesSubSceneDialog g_EventPropertiesSubSceneDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wnd -
+// *params -
+// Output : static
+//-----------------------------------------------------------------------------
+
+void CEventPropertiesSubSceneDialog::ShowControlsForEventType( CEventParams *params )
+{
+ BaseClass::ShowControlsForEventType( params );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK EventPropertiesSubSceneDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_EventPropertiesSubSceneDialog.HandleMessage( hwndDlg, uMsg, wParam, lParam );
+};
+
+BOOL CEventPropertiesSubSceneDialog::HandleMessage( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ m_hDialog = hwndDlg;
+
+ bool handled = false;
+ BOOL bret = InternalHandleMessage( &g_Params, hwndDlg, uMsg, wParam, lParam, handled );
+ if ( handled )
+ return bret;
+
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ hdc = BeginPaint(hwndDlg, &ps);
+ DrawSpline( hdc, GetControl( IDC_STATIC_SPLINE ), g_Params.m_pEvent );
+ EndPaint(hwndDlg, &ps);
+
+ return FALSE;
+ }
+ break;
+ case WM_VSCROLL:
+ {
+ RECT rcOut;
+ GetSplineRect( GetControl( IDC_STATIC_SPLINE ), rcOut );
+
+ InvalidateRect( hwndDlg, &rcOut, TRUE );
+ UpdateWindow( hwndDlg );
+ return FALSE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ InitDialog( hwndDlg );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ char sz[ 512 ];
+ GetDlgItemText( m_hDialog, IDC_FILENAME, sz, sizeof( sz ) );
+
+ // Strip off game directory stuff
+ Q_strncpy( g_Params.m_szParameters, sz, sizeof( g_Params.m_szParameters ) );
+
+ GetDlgItemText( m_hDialog, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ if ( !g_Params.m_szName[ 0 ] )
+ {
+ Q_strncpy( g_Params.m_szName, g_Params.m_szParameters, sizeof( g_Params.m_szName ) );
+ }
+
+ char szTime[ 32 ];
+ GetDlgItemText( m_hDialog, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+ GetDlgItemText( m_hDialog, IDC_ENDTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flEndTime = atof( szTime );
+
+ // Parse tokens from tags
+ ParseTags( &g_Params );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_SELECTWAV:
+ {
+ char filename[ 512 ];
+ if ( FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "scenes", "*.vcd" ) )
+ {
+ SetDlgItemText( m_hDialog, IDC_FILENAME, filename );
+ }
+ }
+ break;
+ case IDC_CHECK_ENDTIME:
+ {
+ g_Params.m_bHasEndTime = SendMessage( GetControl( IDC_CHECK_ENDTIME ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ if ( !g_Params.m_bHasEndTime )
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_HIDE );
+ }
+ else
+ {
+ ShowWindow( GetControl( IDC_ENDTIME ), SW_RESTORE );
+ }
+ }
+ break;
+ case IDC_CHECK_RESUMECONDITION:
+ {
+ g_Params.m_bResumeCondition = SendMessage( GetControl( IDC_CHECK_RESUMECONDITION ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+ }
+ break;
+ case IDC_EVENTCHOICES:
+ {
+ HWND control = (HWND)lParam;
+ if ( control )
+ {
+ SendMessage( control, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szParameters ), (LPARAM)g_Params.m_szParameters );
+ }
+ }
+ break;
+ case IDC_ABSOLUTESTART:
+ {
+ g_Params.m_bUsesTag = false;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ case IDC_RELATIVESTART:
+ {
+ g_Params.m_bUsesTag = true;
+ UpdateTagRadioButtons( &g_Params );
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int EventProperties_SubScene( CEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EVENTPROPERTIES_SUBSCENE ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)EventPropertiesSubSceneDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/eventproperties_subscene.h b/utils/hlfaceposer/eventproperties_subscene.h
new file mode 100644
index 0000000..3556095
--- /dev/null
+++ b/utils/hlfaceposer/eventproperties_subscene.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EVENTPROPERTIES_SUBSCENE_H
+#define EVENTPROPERTIES_SUBSCENE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEventParams;
+
+int EventProperties_SubScene( CEventParams *params );
+
+#endif // EVENTPROPERTIES_SUBSCENE_H
diff --git a/utils/hlfaceposer/expclass.cpp b/utils/hlfaceposer/expclass.cpp
new file mode 100644
index 0000000..9ed9eaa
--- /dev/null
+++ b/utils/hlfaceposer/expclass.cpp
@@ -0,0 +1,735 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <mxtk/mx.h>
+#include "expressions.h"
+#include "expclass.h"
+#include "hlfaceposer.h"
+#include "StudioModel.h"
+#include "filesystem.h"
+#include "FlexPanel.h"
+#include "ControlPanel.h"
+#include "mxExpressionTray.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "ExpressionTool.h"
+#include "faceposer_models.h"
+#include "mdlviewer.h"
+#include "phonemeconverter.h"
+#include "ProgressDialog.h"
+#include "tier1/fmtstr.h"
+#include "tier1/utlstring.h"
+#include "tier1/utlvector.h"
+
+
+#undef ALIGN4
+#undef ALIGN16
+#define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
+#define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15)
+
+char const *GetGlobalFlexControllerName( int index );
+int GetGlobalFlexControllerCount( void );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *classname -
+//-----------------------------------------------------------------------------
+CExpClass::CExpClass( const char *classname )
+{
+ Q_strncpy( m_szClassName, classname, sizeof( m_szClassName ) );
+ Q_FileBase( m_szClassName, m_szBaseName, sizeof( m_szBaseName ) );
+ m_szFileName[ 0 ] = 0;
+ m_bDirty = false;
+ m_nSelectedExpression = -1;
+ m_bIsPhonemeClass = Q_strstr( classname, "phonemes" ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpClass::~CExpClass( void )
+{
+ m_Expressions.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+int CExpClass::FindExpressionIndex( CExpression *exp )
+{
+ for ( int i = 0 ; i < GetNumExpressions(); i++ )
+ {
+ CExpression *e = GetExpression( i );
+ if ( e == exp )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::Save( void )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ const char *filename = GetFileName();
+ if ( !filename || !filename[ 0 ] )
+ return;
+
+ Con_Printf( "Saving changes to %s to file %s\n", GetName(), GetFileName() );
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ int i, j;
+
+ int numflexmaps = 0;
+ int flexmap[128]; // maps file local controlls into global controls
+
+ CExpression *expr = NULL;
+ // find all used controllers
+ int fc = GetGlobalFlexControllerCount();
+ for ( j = 0; j < fc; ++j )
+ {
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+
+ if ( settings[j] != 0 ||
+ weights[j] != 0 )
+ {
+ flexmap[ numflexmaps++ ] = j;
+ break;
+ }
+ }
+ }
+
+ buf.Printf( "$keys" );
+ for (j = 0; j < numflexmaps; j++)
+ {
+ buf.Printf( " %s", GetGlobalFlexControllerName( flexmap[j] ) );
+ }
+ buf.Printf( "\n" );
+
+ buf.Printf( "$hasweighting\n" );
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+
+ buf.Printf( "\"%s\" ", expr->name );
+
+ // isalpha returns non zero for ents > 256
+ if (expr->index <= 'z')
+ {
+ buf.Printf( "\"%c\" ", expr->index );
+ }
+ else
+ {
+ buf.Printf( "\"0x%04x\" ", expr->index );
+ }
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (j = 0; j < numflexmaps; j++)
+ {
+ buf.Printf( "%.3f %.3f ", settings[flexmap[j]], weights[flexmap[j]] );
+ }
+
+ if ( Q_strstr( expr->name, "Right Side Smile" ) )
+ {
+ Con_Printf( "wrote %s with checksum %s\n",
+ expr->name, expr->GetBitmapCheckSum() );
+ }
+
+ buf.Printf( "\"%s\"\n", expr->description );
+ }
+
+ char relative[ 512 ];
+ filesystem->FullPathToRelativePath( filename, relative, sizeof( relative ) );
+
+ MakeFileWriteable( relative );
+ FileHandle_t fh = filesystem->Open( relative, "wt" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
+ return;
+ }
+ else
+ {
+ filesystem->Write( buf.Base(), buf.TellPut(), fh );
+ filesystem->Close(fh);
+ }
+
+ SetDirty( false );
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ if ( expr )
+ {
+ expr->ResetUndo();
+ expr->SetDirty( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::Export( void )
+{
+ char vfefilename[ 512 ];
+ Q_StripExtension( GetFileName(), vfefilename, sizeof( vfefilename ) );
+ Q_DefaultExtension( vfefilename, ".vfe", sizeof( vfefilename ) );
+
+ Con_Printf( "Exporting %s to %s\n", GetName(), vfefilename );
+
+ int i, j;
+
+ int numflexmaps = 0;
+ int flexmap[128]; // maps file local controlls into global controls
+ CExpression *expr = NULL;
+
+ // find all used controllers
+ int fc_count = GetGlobalFlexControllerCount();
+
+ for (j = 0; j < fc_count; j++)
+ {
+ int k = j;
+
+ for (i = 0; i < GetNumExpressions(); i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ if ( settings[k] != 0 || weights[k] != 0 )
+ {
+ flexmap[numflexmaps++] = k;
+ break;
+ }
+ }
+ }
+
+ byte *pData = (byte *)calloc( 1024 * 1024, 1 );
+ byte *pDataStart = pData;
+
+ flexsettinghdr_t *fhdr = (flexsettinghdr_t *)pData;
+
+ fhdr->id = ('V' << 16) + ('F' << 8) + ('E');
+ fhdr->version = 0;
+ V_strncpy( fhdr->name, vfefilename, sizeof( fhdr->name ) );
+
+ // allocate room for header
+ pData += sizeof( flexsettinghdr_t );
+ ALIGN4( pData );
+
+ // store flex settings
+ flexsetting_t *pSetting = (flexsetting_t *)pData;
+ fhdr->numflexsettings = GetNumExpressions();
+ fhdr->flexsettingindex = pData - pDataStart;
+ pData += sizeof( flexsetting_t ) * fhdr->numflexsettings;
+ ALIGN4( pData );
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ expr = GetExpression( i );
+ Assert( expr );
+
+ pSetting[i].index = expr->index;
+ pSetting[i].settingindex = pData - (byte *)(&pSetting[i]);
+
+ flexweight_t *pFlexWeights = (flexweight_t *)pData;
+
+ float *settings = expr->GetSettings();
+ float *weights = expr->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (j = 0; j < numflexmaps; j++)
+ {
+ if (settings[flexmap[j]] != 0 || weights[flexmap[j]] != 0)
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = j;
+ pFlexWeights->weight = settings[flexmap[j]];
+ pFlexWeights->influence = weights[flexmap[j]];
+ pFlexWeights++;
+ }
+ pData = (byte *)pFlexWeights;
+ ALIGN4( pData );
+ }
+ }
+
+ // store indexed table
+ int numindexes = 1;
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ if (pSetting[i].index >= numindexes)
+ numindexes = pSetting[i].index + 1;
+ }
+
+ int *pIndex = (int *)pData;
+ fhdr->numindexes = numindexes;
+ fhdr->indexindex = pData - pDataStart;
+ pData += sizeof( int ) * numindexes;
+ ALIGN4( pData );
+ for (i = 0; i < numindexes; i++)
+ {
+ pIndex[i] = -1;
+ }
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ pIndex[pSetting[i].index] = i;
+ }
+
+ // store flex setting names
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ expr = GetExpression( i );
+ pSetting[i].nameindex = pData - (byte *)(&pSetting[i]);
+ strcpy( (char *)pData, expr->name );
+ pData += strlen( expr->name ) + 1;
+ }
+ ALIGN4( pData );
+
+ // store key names
+ char **pKeynames = (char **)pData;
+ fhdr->numkeys = numflexmaps;
+ fhdr->keynameindex = pData - pDataStart;
+ pData += sizeof( char *) * numflexmaps;
+
+ for (i = 0; i < numflexmaps; i++)
+ {
+ pKeynames[i] = (char *)(pData - pDataStart);
+ strcpy( (char *)pData, GetGlobalFlexControllerName( flexmap[i] ) );
+ pData += strlen( GetGlobalFlexControllerName( flexmap[i] ) ) + 1;
+ }
+ ALIGN4( pData );
+
+ // allocate room for remapping
+ int *keymapping = (int *)pData;
+ fhdr->keymappingindex = pData - pDataStart;
+ pData += sizeof( int ) * numflexmaps;
+ for (i = 0; i < numflexmaps; i++)
+ {
+ keymapping[i] = -1;
+ }
+ ALIGN4( pData );
+
+ fhdr->length = pData - pDataStart;
+
+ char relative[ 512 ];
+ filesystem->FullPathToRelativePath( vfefilename, relative, sizeof( relative ) );
+
+ MakeFileWriteable( relative );
+ FileHandle_t fh = filesystem->Open( relative, "wb" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "Unable to write to %s (read-only?)\n", relative );
+ return;
+ }
+ else
+ {
+ filesystem->Write( pDataStart, fhdr->length, fh );
+ filesystem->Close(fh);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetBaseName( void ) const
+{
+ return m_szBaseName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetName( void ) const
+{
+ return m_szClassName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpClass::GetFileName( void ) const
+{
+ return m_szFileName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CExpClass::SetFileName( const char *filename )
+{
+ strcpy( m_szFileName, filename );
+}
+
+bool IsUsingPerPlayerExpressions();
+
+void CExpClass::ReloadBitmaps( void )
+{
+ bool bUsingPerPlayerOverrides = IsUsingPerPlayerExpressions();
+
+ int c = models->Count();
+ for ( int model = 0; model < MAX_FP_MODELS; model++ )
+ {
+ // Only reload bitmaps for current model index
+ if ( bUsingPerPlayerOverrides && model != models->GetActiveModelIndex() )
+ continue;
+
+ models->ForceActiveModelIndex( model );
+
+ for ( int i = 0 ; i < GetNumExpressions(); i++ )
+ {
+ CExpression *e = GetExpression( i );
+ if ( !e )
+ continue;
+
+ if ( e->m_Bitmap[ model ].valid )
+ {
+ DeleteObject( e->m_Bitmap[ model ].image );
+ e->m_Bitmap[ model ].valid = false;
+ }
+
+ if ( model >= c )
+ continue;
+
+ if ( !LoadBitmapFromFile( e->GetBitmapFilename( model ), e->m_Bitmap[ model ] ) )
+ {
+ e->CreateNewBitmap( model );
+ }
+ }
+ }
+
+ models->UnForceActiveModelIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// *description -
+// *flexsettings -
+// selectnewitem -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::AddExpression( const char *name, const char *description, float *flexsettings, float *flexweights, bool selectnewitem, bool bDirtyClass )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return NULL;
+
+ CExpression *exp = FindExpression( name );
+ if ( exp )
+ {
+ Con_ErrorPrintf( "Can't create, an expression with the name '%s' already exists.\n", name );
+ return NULL;
+ }
+
+ // Add to end of list
+ int idx = m_Expressions.AddToTail();
+
+ exp = &m_Expressions[ idx ];
+
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ exp->SetExpressionClass( GetName() );
+ strcpy( exp->name, name );
+ strcpy( exp->description, description );
+ memcpy( settings, flexsettings, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weights, flexweights, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ exp->index = '_';
+
+ if ( IsPhonemeClass() )
+ {
+ exp->index = TextToPhoneme( name );
+ }
+
+ exp->m_Bitmap[ models->GetActiveModelIndex() ].valid = false;
+ if ( !LoadBitmapFromFile( exp->GetBitmapFilename( models->GetActiveModelIndex() ), exp->m_Bitmap[ models->GetActiveModelIndex() ] ) )
+ {
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+ }
+
+ if ( selectnewitem )
+ {
+ SelectExpression( idx );
+ }
+
+ if ( bDirtyClass )
+ {
+ SetDirty( true );
+ }
+
+ return exp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::FindExpression( const char *name )
+{
+ for ( int i = 0 ; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !stricmp( exp->name, name ) )
+ {
+ return exp;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+void CExpClass::DeleteExpression( const char *name )
+{
+
+ for ( int i = 0 ; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !stricmp( exp->name, name ) )
+ {
+ SetDirty( true );
+
+ m_Expressions.Remove( i );
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetNumExpressions( void )
+{
+ return m_Expressions.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpClass::GetExpression( int num )
+{
+ if ( num < 0 || num >= m_Expressions.Size() )
+ {
+ return NULL;
+ }
+
+ CExpression *exp = &m_Expressions[ num ];
+ return exp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpClass::GetDirty( void )
+{
+ return m_bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void CExpClass::SetDirty( bool dirty )
+{
+ m_bDirty = dirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetIndex( void )
+{
+ for ( int i = 0; i < expressions->GetNumClasses(); i++ )
+ {
+ CExpClass *cl = expressions->GetClass( i );
+ if ( cl == this )
+ return i;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+//-----------------------------------------------------------------------------
+void CExpClass::SelectExpression( int num, bool deselect )
+{
+ m_nSelectedExpression = num;
+
+ g_pFlexPanel->setExpression( num );
+ g_pExpressionTrayTool->Select( num, deselect );
+ g_pExpressionTrayTool->redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpClass::GetSelectedExpression( void )
+{
+ return m_nSelectedExpression;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpClass::DeselectExpression( void )
+{
+ m_nSelectedExpression = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : exp1 -
+// exp2 -
+//-----------------------------------------------------------------------------
+void CExpClass::SwapExpressionOrder( int exp1, int exp2 )
+{
+ CExpression temp1 = m_Expressions[ exp1 ];
+ CExpression temp2 = m_Expressions[ exp2 ];
+
+ m_Expressions.Remove( exp1 );
+ m_Expressions.InsertBefore( exp1, temp2 );
+ m_Expressions.Remove( exp2 );
+ m_Expressions.InsertBefore( exp2, temp1 );
+}
+
+void CExpClass::BuildValidChecksums( CUtlRBTree< CRC32_t > &tree )
+{
+ for ( int i = 0; i < m_Expressions.Size(); i++ )
+ {
+ CExpression *exp = &m_Expressions[ i ];
+ if ( !exp )
+ continue;
+
+ CRC32_t crc = exp->GetBitmapCRC();
+ tree.Insert( crc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: After a class is loaded, check the class directory and delete any bmp files that aren't
+// still referenced
+//-----------------------------------------------------------------------------
+void CExpClass::CheckBitmapConsistency( void )
+{
+ char path[ 512 ];
+
+ Q_snprintf( path, sizeof( path ), "expressions/%s/%s/*.bmp", models->GetActiveModelName(), GetBaseName() );
+ Q_FixSlashes( path );
+ Q_strlower( path );
+
+ g_pProgressDialog->Start( CFmtStr( "%s / %s - Reconcile Expression Thumbnails", models->GetActiveModelName(), GetBaseName() ), "", true );
+
+ CUtlVector< CUtlString > workList;
+
+ 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;
+ workList.AddToTail( s );
+
+
+ }
+
+ fn = filesystem->FindNext( hFindFile );
+ }
+
+ filesystem->FindClose( hFindFile );
+ }
+
+ CUtlRBTree< CRC32_t > tree( 0, 0, DefLessFunc( CRC32_t ) );
+ BuildValidChecksums( tree );
+
+ for ( int i = 0 ; i < workList.Count(); ++i )
+ {
+ char testname[ 256 ];
+ Q_StripExtension( workList[ i ].String(), testname, sizeof( testname ) );
+
+ g_pProgressDialog->UpdateText( "%s", testname );
+ g_pProgressDialog->Update( (float)i / (float)workList.Count() );
+
+ CRC32_t check;
+ Q_hextobinary( testname, Q_strlen( testname ), (byte *)&check, sizeof( check ) );
+
+ if ( tree.Find( check ) == tree.InvalidIndex() )
+ {
+ char kill[ 512 ];
+ Q_snprintf( kill, sizeof( kill ), "expressions/%s/%s/%s", models->GetActiveModelName(), GetBaseName(), fn );
+ Q_FixSlashes( kill );
+ Q_strlower( kill );
+
+ // Delete it
+ Con_ErrorPrintf( "Removing unused bitmap file '%s'\n", kill );
+
+ filesystem->RemoveFile( kill, "MOD" );
+ }
+
+ if ( g_pProgressDialog->IsCancelled() )
+ {
+ Msg( "Cancelled\n" );
+ break;
+ }
+ }
+
+ g_pProgressDialog->Finish();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Does this class have expression indices based on phoneme lookups
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpClass::IsPhonemeClass( void ) const
+{
+ return m_bIsPhonemeClass;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/expclass.h b/utils/hlfaceposer/expclass.h
new file mode 100644
index 0000000..2a1b777
--- /dev/null
+++ b/utils/hlfaceposer/expclass.h
@@ -0,0 +1,74 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EXPCLASS_H
+#define EXPCLASS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+
+class CExpression;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CExpClass
+{
+public:
+
+ CExpClass( const char *classname );
+ virtual ~CExpClass( void );
+
+ void Save( void );
+ void Export( void );
+
+ void CheckBitmapConsistency( void );
+ void ReloadBitmaps( void );
+
+ const char *GetName() const;
+ const char *GetBaseName() const;
+ const char *GetFileName() const;
+ void SetFileName( const char *filename );
+
+ CExpression *AddExpression( const char *name, const char *description, float *flexsettings, float *flexweights, bool selectnewitem, bool bDirtyClass );
+ CExpression *FindExpression( const char *name );
+ int FindExpressionIndex( CExpression *exp );
+ void DeleteExpression( const char *name );
+
+ int GetNumExpressions( void );
+ CExpression *GetExpression( int num );
+
+ bool GetDirty( void );
+ void SetDirty( bool dirty );
+
+ void SelectExpression( int num, bool deselect = true );
+ int GetSelectedExpression( void );
+ void DeselectExpression( void );
+
+ void SwapExpressionOrder( int exp1, int exp2 );
+
+ // Get index of this class in the global class list
+ int GetIndex( void );
+
+ bool IsPhonemeClass( void ) const;
+
+private:
+
+ void BuildValidChecksums( CUtlRBTree< CRC32_t > &tree );
+
+ char m_szBaseName[ 128 ]; // name w/out any subdirectory names
+ char m_szClassName[ 128 ];
+ char m_szFileName[ 128 ];
+ bool m_bDirty;
+ int m_nSelectedExpression;
+ CUtlVector < CExpression > m_Expressions;
+
+ bool m_bIsPhonemeClass;
+};
+#endif // EXPCLASS_H
diff --git a/utils/hlfaceposer/expression.cpp b/utils/hlfaceposer/expression.cpp
new file mode 100644
index 0000000..b710c00
--- /dev/null
+++ b/utils/hlfaceposer/expression.cpp
@@ -0,0 +1,472 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <mxtk/mx.h>
+#include "expressions.h"
+#include "StudioModel.h"
+#include "filesystem.h"
+#include "viewersettings.h"
+#include "matsyswin.h"
+#include "checksum_crc.h"
+#include "expclass.h"
+#include "ControlPanel.h"
+#include "faceposer_models.h"
+#include "mdlviewer.h"
+
+static int g_counter = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpression::CExpression( void )
+{
+ name[ 0 ] = 0;
+ index = 0;
+ description[ 0 ] = 0;
+ memset( setting, 0, sizeof( setting ) );
+
+ for ( int i = 0; i < MAX_FP_MODELS; i++ )
+ {
+ m_Bitmap[ i ].valid = false;
+ }
+
+ m_nUndoCurrent = 0;
+ m_bModified = false;
+
+ m_bSelected = false;
+
+ m_bDirty = false;
+
+ expressionclass[ 0 ] = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+// Input : from -
+//-----------------------------------------------------------------------------
+CExpression::CExpression( const CExpression& from )
+{
+ int i;
+
+ strcpy( name, from.name );
+ index = from.index;
+ strcpy( description, from.description );
+
+ for ( i = 0; i < MAX_FP_MODELS; i++ )
+ {
+ m_Bitmap[ i ] = from.m_Bitmap[ i ];
+ }
+
+ m_bModified = from.m_bModified;
+
+ for ( i = 0 ; i < from.undo.Size(); i++ )
+ {
+ CExpUndoInfo *newUndo = new CExpUndoInfo();
+ *newUndo = *from.undo[ i ];
+ undo.AddToTail( newUndo );
+ }
+
+ m_nUndoCurrent = from.m_nUndoCurrent;
+
+ m_bSelected = from.m_bSelected;
+
+ m_bDirty = from.m_bDirty;
+
+ strcpy( expressionclass, from.expressionclass );
+
+ memcpy( setting, from.setting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weight, from.weight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpression::~CExpression( void )
+{
+ ResetUndo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpression::GetDirty( void )
+{
+ return m_bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void CExpression::SetDirty( bool dirty )
+{
+ m_bDirty = dirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float *CExpression::GetSettings( void )
+{
+ return setting;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float *CExpression::GetWeights( void )
+{
+ return weight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mod -
+//-----------------------------------------------------------------------------
+void CExpression::SetModified( bool mod )
+{
+ m_bModified = mod;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpression::GetModified( void )
+{
+ return m_bModified;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : selected -
+//-----------------------------------------------------------------------------
+void CExpression::SetSelected( bool selected )
+{
+ m_bSelected = selected;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpression::GetSelected( void )
+{
+ return m_bSelected;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpression::ResetUndo( void )
+{
+ CExpUndoInfo *u;
+ for ( int i = 0; i < undo.Size(); i++ )
+ {
+ u = undo[ i ];
+ delete u;
+ }
+
+ undo.RemoveAll();
+ m_nUndoCurrent = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpression::CanRedo( void )
+{
+ if ( !undo.Size() )
+ return false;
+
+ if ( m_nUndoCurrent == 0 )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpression::CanUndo( void )
+{
+ if ( !undo.Size() )
+ return false;
+
+ if ( m_nUndoCurrent >= undo.Size() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CExpression::UndoLevels( void )
+{
+ return undo.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpression::UndoCurrent( void )
+{
+ return m_nUndoCurrent;
+}
+
+void ChecksumFlexControllers( bool bSpew, char const *name, CRC32_t &crc, const float *settings, const float *weights );
+
+CRC32_t CExpression::GetBitmapCRC()
+{
+ CRC32_t crc;
+
+ float *s = setting;
+ float *w = weight;
+
+ // Note, we'll use the pristine values if this has changed
+ if ( undo.Size() >= 1 )
+ {
+ s = undo[ undo.Size() - 1 ]->setting;
+ w = undo[ undo.Size() - 1 ]->weight;
+ }
+
+ // This walks the global controllers sorted by name and only includes values with a setting or value which is != 0.0f
+
+ ChecksumFlexControllers( false, name, crc, s, w );
+
+ return crc;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpression::GetBitmapCheckSum()
+{
+ CRC32_t crc = GetBitmapCRC();
+
+ // Create string name out of binary data
+ static char hex[ 9 ];
+ Q_binarytohex( (byte *)&crc, sizeof( crc ), hex, sizeof( hex ) );
+ return hex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpression::GetBitmapFilename( int modelindex )
+{
+ static char filename[ 256 ] = { 0 };
+
+ char const *classname = "error";
+ CExpClass *cl = GetExpressionClass();
+ if ( cl )
+ {
+ classname = cl->GetBaseName();
+ }
+
+ char modelName[512], modelNameTemp[512];
+ Q_strncpy( modelNameTemp, models->GetModelName( modelindex ), sizeof( modelNameTemp ) );
+
+ char const *in = modelNameTemp;
+ char *out = modelName;
+
+ while ( *in )
+ {
+ if ( V_isalnum( *in ) ||
+ *in == '_' ||
+ *in == '\\' ||
+ *in == '/' ||
+ *in == '.' ||
+ *in == ':' )
+ {
+ *out++ = *in;
+ }
+ in++;
+ }
+ *out = 0;
+
+
+ sprintf( filename, "expressions/%s/%s/%s.bmp", modelName, classname, GetBitmapCheckSum() );
+
+ Q_FixSlashes( filename );
+ strlwr( filename );
+
+ CreatePath( filename );
+
+ return filename;
+}
+
+void CExpression::CreateNewBitmap( int modelindex )
+{
+ MatSysWindow *pWnd = g_pMatSysWindow;
+ if ( !pWnd )
+ return;
+
+ StudioModel *model = models->GetStudioModel( modelindex );
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = models->GetStudioHeader( modelindex );
+ if ( !hdr )
+ return;
+
+ char filename[ 256 ];
+ V_strcpy_safe( filename, GetBitmapFilename( modelindex ) );
+ if ( !Q_strstr( filename, ".bmp" ) )
+ return;
+
+ models->CreateNewBitmap( modelindex, filename, 0, 128, true, this, &m_Bitmap[ modelindex ] );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+void CExpression::PushUndoInformation( void )
+{
+ SetModified( true );
+
+ // A real change to the data wipes out the redo counters
+ WipeRedoInformation();
+
+ CExpUndoInfo *newundo = new CExpUndoInfo;
+
+ memcpy( newundo->setting, setting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memset( newundo->redosetting, 0, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( newundo->weight, weight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memset( newundo->redoweight, 0, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+
+ newundo->counter = g_counter++;
+
+ undo.AddToHead( newundo );
+
+ Assert( m_nUndoCurrent == 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+void CExpression::PushRedoInformation( void )
+{
+ Assert( undo.Size() >= 1 );
+
+ CExpUndoInfo *redo = undo[ 0 ];
+ memcpy( redo->redosetting, setting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( redo->redoweight, weight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+void CExpression::Undo( void )
+{
+ if ( !CanUndo() )
+ return;
+
+ Assert( m_nUndoCurrent < undo.Size() );
+
+ CExpUndoInfo *u = undo[ m_nUndoCurrent++ ];
+ Assert( u );
+
+ memcpy( setting, u->setting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weight, u->weight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+void CExpression::Redo( void )
+{
+ if ( !CanRedo() )
+ return;
+
+ Assert( m_nUndoCurrent >= 1 );
+ Assert( m_nUndoCurrent <= undo.Size() );
+
+ CExpUndoInfo *u = undo[ --m_nUndoCurrent ];
+ Assert( u );
+
+ memcpy( setting, u->redosetting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weight, u->redoweight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+//-----------------------------------------------------------------------------
+void CExpression::WipeRedoInformation( void )
+{
+ // Wipe out all stuff newer then m_nUndoCurrent
+ int level = 0;
+ while ( level < m_nUndoCurrent )
+ {
+ CExpUndoInfo *u = undo[ 0 ];
+ undo.Remove( 0 );
+ Assert( u );
+ delete u;
+ level++;
+ }
+
+ m_nUndoCurrent = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Revert to last saved state
+//-----------------------------------------------------------------------------
+void CExpression::Revert( void )
+{
+ SetDirty( false );
+
+ if ( undo.Size() <= 0 )
+ return;
+
+ // Go back to original data
+ CExpUndoInfo *u = undo[ undo.Size() - 1 ];
+ Assert( u );
+
+ memcpy( setting, u->setting, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+ memcpy( weight, u->weight, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) );
+
+ ResetUndo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CExpClass
+//-----------------------------------------------------------------------------
+CExpClass *CExpression::GetExpressionClass( void )
+{
+ CExpClass *cl = expressions->FindClass( expressionclass, false );
+ if ( !cl )
+ {
+ Assert( cl );
+ }
+ return cl;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *classname -
+//-----------------------------------------------------------------------------
+void CExpression::SetExpressionClass( char const *classname )
+{
+ strcpy( expressionclass, classname );
+}
+
diff --git a/utils/hlfaceposer/expression.h b/utils/hlfaceposer/expression.h
new file mode 100644
index 0000000..1a6d134
--- /dev/null
+++ b/utils/hlfaceposer/expression.h
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EXPRESSION_H
+#define EXPRESSION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+#include "mxBitmapTools.h"
+#include "hlfaceposer.h"
+
+#define GLOBAL_STUDIO_FLEX_CONTROL_COUNT ( MAXSTUDIOFLEXCTRL * 4 )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CExpUndoInfo
+{
+public:
+ float setting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float weight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ float redosetting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float redoweight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ int counter;
+};
+
+class CExpression;
+class CExpClass;
+
+typedef unsigned int CRC32_t;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CExpression
+{
+public:
+ CExpression ( void );
+ ~CExpression ( void );
+
+ CExpression( const CExpression& from );
+
+ void SetModified( bool mod );
+ bool GetModified( void );
+
+ void ResetUndo( void );
+
+ bool CanUndo( void );
+ bool CanRedo( void );
+
+ int UndoLevels( void );
+ int UndoCurrent( void );
+
+ const char *GetBitmapFilename( int modelindex );
+ const char *GetBitmapCheckSum();
+ CRC32_t GetBitmapCRC();
+ void CreateNewBitmap( int modelindex );
+
+ void PushUndoInformation( void );
+ void PushRedoInformation( void );
+
+ void Undo( void );
+ void Redo( void );
+
+ void SetSelected( bool selected );
+ bool GetSelected( void );
+
+ float *GetSettings( void );
+ float *GetWeights( void );
+
+ bool GetDirty( void );
+ void SetDirty( bool dirty );
+
+ void Revert( void );
+
+ CExpClass *GetExpressionClass( void );
+ void SetExpressionClass( char const *classname );
+
+ // name of expression
+ char name[32];
+ int index;
+ char description[128];
+
+ mxbitmapdata_t m_Bitmap[ MAX_FP_MODELS ];
+
+ bool m_bModified;
+
+ // Undo information
+ CUtlVector< CExpUndoInfo * > undo;
+ int m_nUndoCurrent;
+
+ bool m_bSelected;
+
+ bool m_bDirty;
+
+private:
+ // settings of fields
+ float setting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float weight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ char expressionclass[ 128 ];
+
+ void WipeRedoInformation( void );
+};
+
+#endif // EXPRESSION_H
diff --git a/utils/hlfaceposer/expressionproperties.cpp b/utils/hlfaceposer/expressionproperties.cpp
new file mode 100644
index 0000000..f762d4e
--- /dev/null
+++ b/utils/hlfaceposer/expressionproperties.cpp
@@ -0,0 +1,78 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <mxtk/mx.h>
+#include "resource.h"
+#include "ExpressionProperties.h"
+#include "mdlviewer.h"
+
+static CExpressionParams g_Params;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK ExpressionPropertiesDialogProc
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK ExpressionPropertiesDialogProc ( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ g_Params.PositionSelf( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_EXPRESSIONNAME, g_Params.m_szName );
+ SetDlgItemText( hwndDlg, IDC_EXPRESSIONDESC, g_Params.m_szDescription );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_EXPRESSIONNAME ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ g_Params.m_szName[ 0 ] = 0;
+ GetDlgItemText( hwndDlg, IDC_EXPRESSIONNAME, g_Params.m_szName, 256 );
+ GetDlgItemText( hwndDlg, IDC_EXPRESSIONDESC, g_Params.m_szDescription, 256 );
+ EndDialog( hwndDlg, 1 );
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int ExpressionProperties( CExpressionParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_EXPRESSIONPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)ExpressionPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/expressionproperties.h b/utils/hlfaceposer/expressionproperties.h
new file mode 100644
index 0000000..1142797
--- /dev/null
+++ b/utils/hlfaceposer/expressionproperties.h
@@ -0,0 +1,27 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EXPRESSIONPROPERTIES_H
+#define EXPRESSIONPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CExpressionParams : public CBaseDialogParams
+{
+ char m_szName[ 256 ];
+ char m_szDescription[ 256 ];
+};
+
+int ExpressionProperties( CExpressionParams *params );
+
+#endif // EXPRESSIONPROPERTIES_H
diff --git a/utils/hlfaceposer/expressions.cpp b/utils/hlfaceposer/expressions.cpp
new file mode 100644
index 0000000..a199f20
--- /dev/null
+++ b/utils/hlfaceposer/expressions.cpp
@@ -0,0 +1,692 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include "expressions.h"
+#include <mxtk/mx.h>
+#include "ControlPanel.h"
+#include "StudioModel.h"
+#include "expclass.h"
+#include "mxExpressionTab.h"
+#include "mxExpressionTray.h"
+#include "filesystem.h"
+#include "faceposer_models.h"
+#include "utldict.h"
+#include "scriplib.h"
+#include "checksum_crc.h"
+
+bool Sys_Error(const char *pMsg, ...);
+extern char g_appTitle[];
+
+static CUtlVector< CUtlSymbol > g_GlobalFlexControllers;
+static CUtlDict< int, int > g_GlobalFlexControllerLookup;
+
+void ChecksumFlexControllers( bool bSpew, char const *name, CRC32_t &crc, const float *settings, const float *weights )
+{
+ CRC32_Init( &crc );
+
+ // Walk them alphabetically so that load order doesn't matter
+ for ( int i = g_GlobalFlexControllerLookup.First() ;
+ i != g_GlobalFlexControllerLookup.InvalidIndex();
+ i = g_GlobalFlexControllerLookup.Next( i ) )
+ {
+ int controllerIndex = g_GlobalFlexControllerLookup[ i ];
+ char const *pszName = g_GlobalFlexControllerLookup.GetElementName( i );
+
+ // Only count active controllers in checksum
+ float s = settings[ controllerIndex ];
+ float w = weights[ controllerIndex ];
+
+ if ( s == 0.0f && w == 0.0f )
+ {
+ continue;
+ }
+
+ CRC32_ProcessBuffer( &crc, (void *)pszName, Q_strlen( pszName ) );
+ CRC32_ProcessBuffer( &crc, (void *)&s, sizeof( s ) );
+ CRC32_ProcessBuffer( &crc, (void *)&w, sizeof( w ) );
+
+ if ( bSpew )
+ {
+ Msg( "[%d] %s == %f %f\n", controllerIndex, pszName, s, w );
+ }
+ }
+
+ CRC32_Final( &crc );
+
+ if ( bSpew )
+ {
+ char hex[ 17 ];
+ Q_binarytohex( (const byte *)&crc, sizeof( crc ), hex, sizeof( hex ) );
+ Msg( "%s checksum = %sf\n", name, hex );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *GetGlobalFlexControllerName( int index )
+{
+ return g_GlobalFlexControllers[ index ].String();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int GetGlobalFlexControllerCount( void )
+{
+ return g_GlobalFlexControllers.Count();
+}
+//-----------------------------------------------------------------------------
+// Purpose: Accumulates throughout runtime session, oh well
+// Input : *szName -
+// Output : int
+//-----------------------------------------------------------------------------
+int AddGlobalFlexController( StudioModel *model, const char *szName )
+{
+ int idx = g_GlobalFlexControllerLookup.Find( szName );
+ if ( idx != g_GlobalFlexControllerLookup.InvalidIndex() )
+ {
+ return g_GlobalFlexControllerLookup[ idx ];
+ }
+
+ CUtlSymbol sym;
+ sym = szName;
+ idx = g_GlobalFlexControllers.AddToTail( sym );
+ g_GlobalFlexControllerLookup.Insert( szName, idx );
+ // Con_Printf( "Added global flex controller %i %s from %s\n", idx, szName, model->GetStudioHdr()->name );
+ return idx;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *model -
+//-----------------------------------------------------------------------------
+void SetupModelFlexcontrollerLinks( StudioModel *model )
+{
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ if ( hdr->numflexcontrollers() <= 0 )
+ return;
+
+ // Already set up!!!
+ if ( hdr->pFlexcontroller( LocalFlexController_t(0) )->localToGlobal != -1 )
+ return;
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = AddGlobalFlexController( model, hdr->pFlexcontroller( i )->pszName() );
+ hdr->pFlexcontroller( i )->localToGlobal = j;
+ model->SetFlexController( i, 0.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CExpressionManager : public IExpressionManager
+{
+public:
+ CExpressionManager( void );
+ ~CExpressionManager( void );
+
+ void Reset( void );
+
+ void ActivateExpressionClass( CExpClass *cl );
+
+ // File I/O
+ void LoadClass( const char *filename );
+ void CreateNewClass( const char *filename );
+ bool CloseClass( CExpClass *cl );
+
+ CExpClass *AddCExpClass( const char *classname, const char *filename );
+ int GetNumClasses( void );
+
+ CExpression *GetCopyBuffer( void );
+
+ bool CanClose( void );
+
+ CExpClass *GetActiveClass( void );
+ CExpClass *GetClass( int num );
+ CExpClass *FindClass( const char *classname, bool bMatchBaseNameOnly );
+
+private:
+ // Methods
+ const char *GetClassnameFromFilename( const char *filename );
+
+// UI
+ void PopulateClassCB( CExpClass *cl );
+
+ void RemoveCExpClass( CExpClass *cl );
+
+private:
+ // Data
+ CExpClass *m_pActiveClass;
+ CUtlVector < CExpClass * > m_Classes;
+
+ CExpression m_CopyBuffer;
+};
+
+// Expose interface
+static CExpressionManager g_ExpressionManager;
+IExpressionManager *expressions = &g_ExpressionManager;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpressionManager::CExpressionManager( void )
+{
+ m_pActiveClass = NULL;
+ Reset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpressionManager::~CExpressionManager( void )
+{
+ Reset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionManager::Reset( void )
+{
+ while ( m_Classes.Size() > 0 )
+ {
+ CExpClass *p = m_Classes[ 0 ];
+ m_Classes.Remove( 0 );
+ delete p;
+ }
+
+ m_pActiveClass = NULL;
+
+ memset( &m_CopyBuffer, 0, sizeof( m_CopyBuffer ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExpClass *CExpressionManager::GetActiveClass( void )
+{
+ return m_pActiveClass;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : num -
+// Output : CExpClass
+//-----------------------------------------------------------------------------
+CExpClass *CExpressionManager::GetClass( int num )
+{
+ return m_Classes[ num ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *classname -
+// *filename -
+// Output : CExpClass *
+//-----------------------------------------------------------------------------
+CExpClass * CExpressionManager::AddCExpClass( const char *classname, const char *filename )
+{
+ Assert( !FindClass( classname, false ) );
+
+ CExpClass *pclass = new CExpClass( classname );
+ if ( !pclass )
+ return NULL;
+
+ m_Classes.AddToTail( pclass );
+
+ pclass->SetFileName( filename );
+
+ return pclass;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *cl -
+//-----------------------------------------------------------------------------
+void CExpressionManager::RemoveCExpClass( CExpClass *cl )
+{
+ for ( int i = 0; i < m_Classes.Size(); i++ )
+ {
+ CExpClass *p = m_Classes[ i ];
+ if ( p == cl )
+ {
+ m_Classes.Remove( i );
+ delete p;
+ break;
+ }
+ }
+
+ if ( m_Classes.Size() >= 1 )
+ {
+ ActivateExpressionClass( m_Classes[ 0 ] );
+ }
+ else
+ {
+ ActivateExpressionClass( NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *cl -
+//-----------------------------------------------------------------------------
+void CExpressionManager::ActivateExpressionClass( CExpClass *cl )
+{
+ m_pActiveClass = cl;
+ int select = 0;
+ for ( int i = 0; i < GetNumClasses(); i++ )
+ {
+ CExpClass *c = GetClass( i );
+ if ( cl == c )
+ {
+ select = i;
+ break;
+ }
+ }
+
+ g_pExpressionClass->select( select );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpressionManager::GetNumClasses( void )
+{
+ return m_Classes.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *classname -
+// Output : CExpClass
+//-----------------------------------------------------------------------------
+CExpClass *CExpressionManager::FindClass( const char *classname, bool bMatchBaseNameOnly )
+{
+ char search[ 256 ];
+ if ( bMatchBaseNameOnly )
+ {
+ Q_FileBase( classname, search, sizeof( search ) );
+ }
+ else
+ {
+ Q_strncpy( search, classname, sizeof( search ) );
+ }
+
+ Q_FixSlashes( search );
+ Q_strlower( search );
+
+ for ( int i = 0; i < m_Classes.Size(); i++ )
+ {
+ CExpClass *cl = m_Classes[ i ];
+
+ if ( !Q_stricmp( search, bMatchBaseNameOnly ? cl->GetBaseName() : cl->GetName() ) )
+ {
+ return cl;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CExpressionManager::GetClassnameFromFilename( const char *filename )
+{
+ char cleanname[ 256 ];
+ static char classname[ 256 ];
+ classname[ 0 ] = 0;
+
+ Assert( filename && filename[ 0 ] );
+
+ // Strip the .txt
+ Q_StripExtension( filename, cleanname, sizeof( cleanname ) );
+
+ char *p = Q_stristr( cleanname, "expressions" );
+ if ( p )
+ {
+ Q_strncpy( classname, p + Q_strlen( "expressions" ) + 1, sizeof( classname ) );
+ }
+ else
+ {
+ Assert( 0 );
+ Q_strncpy( classname, cleanname, sizeof( classname ) );
+ }
+
+ Q_FixSlashes( classname );
+ Q_strlower( classname );
+ return classname;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CExpression
+//-----------------------------------------------------------------------------
+CExpression *CExpressionManager::GetCopyBuffer( void )
+{
+ return &m_CopyBuffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpressionManager::CanClose( void )
+{
+ for ( int i = 0; i < m_Classes.Size(); i++ )
+ {
+ CExpClass *pclass = m_Classes[ i ];
+ if ( pclass->GetDirty() )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CExpressionManager::LoadClass( const char *inpath )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( inpath[ 0 ] == '/' || inpath[ 0 ] == '\\' )
+ ++inpath;
+
+ char filename[ 512 ];
+ Q_strncpy( filename, inpath, sizeof( filename ) );
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ Con_ErrorPrintf( "Can't load expressions from %s, must load a .mdl file first!\n",
+ filename );
+ return;
+ }
+
+ Con_Printf( "Loading expressions from %s\n", filename );
+
+ const char *classname = GetClassnameFromFilename( filename );
+
+ // Already loaded, don't do anything
+ if ( FindClass( classname, false ) )
+ return;
+
+ // Import actual data
+ LoadScriptFile( filename, SCRIPT_USE_RELATIVE_PATH );
+
+ CExpClass *active = AddCExpClass( classname, filename );
+ if ( !active )
+ return;
+
+ ActivateExpressionClass( active );
+
+ int numflexmaps = 0;
+ int flexmap[128]; // maps file local controls into global controls
+ LocalFlexController_t localflexmap[128]; // maps file local controls into local controls
+ bool bHasWeighting = false;
+ bool bNormalized = false;
+
+ EnableStickySnapshotMode( );
+
+ while (1)
+ {
+ GetToken (true);
+ if (endofscript)
+ break;
+ if (stricmp( token, "$keys" ) == 0)
+ {
+ numflexmaps = 0;
+ while (TokenAvailable())
+ {
+ flexmap[numflexmaps] = -1;
+ localflexmap[numflexmaps] = LocalFlexController_t(-1);
+
+ GetToken( false );
+ bool bFound = false;
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ if (stricmp( hdr->pFlexcontroller(i)->pszName(), token ) == 0)
+ {
+ localflexmap[numflexmaps] = i;
+ flexmap[numflexmaps] = AddGlobalFlexController( models->GetActiveStudioModel(),
+ hdr->pFlexcontroller(i)->pszName() );
+ bFound = true;
+ break;
+ }
+ }
+ if ( !bFound )
+ {
+ flexmap[ numflexmaps ] = AddGlobalFlexController( models->GetActiveStudioModel(), token );
+ }
+ numflexmaps++;
+ }
+ }
+ else if ( !stricmp( token, "$hasweighting" ) )
+ {
+ bHasWeighting = true;
+ }
+ else if ( !stricmp( token, "$normalized" ) )
+ {
+ bNormalized = true;
+ }
+ else
+ {
+ float setting[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float weight[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ char name[ 256 ];
+ char desc[ 256 ];
+ int index;
+
+ memset( setting, 0, sizeof( setting ) );
+ memset( weight, 0, sizeof( weight ) );
+
+ strcpy( name, token );
+
+ // phoneme index
+ GetToken( false );
+ if (token[1] == 'x')
+ {
+ sscanf( &token[2], "%x", &index );
+ }
+ else
+ {
+ index = (int)token[0];
+ }
+
+ // key values
+ for (int i = 0; i < numflexmaps; i++)
+ {
+ if (flexmap[i] > -1)
+ {
+ GetToken( false );
+ setting[flexmap[i]] = atof( token );
+ if (bHasWeighting)
+ {
+ GetToken( false );
+ weight[flexmap[i]] = atof( token );
+ }
+ else
+ {
+ weight[flexmap[i]] = 1.0;
+ }
+
+ if ( bNormalized && localflexmap[ i ] > -1 )
+ {
+ mstudioflexcontroller_t *pFlex = hdr->pFlexcontroller( localflexmap[i] );
+ if ( pFlex->min != pFlex->max )
+ {
+ setting[flexmap[i]] = Lerp( setting[flexmap[i]], pFlex->min, pFlex->max );
+ }
+ }
+ }
+ else
+ {
+ GetToken( false );
+ if (bHasWeighting)
+ {
+ GetToken( false );
+ }
+ }
+ }
+
+ // description
+ GetToken( false );
+ strcpy( desc, token );
+
+ CExpression *exp = active->AddExpression( name, desc, setting, weight, false, false );
+ if ( active->IsPhonemeClass() && exp )
+ {
+ if ( exp->index != index )
+ {
+ Con_Printf( "CExpressionManager::LoadClass (%s): phoneme index for %s in .txt file is wrong (expecting %i got %i), ignoring...\n",
+ classname, name, exp->index, index );
+ }
+ }
+ }
+ }
+
+ active->CheckBitmapConsistency();
+
+ DisableStickySnapshotMode( );
+
+ PopulateClassCB( active );
+
+ active->DeselectExpression();
+
+ Assert( !active->GetDirty() );
+ active->SetDirty( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void CExpressionManager::CreateNewClass( const char *filename )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ Con_ErrorPrintf( "Can't create new expression file %s, must load a .mdl file first!\n", filename );
+ return;
+ }
+
+ // Tell the use that the filename was loaded, expressions are empty for now
+ const char *classname = GetClassnameFromFilename( filename );
+
+ // Already loaded, don't do anything
+ if ( FindClass( classname, false ) )
+ return;
+
+ Con_Printf( "Creating %s\n", filename );
+
+ CExpClass *active = AddCExpClass( classname, filename );
+ if ( !active )
+ return;
+
+ ActivateExpressionClass( active );
+
+ // Select the newly created class
+ PopulateClassCB( active );
+
+ // Select first expression
+ active->SelectExpression( 0 );
+
+ // Nothing has changed so far
+ active->SetDirty( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *cl -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CExpressionManager::CloseClass( CExpClass *cl )
+{
+ if ( !cl )
+ return true;
+
+ if ( cl->GetDirty() )
+ {
+ int retval = mxMessageBox( NULL, va( "Save changes to class '%s'?", cl->GetName() ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return false;
+ }
+ if ( retval == 0 )
+ {
+ Con_Printf( "Saving changes to %s : %s\n", cl->GetName(), cl->GetFileName() );
+ cl->Save();
+ }
+ }
+
+ // The memory can be freed here, so be more careful
+ char temp[ 256 ];
+ V_strcpy_safe( temp, cl->GetName() );
+
+ RemoveCExpClass( cl );
+
+ Con_Printf( "Closed expression class %s\n", temp );
+
+ CExpClass *active = GetActiveClass();
+ if ( !active )
+ {
+ PopulateClassCB( NULL );
+ g_pExpressionTrayTool->redraw();
+ return true;
+ }
+
+ // Select the first remaining class
+ PopulateClassCB( active );
+
+ // Select first expression
+ active->DeselectExpression();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : classnum -
+//-----------------------------------------------------------------------------
+void CExpressionManager::PopulateClassCB( CExpClass *current )
+{
+ g_pExpressionClass->removeAll();
+ int select = 0;
+ for ( int i = 0; i < GetNumClasses(); i++ )
+ {
+ CExpClass *cl = GetClass( i );
+ if ( !cl )
+ continue;
+
+ g_pExpressionClass->add( cl->GetName() );
+
+ if ( cl == current )
+ {
+ select = i;
+ }
+ }
+
+ g_pExpressionClass->select( select );
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/expressions.h b/utils/hlfaceposer/expressions.h
new file mode 100644
index 0000000..ecd184c
--- /dev/null
+++ b/utils/hlfaceposer/expressions.h
@@ -0,0 +1,51 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( EXPRESSIONS_H )
+#define EXPRESSIONS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "studio.h"
+#include "expression.h"
+
+class FlexPanel;
+class ControlPanel;
+class MatSysWindow;
+class CExpClass;
+class ExpressionTool;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class IExpressionManager
+{
+public:
+ virtual void Reset( void ) = 0;
+
+ // File i/o
+ virtual void LoadClass( const char *filename ) = 0;
+ virtual void CreateNewClass( const char *filename ) = 0;
+ virtual bool CloseClass( CExpClass *cl ) = 0;
+ virtual void ActivateExpressionClass( CExpClass *cl ) = 0;
+
+ virtual CExpClass *AddCExpClass( const char *classname, const char *filename ) = 0;
+ virtual int GetNumClasses( void ) = 0;
+
+ virtual CExpression *GetCopyBuffer( void ) = 0;
+
+ virtual bool CanClose( void ) = 0;
+
+ virtual CExpClass *GetActiveClass( void ) = 0;
+ virtual CExpClass *GetClass( int num ) = 0;
+ virtual CExpClass *FindClass( const char *classname, bool bMatchBaseNameOnly ) = 0;
+
+};
+
+extern IExpressionManager *expressions;
+
+#endif // EXPRESSIONS_H \ No newline at end of file
diff --git a/utils/hlfaceposer/expressiontool.cpp b/utils/hlfaceposer/expressiontool.cpp
new file mode 100644
index 0000000..b303e7c
--- /dev/null
+++ b/utils/hlfaceposer/expressiontool.cpp
@@ -0,0 +1,4816 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "hlfaceposer.h"
+#include "ExpressionTool.h"
+#include "mdlviewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "TimelineItem.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "choreoevent.h"
+#include "StudioModel.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "ChoreoView.h"
+#include "InputProperties.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include "mxExpressionTray.h"
+#include "ExpressionProperties.h"
+#include "tier1/strtools.h"
+#include "faceposer_models.h"
+#include "UtlBuffer.h"
+#include "filesystem.h"
+#include "iscenetokenprocessor.h"
+#include "MatSysWin.h"
+#include "choreoviewcolors.h"
+#include "scriplib.h"
+#include "EdgeProperties.h"
+
+ExpressionTool *g_pExpressionTool = 0;
+
+#define TRAY_HEIGHT 55
+
+#define TRAY_ITEM_INSET 10
+
+#define MAX_TIME_ZOOM 1000
+// 10% per step
+#define TIME_ZOOM_STEP 2
+
+void SetupFlexControllerTracks( CStudioHdr *hdr, CChoreoEvent *event );
+
+class CExpressionToolWorkspace : public mxWindow
+{
+public:
+ CExpressionToolWorkspace( mxWindow *parent );
+ ~CExpressionToolWorkspace();
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground( void )
+ {
+ redraw();
+ return false;
+ }
+
+ void RepositionVSlider( void );
+ int ComputeVPixelsNeeded( void );
+ // Playback tick
+ void Think( float dt );
+
+ void LayoutItems( bool force = false );
+
+ void HideTimelines( void );
+ void CollapseAll( TimelineItem *keepExpanded );
+
+ void ExpandAll( void );
+ void ExpandValid( void );
+ void DisableAllExcept( void );
+ void EnableValid( void );
+
+ TimelineItem *GetItem( int number );
+ TimelineItem *GetClickedItem( void );
+ void ClearClickedItem( void );
+
+ void OnSnapAll();
+ void OnDeleteColumn();
+
+ void MoveSelectedSamples( float dfdx, float dfdy, bool snap );
+ void DeleteSelectedSamples( void );
+ int CountSelectedSamples( void );
+ void DeselectAll( void );
+ void SelectPoints( float start, float end );
+
+ void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
+
+ void OnSortByUsed( void );
+ void OnSortByName( void );
+
+private:
+
+ int GetItemUnderMouse( int mx, int my );
+
+ void MouseToToolMouse( int& mx, int& my, char *reason );
+
+ TimelineItem *m_pItems[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ // The scroll bars
+ mxScrollbar *m_pVertScrollBar;
+ int m_nLastVPixelsNeeded;
+
+ int m_nTopOffset;
+ int m_nScrollbarHeight;
+
+ int m_nItemGap;
+ int m_nFocusItem;
+};
+
+CExpressionToolWorkspace::CExpressionToolWorkspace( mxWindow *parent ) :
+ mxWindow( parent, 0, 0, 0, 0 )
+{
+ HWND wnd = (HWND)getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+ SetWindowLong( wnd, GWL_STYLE, style );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ m_pItems[ i ] = new TimelineItem( this );
+ }
+
+ m_nItemGap = 2;
+
+ m_nScrollbarHeight = 12;
+ m_nTopOffset = 0;
+
+ m_nLastVPixelsNeeded = -1;
+
+ m_pVertScrollBar = new mxScrollbar( this, 0, 0, 12, 100, IDC_EXPRESSIONTOOLVSCROLL, mxScrollbar::Vertical );
+
+ m_nFocusItem = -1;
+
+ HideTimelines();
+ LayoutItems();
+}
+
+CExpressionToolWorkspace::~CExpressionToolWorkspace()
+{
+}
+
+void CExpressionToolWorkspace::redraw()
+{
+ CChoreoWidgetDrawHelper drawHelper( this );
+
+ DrawEventEnd( drawHelper );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ if ( !item )
+ continue;
+
+ if ( !item->GetVisible() )
+ continue;
+
+ RECT rcBounds;
+ item->GetBounds( rcBounds );
+
+ if ( rcBounds.bottom < 0 )
+ continue;
+ if ( rcBounds.top > h2() )
+ continue;
+
+ item->Draw( drawHelper );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *elem1 -
+// *elem2 -
+// Output : int
+//-----------------------------------------------------------------------------
+int SortFuncByUse(const void *elem1, const void *elem2 )
+{
+ TimelineItem *item1 = *( TimelineItem ** )elem1;
+ TimelineItem *item2 = *( TimelineItem ** )elem2;
+
+ if ( item1->IsValid() == item2->IsValid() )
+ return 0;
+
+ if ( !item2->IsValid() && item1->IsValid() )
+ return -1;
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *elem1 -
+// *elem2 -
+// Output : int
+//-----------------------------------------------------------------------------
+int SortFuncByName(const void *elem1, const void *elem2 )
+{
+ TimelineItem *item1 = *( TimelineItem ** )elem1;
+ TimelineItem *item2 = *( TimelineItem ** )elem2;
+
+ CFlexAnimationTrack *track1 = item1->GetSafeTrack();
+ CFlexAnimationTrack *track2 = item2->GetSafeTrack();
+
+ if ( !track1 || !track2 )
+ {
+ if ( track1 )
+ return -1;
+ if ( track2 )
+ return 1;
+ return 0;
+ }
+
+ return stricmp( track1->GetFlexControllerName(), track2->GetFlexControllerName() );
+}
+
+void CExpressionToolWorkspace::OnSortByUsed( void )
+{
+ qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByUse );
+ LayoutItems( false );
+}
+
+void CExpressionToolWorkspace::OnSortByName( void )
+{
+ qsort( m_pItems, GLOBAL_STUDIO_FLEX_CONTROL_COUNT, sizeof( TimelineItem * ), SortFuncByName );
+ LayoutItems( false );
+}
+
+void CExpressionToolWorkspace::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ if ( !g_pExpressionTool )
+ return;
+
+ CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+ if ( !duration )
+ return;
+
+ int leftx = g_pExpressionTool->GetPixelForTimeValue( duration ) -5;
+ if ( leftx >= w2() )
+ return;
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, rcClient.top, leftx, rcClient.bottom );
+
+}
+
+int CExpressionToolWorkspace::GetItemUnderMouse( int mx, int my )
+{
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ if ( !item )
+ continue;
+
+ if ( !item->GetVisible() )
+ continue;
+
+ RECT rc;
+ item->GetBounds( rc );
+
+ if ( PtInRect( &rc, pt ) )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void CExpressionToolWorkspace::MouseToToolMouse( int& mx, int& my, char *reason )
+{
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+ ScreenToClient( (HWND)getParent()->getHandle(), &pt );
+
+ mx = pt.x;
+ my = pt.y;
+}
+
+int CExpressionToolWorkspace::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ switch ( event->event )
+ {
+ case mxEvent::MouseDown:
+ {
+ HWND wnd = (HWND)getParent()->getHandle();
+ SetFocus( wnd );
+ SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedown" );
+
+ g_pExpressionTool->SetClickedPos( mx, my );
+ g_pExpressionTool->SetMouseOverPos( mx, my );
+ g_pExpressionTool->DrawMouseOverPos();
+
+ }
+
+ int oldFocus = m_nFocusItem;
+ m_nFocusItem = GetItemUnderMouse( (short)event->x, (short)event->y );
+
+ if ( oldFocus != -1 &&
+ oldFocus != m_nFocusItem )
+ {
+ TimelineItem *item = GetItem( oldFocus );
+ if ( item )
+ {
+ item->DrawSelf();
+ }
+ }
+ if ( m_nFocusItem != -1 )
+ {
+ TimelineItem *item = GetItem( m_nFocusItem );
+ if ( item )
+ {
+ RECT rc;
+ item->GetBounds( rc );
+
+ event->x -= rc.left;
+ event->y -= rc.top;
+
+ iret = item->handleEvent( event );
+ }
+ }
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ //
+ bool handled = false;
+
+ if ( m_nFocusItem != -1 )
+ {
+ TimelineItem *item = GetItem( m_nFocusItem );
+ if ( item )
+ {
+ RECT rc;
+ item->GetBounds( rc );
+
+ event->x -= rc.left;
+ event->y -= rc.top;
+
+ iret = item->handleEvent( event );
+
+ if ( event->event == mxEvent::MouseDrag )
+ {
+ int mx, my;
+
+ item->GetLastMouse( mx, my );
+ mx += rc.left;
+ my += rc.top;
+
+ MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousedrag" );
+
+ g_pExpressionTool->SetMouseOverPos( mx, my );
+ g_pExpressionTool->DrawMouseOverPos();
+ handled = true;
+ }
+ }
+ }
+
+ if ( !handled )
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ mx += TRAY_ITEM_INSET;
+
+ MouseToToolMouse( mx, my, "CExpressionToolWorkspace mousemove" );
+
+ g_pExpressionTool->SetMouseOverPos( mx, my );
+ g_pExpressionTool->DrawMouseOverPos();
+ }
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ //
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ MouseToToolMouse( mx, my, "CExpressionToolWorkspace mouseup" );
+
+ g_pExpressionTool->SetMouseOverPos( mx, my );
+ g_pExpressionTool->DrawMouseOverPos();
+
+ }
+
+ if ( m_nFocusItem != -1 )
+ {
+ TimelineItem *item = GetItem( m_nFocusItem );
+ if ( item )
+ {
+ RECT rc;
+ item->GetBounds( rc );
+
+ event->x -= rc.left;
+ event->y -= rc.top;
+
+ iret = item->handleEvent( event );
+ }
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ RepositionVSlider();
+ LayoutItems();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ // Tell parent
+ {
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int tz = g_pChoreoView->GetTimeZoom( g_pExpressionTool->GetToolName() );
+
+ // Zoom time in / out
+ if ( event->height > 0 )
+ {
+ g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz + TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
+ }
+ else
+ {
+ g_pChoreoView->SetTimeZoom( g_pExpressionTool->GetToolName(), min( tz - TIME_ZOOM_STEP, MAX_TIME_ZOOM ), false );
+ }
+ g_pExpressionTool->RepositionHSlider();
+ }
+ redraw();
+ iret = 1;
+ return iret;
+ }
+
+ int offset = 0;
+ int jump = 50;
+
+ if ( event->height < 0 )
+ {
+ offset = m_pVertScrollBar->getValue();
+ offset += jump;
+ offset = min( offset, m_pVertScrollBar->getMaxValue() );
+ }
+ else
+ {
+ offset = m_pVertScrollBar->getValue();
+ offset -= jump;
+ offset = max( offset, m_pVertScrollBar->getMinValue() );
+ }
+
+ m_pVertScrollBar->setValue( offset );
+ InvalidateRect( (HWND)m_pVertScrollBar->getHandle(), NULL, TRUE );
+ m_nTopOffset = offset;
+ LayoutItems();
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_EXPRESSIONTOOLVSCROLL:
+ {
+ int offset = 0;
+ bool processed = true;
+
+ switch ( event->modifiers )
+ {
+ case SB_THUMBTRACK:
+ offset = event->height;
+ break;
+ case SB_PAGEUP:
+ offset = m_pVertScrollBar->getValue();
+ offset -= 100;
+ offset = max( offset, m_pVertScrollBar->getMinValue() );
+ break;
+ case SB_PAGEDOWN:
+ offset = m_pVertScrollBar->getValue();
+ offset += 100;
+ 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;
+ LayoutItems();
+ }
+ }
+ }
+ }
+ break;
+ }
+ return iret;
+}
+
+void CExpressionToolWorkspace::HideTimelines( void )
+{
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ Assert( item );
+ item->SetVisible( false );
+ }
+
+ redraw();
+}
+
+
+TimelineItem *CExpressionToolWorkspace::GetItem( int number )
+{
+ if ( number < 0 || number >= GLOBAL_STUDIO_FLEX_CONTROL_COUNT )
+ {
+ return NULL;
+ }
+ return m_pItems[ number ];
+}
+
+TimelineItem *CExpressionToolWorkspace::GetClickedItem( void )
+{
+ return GetItem( m_nFocusItem );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::ClearClickedItem( void )
+{
+ m_nFocusItem = -1;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : force - force vert scrollbar recomputation
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::LayoutItems( bool force /* = false */ )
+{
+ int x = TRAY_ITEM_INSET;
+ int y = - m_nTopOffset;
+ int width = w2() - 2 * TRAY_ITEM_INSET - m_nScrollbarHeight;
+ int height;
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ if ( !item || !item->GetVisible() )
+ continue;
+
+ height = item->GetHeight();
+
+ RECT rcBounds;
+ rcBounds.left = x;
+ rcBounds.top = y;
+ rcBounds.right = x + width;
+ rcBounds.bottom = y + height;
+
+ item->SetBounds( rcBounds );
+ y += height + m_nItemGap;
+ }
+
+ if ( force || ( ComputeVPixelsNeeded() != m_nLastVPixelsNeeded ) )
+ {
+ RepositionVSlider();
+ }
+
+ redraw();
+}
+
+int CExpressionToolWorkspace::ComputeVPixelsNeeded( void )
+{
+ int pixels = 0;
+
+ // Count visible
+ int c = 0;
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ if ( !item || !item->GetVisible() )
+ continue;
+
+ c += item->GetHeight();
+ c += m_nItemGap;
+ }
+
+ pixels += c;
+
+ return pixels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::RepositionVSlider( void )
+{
+ int pixelsneeded = ComputeVPixelsNeeded();
+
+ if ( pixelsneeded <= ( h2() ))
+ {
+ m_pVertScrollBar->setVisible( false );
+ m_nTopOffset = 0;
+ }
+ else
+ {
+ m_pVertScrollBar->setVisible( true );
+ }
+
+ m_pVertScrollBar->setBounds(
+ w2() - m_nScrollbarHeight,
+ 0,
+ m_nScrollbarHeight,
+ h2() );
+
+ 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() );
+
+ m_nLastVPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::DisableAllExcept( void )
+{
+ TimelineItem *keepExpanded = GetClickedItem();
+ if ( !keepExpanded )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Disable All Except" );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+
+ item->SetActive( item == keepExpanded ? true : false );
+ }
+
+ LayoutItems();
+ g_pChoreoView->PushRedo( "Disable All Except" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::EnableValid( void )
+{
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Enable Valid" );
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+
+ item->SetActive( item->IsValid() );
+ }
+
+ LayoutItems();
+ g_pChoreoView->PushRedo( "Enable Valid" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::CollapseAll( TimelineItem *keepExpanded )
+{
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+
+ item->SetCollapsed( item == keepExpanded ? false : true );
+ }
+
+ LayoutItems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::ExpandAll( void )
+{
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ item->SetCollapsed( false );
+ }
+
+ LayoutItems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::OnSnapAll()
+{
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Snap All" );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ item->SnapAll();
+ }
+
+ g_pChoreoView->PushRedo( "Snap All" );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::OnDeleteColumn()
+{
+ float t = g_pExpressionTool->GetTimeForClickedPos();
+
+ float snapped = FacePoser_SnapTime( t );
+ int scenefps = FacePoser_GetSceneFPS();
+
+ if ( scenefps <= 0 )
+ {
+ Con_Printf( "Can't delete column, scene fps is <= 0 (%i)\n", scenefps );
+ return;
+ }
+
+ int clickedframe = ( int ) ( scenefps * snapped + 0.5f );
+
+ // One half of 1/fps on each side
+ float epsilon = epsilon = 0.5f / (float)scenefps;
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Delete Column" );
+ strcpy( params.m_szPrompt, "Frame(s) to delete [e.g., 82 or 81-91 ]:" );
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%i", clickedframe );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ int deleteframestart;
+ int deleteframeend;
+
+ char *sep = Q_strstr( params.m_szInputText, "-" );
+ if ( sep )
+ {
+ *sep = 0;
+ deleteframestart = atoi( params.m_szInputText );
+ deleteframeend = atoi( sep + 1 );
+ deleteframeend = max( deleteframestart, deleteframeend );
+ }
+ else
+ {
+ deleteframestart = atoi( params.m_szInputText );
+ deleteframeend = deleteframestart;
+ }
+
+ float start, end;
+
+ start = (float)deleteframestart / (float)scenefps;
+ end = (float)deleteframeend / (float)scenefps;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Delete Column" );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ item->DeletePoints( start - epsilon, end + epsilon );
+ }
+
+ g_pChoreoView->PushRedo( "Delete Column" );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::ExpandValid( void )
+{
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ bool valid = item->IsValid();
+ item->SetCollapsed( !valid );
+ }
+
+ LayoutItems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CExpressionToolWorkspace::CountSelectedSamples( void )
+{
+ int c = 0;
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = GetItem( i );
+ Assert( item );
+ item->CountSelected();
+ c += item->GetNumSelected();
+ }
+ return c;
+}
+
+void CExpressionToolWorkspace::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
+{
+ int selecteditems = CountSelectedSamples();
+ if ( !selecteditems )
+ return;
+
+ CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
+ if ( !e )
+ return;
+
+ float eventduration = e->GetDuration();
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ // If the track is a combo type track, then move any underlying selected samples, too
+ for ( int edittype = 0; edittype <= ( track->IsComboType() ? 1 : 0 ); edittype++ )
+ {
+ for ( int i = 0; i < (int)track->GetNumSamples( edittype ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, edittype );
+ if ( !sample || !sample->selected )
+ continue;
+
+ sample->time += dfdx;
+ sample->time = clamp( sample->time, 0.0f, eventduration );
+
+ if ( snap )
+ {
+ sample->time = FacePoser_SnapTime( sample->time );
+ }
+
+ sample->value -= dfdy;
+ sample->value = clamp( sample->value, 0.0f, 1.0f );
+ }
+ }
+
+ track->Resort();
+
+ item->DrawSelf();
+ }
+}
+
+void CExpressionToolWorkspace::DeleteSelectedSamples( void )
+{
+ int i, t;
+
+ int selecteditems = CountSelectedSamples();
+ if ( !selecteditems )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Delete points" );
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( t = 0; t < 2; t++ )
+ {
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ track->RemoveSample( i, t );
+ }
+ }
+
+ item->DrawSelf();
+ }
+
+ g_pChoreoView->PushRedo( "Delete points" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExpressionToolWorkspace::DeselectAll( void )
+{
+ int i, t;
+
+ int selecteditems = CountSelectedSamples();
+ if ( !selecteditems )
+ return;
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( t = 0; t < 2; t++ )
+ {
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ sample->selected = false;
+ }
+ }
+
+ item->DrawSelf();
+ }
+}
+
+void CExpressionToolWorkspace::SelectPoints( float start, float end )
+{
+ int i, t;
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( t = 0; t < 2; t++ )
+ {
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ bool inrange = ( sample->time >= start && sample->time <= end );
+ sample->selected = inrange;
+ }
+ }
+
+ item->DrawSelf();
+ }
+}
+
+ExpressionTool::ExpressionTool( mxWindow *parent )
+: IFacePoserToolWindow( "ExpressionTool", "Flex Animation" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_bSuppressLayout = false;
+
+ SetAutoProcess( true );
+
+ m_pWorkspace = new CExpressionToolWorkspace( this );
+
+ m_nFocusEventGlobalID = -1;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+ m_nDragType = DRAGTYPE_NONE;
+
+ m_nClickedX = 0;
+ m_nClickedY = 0;
+
+ m_hPrevCursor = 0;
+
+ m_nStartX = 0;
+ m_nStartY = 0;
+
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ m_bUseBounds = false;
+
+ m_pLastEvent = NULL;
+
+ m_nMousePos[ 0 ] = m_nMousePos[ 1 ] = 0;
+
+ m_flSelection[ 0 ] = m_flSelection[ 1 ] = 0.0f;
+ m_bSelectionActive = false;
+
+ m_bLayoutIsValid = false;
+ m_flPixelsPerSecond = 500.0f;
+
+ m_flLastDuration = 0.0f;
+ m_nScrollbarHeight = 12;
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_FLEXHSCROLL, mxScrollbar::Horizontal );
+ m_pHorzScrollBar->setVisible( false );
+
+ m_bInSetEvent = false;
+ m_flScrubberTimeOffset = 0.0f;
+}
+
+ExpressionTool::~ExpressionTool( void )
+{
+}
+
+void ExpressionTool::DoTrackLookup( CChoreoEvent *event )
+{
+ if ( !event || !models->GetActiveStudioModel() )
+ return;
+
+ //if ( event->GetTrackLookupSet() )
+ // return;
+
+ // Force recompute
+ SetEvent( event );
+}
+
+#pragma optimize( "g", off )
+
+void ExpressionTool::SetEvent( CChoreoEvent *event )
+{
+ if ( m_bInSetEvent )
+ return;
+
+ m_bInSetEvent = true;
+
+ if ( event == m_pLastEvent )
+ {
+ if ( event )
+ {
+ float dur = event->GetDuration();
+ if ( dur != m_flLastDuration )
+ {
+ m_flLastDuration = dur;
+ m_nLastHPixelsNeeded = -1;
+ m_flLeftOffset = 0.0f;
+ InvalidateLayout();
+ }
+
+ m_nFocusEventGlobalID = event->GetGlobalID();
+ }
+ m_bInSetEvent = false;
+ return;
+ }
+
+ m_pLastEvent = event;
+
+ m_pWorkspace->HideTimelines();
+
+ m_nFocusEventGlobalID = -1;
+ if ( event )
+ {
+ m_nFocusEventGlobalID = event->GetGlobalID();
+
+ if ( models->GetActiveStudioModel() )
+ {
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ // Force re-lookup
+ event->SetTrackLookupSet( false );
+
+ SetupFlexControllerTracks( hdr, event );
+
+ int itemCount = 0;
+
+ for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ Assert( track );
+ if ( !track )
+ continue;
+
+ TimelineItem *item = m_pWorkspace->GetItem( itemCount++ );
+ item->SetExpressionInfo( track, track->GetFlexControllerIndex( 0 ) );
+ item->SetCollapsed( track->GetNumSamples( 0 ) <= 0 );
+ item->SetVisible( true );
+ }
+
+ m_pWorkspace->LayoutItems( true );
+ }
+ }
+ }
+
+ DeselectAll();
+
+ if ( event )
+ {
+ m_flLastDuration = event->GetDuration();
+ }
+ else
+ {
+ m_flLastDuration = 0.0f;
+ }
+
+ m_flLeftOffset = 0.0f;
+ m_nLastHPixelsNeeded = -1;
+ InvalidateLayout();
+
+ m_bInSetEvent = false;
+}
+
+#pragma optimize( "g", on )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ExpressionTool::HasCopyData( void )
+{
+ return ( m_CopyData[0].Size() != 0 ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *source -
+//-----------------------------------------------------------------------------
+void ExpressionTool::Copy( CFlexAnimationTrack *source )
+{
+ for ( int t = 0; t < 2; t++ )
+ {
+ m_CopyData[ t ].RemoveAll();
+
+ if ( t == 0 || source->IsComboType() )
+ {
+ for ( int i = 0 ; i < source->GetNumSamples( t ); i++ )
+ {
+ CExpressionSample *s = source->GetSample( i, t );
+ m_CopyData[ t ].AddToTail( *s );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *destination -
+//-----------------------------------------------------------------------------
+void ExpressionTool::Paste( CFlexAnimationTrack *destination )
+{
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Paste" );
+
+ destination->Clear();
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ for ( int i = 0; i < m_CopyData[ t ].Size() ; i++ )
+ {
+ CExpressionSample *s = &m_CopyData[ t ][ i ];
+
+ if ( t == 0 || destination->IsComboType() )
+ {
+ destination->AddSample( s->time, s->value, t );
+ }
+ }
+
+ destination->Resort( t );
+ }
+ g_pChoreoView->PushRedo( "Paste" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CChoreoEvent *ExpressionTool::GetSafeEvent( void )
+{
+ if ( m_nFocusEventGlobalID == -1 )
+ return NULL;
+
+ if ( !g_pChoreoView )
+ return NULL;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return NULL;
+
+ // look to see if it's focused any any event
+ for ( int i = 0; i < scene->GetNumEvents() ; i++ )
+ {
+ CChoreoEvent *e = scene->GetEvent( i );
+ if ( !e || e->GetType() != CChoreoEvent::FLEXANIMATION )
+ continue;
+
+ if ( e->GetGlobalID() == m_nFocusEventGlobalID )
+ {
+ DoTrackLookup( e );
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void ExpressionTool::GetScrubHandleRect( RECT& rcHandle, bool clipped )
+{
+ float pixel = 0.0f;
+ if ( m_pWorkspace->w2() > 0 )
+ {
+ pixel = GetPixelForTimeValue( m_flScrub );
+ 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 : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void ExpressionTool::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle )
+{
+ HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
+
+ drawHelper.DrawFilledRect( br, rcHandle );
+
+ //
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", m_flScrub );
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ float st, ed;
+ st = ev->GetStartTime();
+ ed = ev->GetEndTime();
+
+ float dt = ed - st;
+ if ( dt > 0.0f )
+ {
+ sprintf( sz, "%.3f", st + 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 ExpressionTool::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;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ExpressionTool::IsProcessing( void )
+{
+ if ( !GetSafeEvent() )
+ return false;
+
+ if ( m_flScrub != m_flScrubTarget )
+ return true;
+
+ return false;
+}
+
+bool ExpressionTool::IsScrubbing( void ) const
+{
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ return scrubbing;
+}
+
+void ExpressionTool::Think( float dt )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ bool scrubbing = IsScrubbing();
+
+ ScrubThink( dt, scrubbing );
+}
+
+void ExpressionTool::SetScrubTime( float t )
+{
+ m_flScrub = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ float realtime = e->GetStartTime() + m_flScrub;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->DrawScrubHandle();
+ }
+}
+
+void ExpressionTool::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ float realtime = e->GetStartTime() + m_flScrubTarget;
+
+ g_pChoreoView->SetScrubTargetTime( realtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void ExpressionTool::ScrubThink( float dt, bool scrubbing )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ return;
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt;
+
+ if ( sign > 0 )
+ {
+ if ( d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub + maxmove );
+ }
+ }
+ else
+ {
+ if ( -d < maxmove )
+ {
+ SetScrubTime( m_flScrubTarget );
+ }
+ else
+ {
+ SetScrubTime( m_flScrub - maxmove );
+ }
+ }
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+}
+
+void ExpressionTool::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ RECT rcSelection;
+ GetWorkspaceRect( rcSelection );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcSelection.top, w2(), rcSelection.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcSelection.bottom, w2(), rcSelection.bottom );
+
+ if ( m_bSelectionActive )
+ {
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ int left, right;
+ left = GetPixelForTimeValue( m_flSelection[ 0 ] );
+ right = GetPixelForTimeValue( m_flSelection[ 1 ] );
+
+ rcSelection.left = left;
+ rcSelection.right = right;
+ rcSelection.bottom = TRAY_HEIGHT;
+
+ drawHelper.DrawFilledRect( RGB( 200, 220, 230 ), rcSelection );
+
+ drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.left, rcSelection.top, rcSelection.left, rcSelection.bottom );
+ drawHelper.DrawColoredLine( RGB( 100, 100, 255 ), PS_SOLID, 3, rcSelection.right, rcSelection.top, rcSelection.right, rcSelection.bottom );
+
+ }
+
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( ev )
+ {
+ RECT rcText;
+ drawHelper.GetClientRect( rcText );
+ rcText.top += GetCaptionHeight()+1;
+ rcText.bottom = rcText.top + 13;
+ rcText.left += 5;
+ rcText.right -= 5;
+
+ OffsetRect( &rcText, 0, 12 );
+
+ int current, total;
+
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ RECT rcUndo = rcText;
+ OffsetRect( &rcUndo, 0, 2 );
+
+ drawHelper.DrawColoredText( "Small Fonts", 8, FW_NORMAL, RGB( 0, 100, 0 ), rcUndo,
+ "Undo: %i/%i", current, total );
+ }
+
+ rcText.left += 60;
+
+ // Found it, write out description
+ //
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 200, 150, 100 ), rcText,
+ "Event: %s",
+ ev->GetName() );
+
+ OffsetRect( &rcText, 0, 30 );
+
+ rcText.left = 5;
+
+ RECT timeRect = rcText;
+
+ timeRect.right = timeRect.left + 100;
+
+ char sz[ 32 ];
+
+ float st, ed;
+
+ GetStartAndEndTime( st, ed );
+
+ st += ev->GetStartTime();
+ ed += ev->GetStartTime();
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", st );
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+
+ timeRect = rcText;
+
+ Q_snprintf( sz, sizeof( sz ), "%.2f", ed );
+
+ int textW = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
+
+ timeRect.right = w2() - 10;
+ timeRect.left = timeRect.right - textW;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), timeRect, sz );
+ }
+
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+ DrawScrubHandle( drawHelper, rcHandle );
+
+ DrawRelativeTags( drawHelper );
+
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+
+ DrawEventEnd( drawHelper );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *tag -
+//-----------------------------------------------------------------------------
+bool ExpressionTool::GetTimingTagRect( RECT& rcClient, CChoreoEvent *event, CFlexTimingTag *tag, RECT& rcTag )
+{
+ rcTag = rcClient;
+
+ int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime() );
+
+ rcTag.top = rcClient.bottom - 6;
+ rcTag.bottom = rcTag.top + 6;
+ rcTag.left = tagx - 3;
+ rcTag.right = tagx + 3;
+
+ return true;
+}
+
+// Get workspace min, max point in terms of tool window
+void ExpressionTool::GetWorkspaceLeftRight( int& left, int& right )
+{
+ POINT pt;
+ pt.x = TRAY_ITEM_INSET;
+ pt.y = 0;
+
+ ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ left = (short)pt.x;
+
+ pt.x = m_pWorkspace->w2() - TRAY_ITEM_INSET - 12;
+ pt.y = 0;
+
+ ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ right = (short)pt.x;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : CFlexTimingTag
+//-----------------------------------------------------------------------------
+CFlexTimingTag *ExpressionTool::IsMouseOverTag( int mx, int my )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return NULL;
+
+ RECT rcClient;
+ GetClientRect( (HWND)getHandle(), &rcClient );
+
+ int left, right;
+
+ GetWorkspaceLeftRight( left, right );
+
+ rcClient.left = left;
+ rcClient.right = right;
+ rcClient.top = GetCaptionHeight();
+ rcClient.bottom = rcClient.top + TRAY_HEIGHT;
+
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ for ( int i = 0 ; i < event->GetNumTimingTags(); i++ )
+ {
+ CFlexTimingTag *tag = event->GetTimingTag( i );
+ if ( !tag )
+ continue;
+
+ RECT rcTag;
+
+ if ( !GetTimingTagRect( rcClient, event, tag, rcTag ) )
+ continue;
+
+ if ( !PtInRect( &rcTag, pt ) )
+ continue;
+
+ return tag;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+//-----------------------------------------------------------------------------
+void ExpressionTool::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ if ( event->GetDuration() <= 0.0f )
+ return;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return;
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ int left, right;
+
+ GetWorkspaceLeftRight( left, right );
+
+ rcClient.top += GetCaptionHeight();
+
+ rcClient.left = left;
+ rcClient.right = right;
+
+ rcClient.bottom = rcClient.top + TRAY_HEIGHT;
+
+ // Iterate relative tags
+ for ( int i = 0; i < scene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannel *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0 ; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEvent *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ // add each tag to combo box
+ for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
+ {
+ CEventRelativeTag *tag = e->GetRelativeTag( t );
+ if ( !tag )
+ continue;
+
+ //SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
+ bool clipped;
+ int tagx = GetPixelForTimeValue( tag->GetStartTime() - event->GetStartTime(), &clipped );
+ if ( clipped )
+ continue;
+
+ //drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
+
+ RECT rcMark;
+ rcMark = rcClient;
+ rcMark.top = rcClient.bottom - 6;
+ rcMark.left = tagx - 3;
+ rcMark.right = tagx + 3;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 0, 100, 250 ) );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 10;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = tagx - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 100, 200 ), rcText, tag->GetName() );
+
+ }
+ }
+ }
+ }
+
+ for ( int t = 0; t < event->GetNumTimingTags(); t++ )
+ {
+ CFlexTimingTag *tag = event->GetTimingTag( t );
+ if ( !tag )
+ continue;
+
+ RECT rcMark;
+
+ if ( !GetTimingTagRect( rcClient, event, tag, rcMark ) )
+ continue;
+
+ drawHelper.DrawTriangleMarker( rcMark, RGB( 250, 100, 0 ) );
+
+ RECT rcText;
+ rcText = rcMark;
+ rcText.top -= 20;
+
+ char text[ 256 ];
+ sprintf( text, "%s", tag->GetName() );
+ if ( tag->GetLocked() )
+ {
+ strcat( text, " - locked" );
+ }
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, text );
+ rcText.left = ( rcMark.left + rcMark.right ) / 2 - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ rcText.bottom = rcText.top + 10;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 100, 0 ), rcText, text );
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::ShowContextMenu( mxEvent *event, bool include_track_menus )
+{
+ // Construct main menu
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ TimelineItem *item = NULL;
+ CFlexAnimationTrack *track = NULL;
+
+ if ( include_track_menus )
+ {
+ item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->CountSelected();
+ track = item->GetSafeTrack();
+ }
+ }
+
+ int current, total;
+ g_pChoreoView->GetUndoLevels( current, total );
+ if ( total > 0 )
+ {
+ if ( current > 0 )
+ {
+ pop->add( va( "Undo %s", g_pChoreoView->GetUndoDescription() ), IDC_UNDO_FA );
+ }
+
+ if ( current <= total - 1 )
+ {
+ pop->add( va( "Redo %s", g_pChoreoView->GetRedoDescription() ), IDC_REDO_FA );
+ }
+ pop->addSeparator();
+ }
+
+ // Create expand menu
+ mxPopupMenu *expand = new mxPopupMenu();
+ if ( item && track && item->IsCollapsed() )
+ {
+ expand->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_EXPAND );
+ }
+ expand->add( "All tracks", IDC_EXPANDALL );
+ expand->add( "Used tracks", IDC_EXPANDVALID );
+
+ pop->addMenu( "Expand", expand );
+
+ mxPopupMenu *collapse = new mxPopupMenu;
+
+ if ( item && track && !item->IsCollapsed() )
+ {
+ collapse->add( va( "Track '%s'", track->GetFlexControllerName() ), IDC_TL_COLLAPSE );
+ collapse->add( va( "All tracks except '%s'", track->GetFlexControllerName() ), IDC_COLLAPSE_ALL_EXCEPT );
+ }
+
+ collapse->add( "All tracks", IDC_COLLAPSEALL );
+
+ pop->addMenu( "Collapse", collapse );
+
+ pop->addSeparator();
+
+ pop->add( va( "Enable all valid" ), IDC_ENABLE_ALL_VALID );
+
+ if ( item && track )
+ {
+ if ( item->IsActive() )
+ {
+ pop->add( va( "Disable '%s'", track->GetFlexControllerName() ), IDC_TL_DISABLE );
+ }
+ else
+ {
+ pop->add( va( "Enable '%s'", track->GetFlexControllerName() ), IDC_TL_ENABLE );
+ }
+ pop->add( va( "Disable all except '%s'", track->GetFlexControllerName() ), IDC_DISABLE_ALL_EXCEPT );
+
+ pop->addSeparator();
+ pop->add( "Copy", IDC_TL_COPY );
+ if ( HasCopyData() )
+ {
+ pop->add( "Paste", IDC_TL_PASTE );
+ }
+
+ pop->addSeparator();
+ if ( item->GetNumSelected() > 0 )
+ {
+ pop->add( va( "Delete" ), IDC_TL_DELETE );
+ pop->add( "Deselect all", IDC_TL_DESELECT );
+ pop->add( va( "Scale selected..." ), IDC_FLEX_SCALESAMPLES );
+ }
+ pop->add( "Select all", IDC_TL_SELECTALL );
+
+ if ( FacePoser_IsSnapping() )
+ {
+ mxPopupMenu *snap = new mxPopupMenu();
+
+ snap->add( va( "All points" ), IDC_TL_SNAPALL );
+ snap->add( va( "All points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPPOINTS );
+ snap->add( va( "Selected points in '%s'", track->GetFlexControllerName() ), IDC_TL_SNAPSELECTED );
+
+ pop->addSeparator();
+
+ pop->addMenu( "Snap", snap );
+ }
+
+ if ( track->IsComboType() )
+ {
+ pop->addSeparator();
+
+ if ( item->GetEditType() == 0 )
+ {
+ pop->add( "Edit <left/right>", IDC_TL_EDITLEFTRIGHT );
+ }
+ else
+ {
+ pop->add( "Edit <amount>", IDC_TL_EDITNORMAL );
+ }
+ }
+
+ pop->addSeparator();
+ mxPopupMenu *heightMenu = new mxPopupMenu();
+ heightMenu->add( va( "Reset '%s'", track->GetFlexControllerName() ) , IDC_ET_RESET_ITEM_SIZE );
+ heightMenu->add( "Reset All", IDC_ET_RESET_ALL_ITEM_SIZES );
+ pop->addMenu( "Height", heightMenu );
+
+ pop->addSeparator();
+ pop->add( "Edge Properties...", IDC_ET_EDGEPROPERTIES );
+ }
+ pop->addSeparator();
+
+ mxPopupMenu *tagmenu = new mxPopupMenu();
+
+ CFlexTimingTag *tag = IsMouseOverTag( (short)event->x, (short)event->y );
+ if ( tag )
+ {
+ if ( tag->GetLocked() )
+ {
+ tagmenu->add( va( "Unlock tag '%s'...", tag->GetName() ), IDC_UNLOCK_TIMING_TAG );
+ }
+ else
+ {
+ tagmenu->add( va( "Lock tag '%s'...", tag->GetName() ), IDC_LOCK_TIMING_TAG );
+ }
+ tagmenu->addSeparator();
+ tagmenu->add( va( "Delete tag '%s'...", tag->GetName() ), IDC_DELETE_TIMING_TAG );
+ }
+ else
+ {
+ tagmenu->add( "Insert...", IDC_INSERT_TIMING_TAG );
+ }
+
+ bool bMouseOverSelection = IsMouseOverSelection( (short)event->x, (short)event->y );
+
+ if ( bMouseOverSelection || HasCopiedColumn() )
+ {
+ mxPopupMenu *selectionMenu = new mxPopupMenu();
+
+ if ( bMouseOverSelection )
+ {
+ selectionMenu->add( "Copy samples", IDC_ET_SELECTION_COPY );
+ }
+
+ if ( HasCopiedColumn() )
+ {
+ selectionMenu->add( "Paste samples", IDC_ET_SELECTION_PASTE );
+ }
+
+ if ( bMouseOverSelection )
+ {
+ selectionMenu->addSeparator();
+ selectionMenu->add( "Delete samples", IDC_ET_SELECTION_DELETE );
+ selectionMenu->add( "Delete samples and shift remainder", IDC_ET_SELECTION_EXCISE );
+ }
+ pop->addMenu( "Column", selectionMenu );
+ }
+
+
+
+ pop->addMenu( "Timing Tags", tagmenu );
+
+ if ( FacePoser_IsSnapping() )
+ {
+ pop->addSeparator();
+ pop->add( "Delete keys by frame", IDC_TL_DELETECOLUMN );
+ pop->addSeparator();
+ }
+
+ mxPopupMenu *flexmenu = new mxPopupMenu();
+
+ flexmenu->add( "Copy to sliders", IDC_COPY_TO_FLEX );
+ flexmenu->add( "Copy from sliders", IDC_COPY_FROM_FLEX );
+ pop->addMenu( "Flex", flexmenu );
+
+ pop->add( "Create expression...", IDC_NEW_EXPRESSION_FROM_FLEXANIMATION );
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ mxPopupMenu *importexport = new mxPopupMenu();
+
+ importexport->add( "Export flex animation...", IDC_EXPORT_FA );
+ importexport->add( "Import flex animation...", IDC_IMPORT_FA );
+
+ pop->addMenu( "Import/Export", importexport );
+ }
+
+ pop->add( va( "Change scale..." ), IDC_FLEX_CHANGESCALE );
+
+ mxPopupMenu *sortmenu = new mxPopupMenu();
+ sortmenu->add( "Sort by name", IDC_ET_SORT_BY_NAME );
+ sortmenu->add( "Sort by used", IDC_ET_SORT_BY_USED );
+
+ pop->addMenu( "Sort", sortmenu );
+
+ pop->popup( this, (short)event->x, (short)event->y );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::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 );
+}
+
+void ExpressionTool::SetClickedPos( int x, int y )
+{
+ m_nClickedX = x;
+ m_nClickedY = y;
+}
+
+float ExpressionTool::GetTimeForClickedPos( void )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return 0.0f;
+
+ float t = GetTimeValueForMouse( m_nClickedX );
+
+ // Get spline intensity for controller
+ float faketime = e->GetStartTime() + t;
+ return faketime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dragtype -
+// startx -
+// cursor -
+//-----------------------------------------------------------------------------
+void ExpressionTool::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
+{
+ m_nDragType = dragtype;
+ m_nStartX = startx;
+ m_nLastX = startx;
+ m_nStartY = starty;
+ m_nLastY = starty;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ m_hPrevCursor = SetCursor( cursor );
+
+ m_FocusRects.Purge();
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ RECT rcStart;
+ rcStart.left = startx;
+ rcStart.right = startx;
+
+ bool addrect = true;
+ switch ( dragtype )
+ {
+ default:
+ case DRAGTYPE_SCRUBBER:
+ {
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, true );
+
+ rcStart = rcScrub;
+ rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcStart.right = rcStart.left;
+ rcStart.top = rcScrub.bottom;
+
+ rcStart.bottom = h2();
+ }
+ break;
+ case DRAGTYPE_FLEXTIMINGTAG:
+ {
+ rcStart.top = rc.top;
+ rcStart.bottom = h2();
+ }
+ break;
+ case DRAGTYPE_SELECTSAMPLES:
+ {
+ float st = GetTimeValueForMouse( startx );
+ rcStart.left = GetPixelForTimeValue( st );
+ rcStart.right = rcStart.left;
+
+ m_nStartX = rcStart.left;
+ m_nLastX = rcStart.left;
+ }
+ case DRAGTYPE_MOVESELECTIONSTART:
+ case DRAGTYPE_MOVESELECTIONEND:
+ {
+ rcStart.top = rc.top;
+ rcStart.bottom = rc.bottom;
+ }
+ break;
+ case DRAGTYPE_MOVESELECTION:
+ {
+ rcStart.top = rc.top;
+ rcStart.bottom = rc.bottom;
+
+ // Compute left/right pixels for selection
+ rcStart.left = GetPixelForTimeValue( m_flSelection[ 0 ] );
+ rcStart.right = GetPixelForTimeValue( m_flSelection[ 1 ] );
+ }
+ break;
+ }
+
+
+ if ( addrect )
+ {
+ AddFocusRect( rcStart );
+ }
+
+ DrawFocusRect();
+}
+
+void ExpressionTool::OnMouseMove( mxEvent *event )
+{
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ event->x = (short)mx;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ {
+ OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ), 0 );
+ }
+ break;
+ case DRAGTYPE_SELECTSAMPLES:
+ {
+ float st = GetTimeValueForMouse( mx );
+ int snapx = GetPixelForTimeValue( st );
+ f->m_rcFocus.left = min( snapx, m_nStartX );
+ f->m_rcFocus.right = max( snapx, m_nStartY );
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &f->m_rcFocus, offset.x, 0 );
+
+ }
+ break;
+ }
+ }
+
+ DrawFocusRect();
+ }
+ else
+ {
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverTag( mx, my ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
+ {
+ if ( IsMouseOverSelectionStartEdge( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelectionEndEdge( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ }
+ }
+ }
+
+ switch ( m_nDragType )
+ {
+ default:
+ break;
+ case DRAGTYPE_FLEXTIMINGTAG:
+ {
+ ApplyBounds( mx, my );
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( mx );
+ t += m_flScrubberTimeOffset;
+ ForceScrubPosition( t );
+ }
+ }
+ break;
+ }
+
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+}
+
+int ExpressionTool::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ int w, h;
+ w = event->width;
+ h = event->height;
+
+ m_pWorkspace->setBounds( 5, TRAY_HEIGHT + GetCaptionHeight(), w - 10, h - ( TRAY_HEIGHT + 5 + GetCaptionHeight() ) - m_nScrollbarHeight );
+
+ m_nLastHPixelsNeeded = 0;
+ InvalidateLayout();
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int tz = g_pChoreoView->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 );
+ }
+
+ g_pChoreoView->SetPreservedTimeZoom( this, tz );
+ }
+ //RepositionHSlider();
+ m_pWorkspace->redraw();
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+// bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+ iret = 1;
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetClickedPos( mx, my );
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowContextMenu( event, false );
+ return iret;
+ }
+
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ if ( w2() > 0 )
+ {
+ 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;
+ ForceScrubPosition( t );
+ }
+
+ StartDragging( DRAGTYPE_SCRUBBER, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverTag( m_nClickedX, m_nClickedY ) )
+ {
+ StartDragging( DRAGTYPE_FLEXTIMINGTAG, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverPoints( m_nClickedX, m_nClickedY ) )
+ {
+ if ( !m_bSelectionActive )
+ {
+ StartDragging( DRAGTYPE_SELECTSAMPLES, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ // Either move, move edge if ctrl key is held, or deselect
+ if ( IsMouseOverSelection( m_nClickedX,m_nClickedY ) )
+ {
+ if ( IsMouseOverSelectionStartEdge( event ) )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTIONSTART, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelectionEndEdge( event ) )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTIONEND, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ if ( shiftdown )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTION, m_nClickedX, m_nClickedY, LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ }
+ }
+ else
+ {
+ m_bSelectionActive = false;
+ redraw();
+ return iret;
+ }
+ }
+ }
+ else
+ {
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+
+ SetScrubTargetTime( t );
+ }
+ }
+
+ CalcBounds( m_nDragType );
+ }
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ OnMouseMove( event );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ return 1;
+ }
+
+ int mx = (short)event->x;
+ int my = (short)event->y;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect();
+ }
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_NONE:
+ break;
+ case DRAGTYPE_SELECTSAMPLES:
+ FinishSelect( m_nStartX, mx );
+ break;
+ case DRAGTYPE_MOVESELECTION:
+ FinishMoveSelection( m_nStartX, mx );
+ break;
+ case DRAGTYPE_MOVESELECTIONSTART:
+ FinishMoveSelectionStart( m_nStartX, mx );
+ break;
+ case DRAGTYPE_MOVESELECTIONEND:
+ FinishMoveSelectionEnd( m_nStartX, mx );
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ ApplyBounds( mx, my );
+
+// int dx = mx - m_nStartX;
+// int dy = my = m_nStartY;
+
+ if ( w2() > 0 )
+ {
+ float t = GetTimeValueForMouse( (short)event->x );
+ t += m_flScrubberTimeOffset;
+ m_flScrubberTimeOffset = 0.0f;
+ ForceScrubPosition( t );
+ }
+ }
+ break;
+ case DRAGTYPE_FLEXTIMINGTAG:
+ {
+ ApplyBounds( mx, my );
+
+// int dx = mx - m_nStartX;
+// int dy = my = m_nStartY;
+
+ // Compute dx, dy and apply to sections
+ //Con_Printf( "dx == %i\n", dx );
+ CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( tag && g_pChoreoView && ev && ev->GetDuration() )
+ {
+ float t = GetTimeValueForMouse( mx );
+
+ float percent = t / ev->GetDuration();
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Move Timing Tag" );
+
+ if ( tag->GetLocked() )
+ {
+ // Resample all control points on right/left
+ // of locked tags all the way to the next lock or edge
+ ResampleControlPoints( tag, percent );
+ }
+
+ tag->SetPercentage( percent );
+
+ g_pChoreoView->PushRedo( "Move Timing Tag" );
+ }
+
+ LayoutItems( true );
+ redraw();
+ }
+ break;
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+
+ SetMouseOverPos( mx, my );
+ DrawMouseOverPos();
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_ET_RESET_ITEM_SIZE:
+ OnResetItemSize();
+ break;
+ case IDC_ET_RESET_ALL_ITEM_SIZES:
+ OnResetAllItemSizes();
+ break;
+ case IDC_ET_SELECTION_DELETE:
+ OnDeleteSelection( false );
+ break;
+ case IDC_ET_SELECTION_EXCISE:
+ OnDeleteSelection( true );
+ break;
+ case IDC_ET_SELECTION_COPY:
+ OnCopyColumn();
+ break;
+ case IDC_ET_SELECTION_PASTE:
+ OnPasteColumn();
+ break;
+ case IDC_ET_SORT_BY_USED:
+ OnSortByUsed();
+ break;
+ case IDC_ET_SORT_BY_NAME:
+ OnSortByName();
+ break;
+ case IDC_EXPORT_FA:
+ OnExportFlexAnimation();
+ break;
+ case IDC_IMPORT_FA:
+ OnImportFlexAnimation();
+ break;
+ case IDC_LOCK_TIMING_TAG:
+ LockTimingTag();
+ break;
+ case IDC_UNLOCK_TIMING_TAG:
+ UnlockTimingTag();
+ break;
+ case IDC_DELETE_TIMING_TAG:
+ DeleteFlexTimingTag( m_nClickedX, m_nClickedY );
+ break;
+ case IDC_INSERT_TIMING_TAG:
+ AddFlexTimingTag( m_nClickedX );
+ break;
+ case IDC_EXPANDALL:
+ m_pWorkspace->ExpandAll();
+ break;
+ case IDC_COLLAPSEALL:
+ m_pWorkspace->CollapseAll( NULL );
+ break;
+ case IDC_COLLAPSE_ALL_EXCEPT:
+ m_pWorkspace->CollapseAll( m_pWorkspace->GetClickedItem() );
+ break;
+ case IDC_EXPANDVALID:
+ m_pWorkspace->ExpandValid();
+ break;
+ case IDC_COPY_TO_FLEX:
+ OnCopyToFlex( true );
+ break;
+ case IDC_COPY_FROM_FLEX:
+ OnCopyFromFlex( false );
+ break;
+ case IDC_NEW_EXPRESSION_FROM_FLEXANIMATION:
+ OnNewExpression();
+ break;
+ case IDC_UNDO_FA:
+ OnUndo();
+ break;
+ case IDC_REDO_FA:
+ OnRedo();
+ break;
+ case IDC_TL_EDITNORMAL:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->SetEditType( 0 );
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_EDITLEFTRIGHT:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->SetEditType( 1 );
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_EXPAND:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->SetCollapsed( false );
+ }
+ LayoutItems();
+ }
+ break;
+ case IDC_TL_COLLAPSE:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->SetCollapsed( true );
+ }
+ LayoutItems();
+ }
+ break;
+ case IDC_TL_ENABLE:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Enable item" );
+
+ item->SetActive( true );
+
+ g_pChoreoView->PushRedo( "Enable item" );
+
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_DISABLE:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Disable item" );
+
+ item->SetActive( false );
+
+ g_pChoreoView->PushRedo( "Disable item" );
+
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_COPY:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->Copy();
+ }
+ }
+ break;
+ case IDC_TL_PASTE:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->Paste();
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_DELETE:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->Delete();
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_DESELECT:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->DeselectAll();
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_SELECTALL:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ item->SelectAll();
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_DISABLE_ALL_EXCEPT:
+ {
+ m_pWorkspace->DisableAllExcept();
+ }
+ break;
+ case IDC_ENABLE_ALL_VALID:
+ {
+ m_pWorkspace->EnableValid();
+ }
+ break;
+ case IDC_TL_SNAPSELECTED:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Snap Selected" );
+
+ item->SnapSelected();
+
+ g_pChoreoView->PushRedo( "Snap Selected" );
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_SNAPPOINTS:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Snap Item" );
+
+ item->SnapAll();
+
+ g_pChoreoView->PushRedo( "Snap Item" );
+ item->DrawSelf();
+ }
+ }
+ break;
+ case IDC_TL_DELETECOLUMN:
+ {
+ m_pWorkspace->OnDeleteColumn();
+ }
+ break;
+ case IDC_TL_SNAPALL:
+ {
+ m_pWorkspace->OnSnapAll();
+ }
+ break;
+ case IDC_FLEXHSCROLL:
+ {
+ 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_FLEX_CHANGESCALE:
+ {
+ OnChangeScale();
+ }
+ break;
+ case IDC_FLEX_SCALESAMPLES:
+ {
+ OnScaleSamples();
+ }
+ break;
+ case IDC_ET_EDGEPROPERTIES:
+ {
+ OnEdgeProperties();
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::KeyDown:
+ case mxEvent::KeyUp:
+ {
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( item )
+ {
+ iret = item->handleEvent( event );
+ }
+
+ if ( !iret )
+ {
+ switch ( event->key )
+ {
+ default:
+ break;
+ case VK_ESCAPE:
+ {
+ DeselectAll();
+ iret = 1;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return iret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : false -
+//-----------------------------------------------------------------------------
+void ExpressionTool::LayoutItems( bool force /*= false*/ )
+{
+ m_pWorkspace->LayoutItems( force );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void ExpressionTool::AddFlexTimingTag( int mx )
+{
+ Assert( g_pChoreoView );
+
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ if ( event->GetType() != CChoreoEvent::FLEXANIMATION )
+ {
+ Con_ErrorPrintf( "Timing Tag: Can only tag FLEXANIMATION events\n" );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Event Tag Name" );
+ strcpy( params.m_szPrompt, "Name:" );
+
+ strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Timing Tag Name: No name entered!\n" );
+ return;
+ }
+
+ // Convert click to frac
+ float t = GetTimeValueForMouse( mx );
+ float frac = 0.0f;
+ if ( event->GetDuration() )
+ {
+ frac = t / event->GetDuration();
+ frac = clamp( frac, 0.0f, 1.0f );
+ }
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Add Timing Tag" );
+
+ event->AddTimingTag( params.m_szInputText, frac, true );
+
+ g_pChoreoView->PushRedo( "Add Timing Tag" );
+
+ // Redraw this window
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+void ExpressionTool::DeleteFlexTimingTag( int mx, int my )
+{
+ Assert( g_pChoreoView );
+
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ CFlexTimingTag *tag = IsMouseOverTag( mx, my );
+ if ( !tag )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Delete Timing Tag" );
+
+ event->RemoveTimingTag( tag->GetName() );
+
+ g_pChoreoView->PushRedo( "Delete Timing Tag" );
+
+ LayoutItems( true );
+ // Redraw this window
+ redraw();
+
+}
+
+void ExpressionTool::LockTimingTag( void )
+{
+ Assert( g_pChoreoView );
+
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
+ if ( !tag )
+ return;
+
+ if ( tag->GetLocked() )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Lock Timing Tag" );
+
+ tag->SetLocked( true );
+
+ g_pChoreoView->PushRedo( "Lock Timing Tag" );
+
+ redraw();
+}
+
+void ExpressionTool::UnlockTimingTag( void )
+{
+ Assert( g_pChoreoView );
+
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ CFlexTimingTag *tag = IsMouseOverTag( m_nClickedX, m_nClickedY );
+ if ( !tag )
+ return;
+
+ if ( !tag->GetLocked() )
+ return;
+
+ g_pChoreoView->SetDirty( true );
+
+ g_pChoreoView->PushUndo( "Unlock Timing Tag" );
+
+ tag->SetLocked( false );
+
+ g_pChoreoView->PushRedo( "Unlock Timing Tag" );
+
+ redraw();
+}
+
+void ExpressionTool::ApplyBounds( int& mx, int& my )
+{
+ if ( !m_bUseBounds )
+ return;
+
+ mx = clamp( mx, m_nMinX, m_nMaxX );
+}
+
+void ExpressionTool::CalcBounds( int movetype )
+{
+ switch ( movetype )
+ {
+ default:
+ case DRAGTYPE_NONE:
+ m_bUseBounds = false;
+ m_nMinX = 0;
+ m_nMaxX = 0;
+ break;
+ case DRAGTYPE_SCRUBBER:
+ m_bUseBounds = true;
+ m_nMinX = 0;
+ m_nMaxX = w2();
+ break;
+ case DRAGTYPE_FLEXTIMINGTAG:
+ {
+ m_bUseBounds = true;
+
+ int left, right;
+ GetWorkspaceLeftRight( left, right );
+
+ m_nMinX = left;
+ m_nMaxX = right;
+
+ RECT rcClient;
+ rcClient.left = left;
+ rcClient.right = right;
+ rcClient.top = 0;
+ rcClient.bottom = TRAY_HEIGHT;
+
+ CFlexTimingTag *tag = IsMouseOverTag( m_nStartX, m_nStartY );
+ if ( tag &&
+ tag->GetOwner() )
+ {
+ CChoreoEvent *e = tag->GetOwner();
+
+ float st = e->GetStartTime();
+ float ed = e->GetEndTime();
+
+ if ( ed > st )
+ {
+
+
+ // Find previous tag, if any
+ CFlexTimingTag *prev = NULL;
+ CFlexTimingTag *next = NULL;
+
+ for ( int i = 0; i < e->GetNumTimingTags(); i++ )
+ {
+ CFlexTimingTag *test = e->GetTimingTag( i );
+ if ( test != tag )
+ continue;
+
+ // Found it
+ if ( i > 0 )
+ {
+ prev = e->GetTimingTag( i - 1 );
+ }
+
+ if ( i + 1 < e->GetNumTimingTags() )
+ {
+ next = e->GetTimingTag( i + 1 );
+ }
+ break;
+ }
+
+ if ( prev )
+ {
+ // Compute x pixel of prev tag
+ float frac = ( prev->GetStartTime() - st ) / ( ed - st );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
+
+ m_nMinX = max( m_nMinX, tagx + 5 );
+ }
+ }
+
+ if ( next )
+ {
+ // Compute x pixel of next tag
+ float frac = ( next->GetStartTime() - st ) / ( ed - st );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ int tagx = rcClient.left + (int)( frac * (float)( rcClient.right - rcClient.left ) );
+ m_nMaxX = min( m_nMaxX, tagx - 5 );
+ }
+ }
+ }
+
+ }
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *tag -
+// newposition -
+//-----------------------------------------------------------------------------
+void ExpressionTool::ResampleControlPoints( CFlexTimingTag *tag, float newposition )
+{
+ CChoreoEvent *e = tag->GetOwner();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+
+ float leftedge = 0.0f;
+ float rightedge = duration;
+
+ // Find neighboring locked tags, if any
+ CFlexTimingTag *prev = NULL;
+ CFlexTimingTag *next = NULL;
+
+ int i;
+ for ( i = 0; i < e->GetNumTimingTags(); i++ )
+ {
+ CFlexTimingTag *test = e->GetTimingTag( i );
+ if ( test != tag )
+ continue;
+
+ // Found it
+ if ( i > 0 )
+ {
+ int i1 = i - 1;
+ while ( 1 )
+ {
+ if ( i1 < 0 )
+ {
+ prev = NULL;
+ break;
+ }
+
+ prev = e->GetTimingTag( i1 );
+ if ( prev->GetLocked() )
+ break;
+
+ i1--;
+ }
+ }
+
+ if ( i + 1 < e->GetNumTimingTags() )
+ {
+ int i1 = i + 1;
+ while ( 1 )
+ {
+ if ( i1 >= e->GetNumTimingTags() )
+ {
+ next = NULL;
+ break;
+ }
+
+ next = e->GetTimingTag( i1 );
+ if ( next->GetLocked() )
+ break;
+
+ i1++;
+ }
+ }
+ break;
+ }
+
+ if ( prev )
+ {
+ leftedge = prev->GetPercentage() * duration;
+ }
+
+ if ( next )
+ {
+ rightedge = next->GetPercentage() * duration;
+ }
+
+ // Now, using the tags old position as a pivot, rescale intervening
+ // sample points based on size delta of new vs old range
+ float oldpivot = tag->GetPercentage() * duration;
+ float newpivot = newposition * duration;
+
+ float oldleftrange = oldpivot - leftedge;
+ float oldrightrange = rightedge - oldpivot;
+
+ float newleftrange = newpivot - leftedge;
+ float newrightrange = rightedge - newpivot;
+
+ if ( oldleftrange <= 0.0f ||
+ oldrightrange <= 0.0f ||
+ newleftrange <= 0.0f ||
+ newrightrange <= 0.0f )
+ {
+ Con_Printf( "Range problem!!! avoiding division by zero\n" );
+ return;
+ }
+
+ for ( i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ for ( int t = 0; t < ( track->IsComboType() ? 2 : 1 ); t++ )
+ {
+ for ( int j = 0; j < track->GetNumSamples( t ); j++ )
+ {
+ CExpressionSample *s = track->GetSample( j, t );
+ if ( !s )
+ continue;
+
+ float oldtime = s->time;
+
+ // In old range?
+ if ( oldtime < leftedge )
+ continue;
+ if ( oldtime > rightedge )
+ continue;
+
+ // In left or right side( tiebreak toward left )
+ float newtime = oldtime;
+
+ if ( oldtime <= oldpivot )
+ {
+ float n = ( oldtime - leftedge ) / oldleftrange;
+ newtime = leftedge + n * newleftrange;
+ }
+ else
+ {
+ float n = ( oldtime - oldpivot ) / oldrightrange;
+ newtime = newpivot + n * newrightrange;
+ }
+
+ //newtime = FacePoser_SnapTime( newtime );
+
+ s->time = newtime;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::OnNewExpression( void )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load a model first!\n" );
+ return;
+ }
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ {
+ Con_ErrorPrintf( "ExpressionTool::OnNewExpression: Can't create new face pose, must load an expression file first!\n" );
+ return;
+ }
+
+ g_pExpressionTrayTool->Deselect();
+
+ float t = GetTimeValueForMouse( m_nClickedX );
+
+ // Get spline intensity for controller
+ float faketime = e->GetStartTime() + t;
+
+ float settings[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float weights[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ memset( settings, 0, sizeof( settings ) );
+ memset( weights, 0, sizeof( settings ) );
+
+ for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ // Disabled
+ if ( !track->IsTrackActive() )
+ 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( faketime, side );
+
+ settings[ controller ] = flIntensity;
+ weights[ controller ] = 1.0f;
+ }
+ }
+ }
+ else
+ {
+ int controller = track->GetFlexControllerIndex( 0 );
+ if ( controller != -1 )
+ {
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( faketime, 0 );
+
+ settings[ controller ] = flIntensity;
+ weights[ controller ] = 1.0f;
+ }
+ }
+ }
+
+ CExpressionParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Add Expression" );
+ strcpy( params.m_szName, "" );
+ strcpy( params.m_szDescription, "" );
+
+ if ( !ExpressionProperties( &params ) )
+ return;
+
+ if ( ( strlen( params.m_szName ) <= 0 ) ||
+ !stricmp( params.m_szName, "unnamed" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid name\n" );
+ return;
+ }
+
+ if ( ( strlen( params.m_szDescription ) <= 0 ) ||
+ !stricmp( params.m_szDescription, "description" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid description\n" );
+ return;
+ }
+
+ active->AddExpression( params.m_szName, params.m_szDescription, settings, weights, true, true );
+}
+
+LocalFlexController_t FindFlexControllerIndexByName( StudioModel *model, char const *searchname )
+{
+ if ( !model )
+ return LocalFlexController_t(-1);
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return LocalFlexController_t(-1);
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
+ {
+ char const *name = hdr->pFlexcontroller( i )->pszName();
+ if ( !name )
+ continue;
+
+ if ( strcmp( name, searchname ) )
+ continue;
+
+ return i;
+ }
+ return LocalFlexController_t(-1);
+}
+
+void ExpressionTool::OnCopyToFlex( bool isEdited )
+{
+ // local time in the expression tool for the last mouse click
+ float t = GetTimeValueForMouse( m_nClickedX );
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float scenetime = e->GetStartTime() + t;
+
+ OnCopyToFlex( scenetime, isEdited );
+
+ return;
+}
+
+
+void ExpressionTool::OnCopyToFlex( float scenetime, bool isEdited )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
+ return;
+
+ bool needundo = false;
+
+ float *settings = NULL;
+ float *weights = NULL;
+ CExpression *exp = NULL;
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+
+ int index = active->GetSelectedExpression();
+ if ( index != -1 )
+ {
+ exp = active->GetExpression( index );
+ if ( exp )
+ {
+ needundo = true;
+ settings = exp->GetSettings();
+ weights = exp->GetWeights();
+ }
+ }
+ }
+
+ if ( needundo && exp )
+ {
+ exp->PushUndoInformation();
+ active->SetDirty( true );
+ }
+
+ g_pFlexPanel->ResetSliders( false, true );
+
+ StudioModel *model = models->GetActiveStudioModel();
+
+ for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ // Disabled
+ if ( !track->IsTrackActive() )
+ continue;
+
+ // Map track flex controller to global name
+ for ( int side = 0; side < 1 + track->IsComboType(); side++ )
+ {
+ int controller = track->GetFlexControllerIndex( side );
+ if ( controller != -1 )
+ {
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scenetime, side );
+
+ g_pFlexPanel->SetSlider( controller, flIntensity );
+ g_pFlexPanel->SetInfluence( controller, 1.0f );
+ g_pFlexPanel->SetEdited( controller, isEdited );
+ if( model )
+ {
+ LocalFlexController_t raw = track->GetRawFlexControllerIndex( side );
+ if ( raw != LocalFlexController_t(-1) )
+ {
+ model->SetFlexController( raw, flIntensity );
+ }
+ }
+ if ( settings && weights )
+ {
+ settings[ controller ] = flIntensity;
+ weights[ controller ] = 1.0f;
+ }
+ }
+ }
+ }
+
+ if ( needundo && exp )
+ {
+ exp->PushRedoInformation();
+ }
+}
+
+void ExpressionTool::OnCopyFromFlex( bool isEdited )
+{
+ // local time in the expression tool for the last mouse click
+ float t = GetTimeValueForMouse( m_nClickedX );
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float scenetime = e->GetStartTime() + t;
+
+ OnCopyFromFlex( scenetime, isEdited );
+
+ return;
+}
+
+void ExpressionTool::OnSetSingleKeyFromFlex( char const *sliderName )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e || !e->GetDuration() )
+ return;
+
+ float scenetime = g_pChoreoView->GetScene()->GetTime();
+
+ if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
+ return;
+
+ scenetime = FacePoser_SnapTime( scenetime );
+
+ float relativetime = scenetime - e->GetStartTime();
+
+ // Get spline intensity for controller
+
+ float setting;
+ float influence;
+ float minvalue, maxvalue;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Set Single Key" );
+
+ for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
+ {
+ if ( !g_pFlexPanel->IsValidSlider( j ) )
+ continue;
+
+ if ( Q_stricmp( g_pFlexPanel->getLabel(), sliderName ) )
+ continue;
+
+ setting = g_pFlexPanel->GetSliderRawValue( j );
+ influence = g_pFlexPanel->GetInfluence( j );
+
+ // g_pFlexPanel->SetEdited( j, isEdited );
+
+ g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
+
+ bool found = false;
+ for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ for ( int side = 0; side < 1 + track->IsComboType(); side++ )
+ {
+ if ( track->GetFlexControllerIndex( side ) != j )
+ continue;
+
+ float normalized = setting;
+ if ( side == 0 )
+ {
+ if ( minvalue != maxvalue )
+ {
+ normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
+ }
+ if (track->IsInverted())
+ {
+ normalized = 1.0 - normalized;
+ }
+ }
+
+ found = true;
+
+ int nSampleCount = track->GetNumSamples( side );
+
+ int j = 0;
+ for ( ; j < nSampleCount; ++j )
+ {
+ CExpressionSample *s = track->GetSample( j, side );
+ if ( s->time == relativetime )
+ break;
+ }
+
+ if ( j >= nSampleCount )
+ {
+ track->AddSample( relativetime, normalized, side );
+ track->Resort( side );
+ }
+ else
+ {
+ CExpressionSample *s = track->GetSample( j, side );
+ s->value = normalized;
+ }
+
+ track->SetTrackActive( true );
+
+ break;
+ }
+ }
+ }
+
+ g_pChoreoView->PushRedo( "Set Single Key" );
+
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+void ExpressionTool::OnCopyFromFlex( float scenetime, bool isEdited )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e || !e->GetDuration() )
+ return;
+
+ if ( scenetime < e->GetStartTime() || scenetime > e->GetEndTime() )
+ return;
+
+ scenetime = FacePoser_SnapTime( scenetime );
+
+ float relativetime = scenetime - e->GetStartTime();
+
+ // Get spline intensity for controller
+
+ float setting;
+ float influence;
+ float minvalue, maxvalue;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Copy from Flex" );
+
+ for (int j = 0; j < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; j++)
+ {
+ if ( !g_pFlexPanel->IsValidSlider( j ) )
+ continue;
+
+ setting = g_pFlexPanel->GetSliderRawValue( j );
+ //setting = g_pFlexPanel->GetSlider( j );
+ influence = g_pFlexPanel->GetInfluence( j );
+
+ g_pFlexPanel->SetEdited( j, isEdited );
+
+ g_pFlexPanel->GetSliderRange( j, minvalue, maxvalue );
+
+ // Found it
+ if ( !influence )
+ {
+ continue;
+ }
+
+ bool found = false;
+ for ( int i = 0 ; i < e->GetNumFlexAnimationTracks() && !found; i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ for ( int side = 0; side < 1 + track->IsComboType(); side++ )
+ {
+ if ( track->GetFlexControllerIndex( side ) != j )
+ continue;
+
+ float normalized = setting;
+ if ( side == 0 )
+ {
+ if ( minvalue != maxvalue )
+ {
+ normalized = ( setting - minvalue ) / ( maxvalue - minvalue );
+ }
+ if (track->IsInverted())
+ {
+ normalized = 1.0 - normalized;
+ }
+ }
+
+ found = true;
+
+ track->AddSample( relativetime, normalized, side );
+ track->Resort( side );
+ track->SetTrackActive( true );
+
+ break;
+ }
+ }
+ }
+
+ g_pChoreoView->PushRedo( "Copy from Flex" );
+
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+bool ExpressionTool::SetFlexAnimationTrackFromExpression( int mx, int my, CExpClass *cl, CExpression *exp )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e | !e->GetDuration() )
+ {
+ 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;
+ }
+
+ float t = GetTimeValueForMouse( (short)pt.x );
+
+ // Get spline intensity for controller
+ // Get spline intensity for controller
+ float relativetime = t;
+ float faketime = e->GetStartTime() + relativetime;
+
+ faketime = FacePoser_SnapTime( faketime );
+
+ float *settings = exp->GetSettings();
+ float *influence = exp->GetWeights();
+
+ if ( !settings || !influence )
+ return false;
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Copy from Expression" );
+
+ for ( int i = 0 ; i < e->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ if ( track->IsComboType() )
+ {
+ int left = track->GetFlexControllerIndex( 0 );
+ int right = track->GetFlexControllerIndex( 1 );
+
+ float leftval = settings[ left ];
+ float leftinfluence = influence[ left ];
+ float rightval = settings[ right ];
+ float rightinfluence = influence[ right ];
+
+ if ( leftinfluence || rightinfluence )
+ {
+
+ //Con_Printf( "%s %i(side %i): amount %f inf %f\n", track->GetFlexControllerName(), j, side, s, inf );
+
+ float mag, leftright;
+
+ if (leftval < rightval)
+ {
+ mag = rightval;
+ leftright = 1.0 - (leftval / rightval) * 0.5;
+ }
+ else if (leftval > rightval)
+ {
+ mag = leftval;
+ leftright = (rightval / leftval) * 0.5;
+ }
+ else
+ {
+ mag = leftval;
+ leftright = 0.5;
+ }
+
+ track->AddSample( relativetime, mag * leftinfluence, 0 );
+ track->AddSample( relativetime, leftright, 1 );
+
+ track->Resort( 0 );
+ track->Resort( 1 );
+
+ track->SetTrackActive( true );
+ }
+ }
+ else
+ {
+ int j = track->GetFlexControllerIndex( 0 );
+
+ float s = settings[ j ];
+ float inf = influence[ j ];
+
+ if ( inf )
+ {
+ track->AddSample( relativetime, s, 0 );
+
+ track->Resort( 0 );
+
+ track->SetTrackActive( true );
+ }
+ }
+ }
+
+ g_pChoreoView->PushRedo( "Copy from Expression" );
+
+ m_pWorkspace->redraw();
+ redraw();
+
+ return true;
+}
+
+bool ExpressionTool::PaintBackground()
+{
+ redraw();
+ return false;
+}
+
+void ExpressionTool::OnExportFlexAnimation( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ // Create flexanimations dir
+ CreatePath( "flexanimations/foo" );
+
+ char fafilename[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
+ {
+ return;
+ }
+
+ Q_DefaultExtension( fafilename, ".vfa", sizeof( fafilename ) );
+
+ Con_Printf( "Exporting events to %s\n", fafilename );
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ CChoreoScene::FileSaveFlexAnimations( buf, 0, event );
+
+ // Write it out baby
+ FileHandle_t fh = filesystem->Open( fafilename, "wt" );
+ if (fh)
+ {
+ filesystem->Write( buf.Base(), buf.TellPut(), fh );
+ filesystem->Close(fh);
+ }
+ else
+ {
+ Con_Printf( "Unable to write file %s!!!\n", fafilename );
+ }
+}
+
+void ExpressionTool::OnImportFlexAnimation( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return;
+
+ char fafilename[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( fafilename, sizeof( fafilename ), "flexanimations", "*.vfa" ) )
+ {
+ return;
+ }
+
+ if ( !filesystem->FileExists( fafilename ) )
+ return;
+
+ char fullpath[ 512 ];
+ filesystem->RelativePathToFullPath( fafilename, "MOD", fullpath, sizeof( fullpath ) );
+
+ LoadScriptFile( (char *)fullpath );
+
+ tokenprocessor->GetToken( true );
+ if ( stricmp( tokenprocessor->CurrentToken(), "flexanimations" ) )
+ {
+ Con_Printf( "ExpressionTool::OnImportFlexAnimation: %s, expecting \"flexanimations\"\n",
+ fullpath );
+ }
+ else
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Import flex animations" );
+
+ CChoreoScene::ParseFlexAnimations( tokenprocessor, event, true );
+
+ // Force a full reset
+ m_pLastEvent = NULL;
+ SetEvent( event );
+
+ g_pChoreoView->PushRedo( "Import flex animations" );
+
+ Con_Printf( "Parsed flex animations from %s\n", fullpath );
+ }
+}
+
+void ExpressionTool::OnUndo( void )
+{
+ g_pChoreoView->Undo();
+}
+
+void ExpressionTool::OnRedo( void )
+{
+ g_pChoreoView->Redo();
+}
+
+void ExpressionTool::ForceScrubPositionFromSceneTime( float scenetime )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e || !e->GetDuration() )
+ return;
+
+ float t = scenetime - e->GetStartTime();
+ m_flScrub = t;
+ m_flScrubTarget = t;
+
+ DrawScrubHandles();
+}
+
+void ExpressionTool::ForceScrubPosition( float frac )
+{
+ m_flScrub = frac;
+ m_flScrubTarget = frac;
+
+ CChoreoEvent *e = GetSafeEvent();
+ if ( e )
+ {
+ float realtime = e->GetStartTime() + frac;
+
+ g_pChoreoView->SetScrubTime( realtime );
+ g_pChoreoView->SetScrubTargetTime( realtime );
+
+ g_pChoreoView->DrawScrubHandle();
+ }
+
+ DrawScrubHandles();
+}
+
+void ExpressionTool::DrawScrubHandles()
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+
+ RECT rcTray = rcHandle;
+ rcTray.left = 0;
+ rcTray.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcTray );
+ DrawScrubHandle( drawHelper, rcHandle );
+}
+
+void ExpressionTool::SetMouseOverPos( int x, int y )
+{
+ m_nMousePos[ 0 ] = x;
+ m_nMousePos[ 1 ] = y;
+}
+
+void ExpressionTool::GetMouseOverPos( int &x, int& y )
+{
+ x = m_nMousePos[ 0 ];
+ y = m_nMousePos[ 1 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcPos -
+//-----------------------------------------------------------------------------
+void ExpressionTool::GetMouseOverPosRect( RECT& rcPos )
+{
+ rcPos.top = GetCaptionHeight() + 12;
+ rcPos.left = w2() - 200;
+ rcPos.right = w2() - 5;
+ rcPos.bottom = rcPos.top + 13;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcPos -
+//-----------------------------------------------------------------------------
+void ExpressionTool::DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos )
+{
+ // Compute time for pixel x
+ float t = GetTimeValueForMouse( m_nMousePos[ 0 ] );
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ t += e->GetStartTime();
+
+ float snapped = FacePoser_SnapTime( t );
+
+ // Found it, write out description
+ //
+ char sz[ 128 ];
+ if ( t != snapped )
+ {
+ Q_snprintf( sz, sizeof( sz ), "%s", FacePoser_DescribeSnappedTime( t ) );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), "%.3f", t );
+ }
+
+ int len = drawHelper.CalcTextWidth( "Arial", 11, 900, sz );
+
+ RECT rcText = rcPos;
+ rcText.left = max( rcPos.left, rcPos.right - len );
+
+ drawHelper.DrawColoredText( "Arial", 11, 900, RGB( 255, 50, 70 ), rcText, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::DrawMouseOverPos()
+{
+ RECT rcPos;
+ GetMouseOverPosRect( rcPos );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcPos );
+ DrawMouseOverPos( drawHelper, rcPos );
+}
+
+int ExpressionTool::CountSelectedSamples( void )
+{
+ return m_pWorkspace->CountSelectedSamples();
+}
+
+void ExpressionTool::MoveSelectedSamples( float dfdx, float dfdy, bool snap )
+{
+ m_pWorkspace->MoveSelectedSamples( dfdx, dfdy, snap );
+}
+
+void ExpressionTool::DeleteSelectedSamples( void )
+{
+ m_pWorkspace->DeleteSelectedSamples();
+}
+
+void ExpressionTool::DeselectAll( void )
+{
+ m_pWorkspace->DeselectAll();
+ m_bSelectionActive = false;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : start -
+// end -
+//-----------------------------------------------------------------------------
+void ExpressionTool::SelectPoints( float starttime, float endtime )
+{
+ // Make sure order is correct
+ if ( endtime < starttime )
+ {
+ float temp = endtime;
+ endtime = starttime;
+ starttime = temp;
+ }
+
+ DeselectAll();
+
+ m_flSelection[ 0 ] = starttime;
+ m_flSelection[ 1 ] = endtime;
+ m_bSelectionActive = true;
+
+ // Select any words that span the selection
+ //
+ m_pWorkspace->SelectPoints( starttime, endtime );
+
+ redraw();
+}
+
+void ExpressionTool::FinishMoveSelection( int startx, int mx )
+{
+ float start = GetTimeValueForMouse( startx );
+ float end = GetTimeValueForMouse( mx );
+
+ float delta = end - start;
+
+ for ( int i = 0; i < 2; i++ )
+ {
+ m_flSelection[ i ] += delta;
+ }
+
+ SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
+
+ redraw();
+}
+
+void ExpressionTool::FinishMoveSelectionStart( int startx, int mx )
+{
+ float start = GetTimeValueForMouse( startx );
+ float end = GetTimeValueForMouse( mx );
+
+ float delta = end - start;
+
+ m_flSelection[ 0 ] += delta;
+
+ SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
+
+ redraw();
+}
+
+void ExpressionTool::FinishMoveSelectionEnd( int startx, int mx )
+{
+ float start = GetTimeValueForMouse( startx );
+ float end = GetTimeValueForMouse( mx );
+
+ float delta = end - start;
+
+ m_flSelection[ 1 ] += delta;
+
+ SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : startx -
+// mx -
+//-----------------------------------------------------------------------------
+void ExpressionTool::FinishSelect( int startx, int mx )
+{
+ // Don't select really small areas
+ if ( abs( startx - mx ) < 1 )
+ return;
+
+ float start = GetTimeValueForMouse( startx );
+ float end = GetTimeValueForMouse( mx );
+
+ SelectPoints( start, end );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ExpressionTool::IsMouseOverPoints( int mx, int my )
+{
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Over tag
+ if ( my > TRAY_HEIGHT )
+ return false;
+
+ if ( my <= 12 + GetCaptionHeight() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ExpressionTool::IsMouseOverSelection( int mx, int my )
+{
+ if ( !m_bSelectionActive )
+ return false;
+
+ if ( !IsMouseOverPoints( mx, my ) )
+ return false;
+
+ float t = GetTimeValueForMouse( mx );
+
+ if ( t >= m_flSelection[ 0 ] &&
+ t <= m_flSelection[ 1 ] )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool ExpressionTool::IsMouseOverSelectionStartEdge( mxEvent *event )
+{
+ int mx, my;
+ mx = (short)event->x;
+ my = (short)event->y;
+
+ if ( !(event->modifiers & mxEvent::KeyCtrl ) )
+ return false;
+
+ if ( !IsMouseOverSelection( mx, my ) )
+ return false;
+
+ int left;
+
+ left = GetPixelForTimeValue( m_flSelection[ 0 ] );
+
+ if ( abs( left - mx ) <= 2 )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool ExpressionTool::IsMouseOverSelectionEndEdge( mxEvent *event )
+{
+ int mx, my;
+ mx = (short)event->x;
+ my = (short)event->y;
+
+ if ( !(event->modifiers & mxEvent::KeyCtrl ) )
+ return false;
+
+ if ( !IsMouseOverSelection( mx, my ) )
+ return false;
+
+ int right;
+
+ right = GetPixelForTimeValue( m_flSelection[ 1 ] );
+
+ if ( abs( right - mx ) <= 2 )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &rc -
+//-----------------------------------------------------------------------------
+void ExpressionTool::GetWorkspaceRect( RECT &rc )
+{
+ GetClientRect( (HWND)getHandle(), &rc );
+
+ rc.top = TRAY_HEIGHT - 17;
+ rc.bottom = TRAY_HEIGHT - 1;
+ //InflateRect( &rc, -1, -1 );
+}
+
+void ExpressionTool::AddFocusRect( RECT& rc )
+{
+ RECT rcFocus = rc;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ // Convert to screen space?
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int ExpressionTool::ComputeHPixelsNeeded( void )
+{
+ CChoreoEvent *event = GetSafeEvent();
+ if ( !event )
+ return 0;
+
+ int pixels = 0;
+ float maxtime = event->GetDuration();
+ pixels = (int)( ( maxtime + 5.0 ) * GetPixelsPerSecond() + 10 );
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ if ( pixelsneeded <= w2() )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+ m_pHorzScrollBar->setBounds( 0, h2() - m_nScrollbarHeight, w2(), m_nScrollbarHeight );
+
+ m_flLeftOffset = max( 0.f, m_flLeftOffset );
+ m_flLeftOffset = min( (float)pixelsneeded, m_flLeftOffset );
+
+ m_pHorzScrollBar->setRange( 0, pixelsneeded );
+ m_pHorzScrollBar->setValue( m_flLeftOffset );
+ m_pHorzScrollBar->setPagesize( w2() );
+
+ m_nLastHPixelsNeeded = pixelsneeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float ExpressionTool::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void ExpressionTool::MoveTimeSliderToPos( int x )
+{
+ m_flLeftOffset = x;
+ m_pHorzScrollBar->setValue( m_flLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::InvalidateLayout( void )
+{
+ if ( m_bSuppressLayout )
+ return;
+
+ if ( ComputeHPixelsNeeded() != m_nLastHPixelsNeeded )
+ {
+ RepositionHSlider();
+ }
+
+ m_bLayoutIsValid = false;
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// *clipped -
+// Output : int
+//-----------------------------------------------------------------------------
+int ExpressionTool::GetPixelForTimeValue( float time, bool *clipped /*=NULL*/ )
+{
+ int left, right;
+
+ GetWorkspaceLeftRight( left, right );
+
+ if ( clipped )
+ {
+ *clipped = false;
+ }
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ float frac = ( time - st ) / ( ed - st );
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int pixel = left + ( int )( frac * (right - left ) );
+ return pixel;
+}
+
+void ExpressionTool::OnChangeScale( void )
+{
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ {
+ return;
+ }
+
+ // Zoom time in / out
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change Zoom" );
+ strcpy( params.m_szPrompt, "New scale (e.g., 2.5x):" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ), "%.2f", (float)g_pChoreoView->GetTimeZoom( GetToolName() ) / 100.0f );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ g_pChoreoView->SetTimeZoom( GetToolName(), clamp( (int)( 100.0f * atof( params.m_szInputText ) ), 1, MAX_TIME_ZOOM ), false );
+
+ m_nLastHPixelsNeeded = -1;
+ InvalidateLayout();
+ Con_Printf( "Zoom factor %i %%\n", g_pChoreoView->GetTimeZoom( GetToolName() ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : st -
+// ed -
+//-----------------------------------------------------------------------------
+void ExpressionTool::GetStartAndEndTime( float& st, float& ed )
+{
+ st = m_flLeftOffset / GetPixelsPerSecond();
+ int left, right;
+ GetWorkspaceLeftRight( left, right );
+ if ( right <= left )
+ {
+ ed = st;
+ }
+ else
+ {
+ ed = st + (float)( right - left ) / GetPixelsPerSecond();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : float
+//-----------------------------------------------------------------------------
+float ExpressionTool::GetEventEndTime()
+{
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( !ev )
+ return 1.0f;
+
+ return ev->GetDuration();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// clip -
+// Output : float
+//-----------------------------------------------------------------------------
+float ExpressionTool::GetTimeValueForMouse( int mx, bool clip /*=false*/)
+{
+ int left, right;
+
+ GetWorkspaceLeftRight( left, right );
+
+ float st, ed;
+ GetStartAndEndTime( st, ed );
+
+ if ( clip )
+ {
+ if ( mx < 0 )
+ {
+ return st;
+ }
+ if ( mx > w2() )
+ {
+ return ed;
+ }
+ }
+
+ float frac = (float)( mx - left ) / (float)( right - left );
+ return st + frac * ( ed - st );
+}
+
+void ExpressionTool::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CChoreoEvent *e = GetSafeEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+ if ( !duration )
+ return;
+
+ int leftx = GetPixelForTimeValue( duration );
+ if ( leftx >= w2() )
+ return;
+
+ RECT rcClient;
+ drawHelper.GetClientRect( rcClient );
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, rcClient.top + TRAY_HEIGHT, leftx, rcClient.bottom );
+
+}
+
+void ExpressionTool::OnSortByUsed( void )
+{
+ m_pWorkspace->OnSortByUsed();
+}
+
+void ExpressionTool::OnSortByName( void )
+{
+ m_pWorkspace->OnSortByName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *item -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ExpressionTool::IsFocusItem( TimelineItem *item )
+{
+ return m_pWorkspace->GetClickedItem() == item;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete a vertical column of samples between the selection
+// markers. If excise_time is true, shifts remaining samples left
+// Input : excise_time -
+//-----------------------------------------------------------------------------
+void ExpressionTool::OnDeleteSelection( bool excise_time )
+{
+ if ( !m_bSelectionActive )
+ return;
+
+ // Force selection of everything again!
+ SelectPoints( m_flSelection[ 0 ], m_flSelection[ 1 ] );
+
+ int i, t;
+
+ char const *undotext = excise_time ? "Excise column" : "Delete column";
+
+ float shift_left_time = m_flSelection[ 1 ] - m_flSelection[ 0 ];
+ Assert( shift_left_time > 0.0f );
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undotext );
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( t = 0; t < 2; t++ )
+ {
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ track->RemoveSample( i, t );
+ }
+
+ if ( !excise_time )
+ continue;
+
+
+ // Now shift things after m_flSelection[0] to the left
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( sample->time < m_flSelection[ 1 ] )
+ continue;
+
+ // Shift it
+ sample->time -= shift_left_time;
+ }
+ }
+
+ item->DrawSelf();
+ }
+
+ g_pChoreoView->PushRedo( undotext );
+
+ // Clear selection and redraw()
+ DeselectAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::OnResetItemSize()
+{
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( !item )
+ return;
+
+ item->ResetHeight();
+ m_pWorkspace->LayoutItems( true );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::OnResetAllItemSizes()
+{
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( controller );
+ if ( !item )
+ continue;
+ item->ResetHeight();
+ }
+
+ m_pWorkspace->LayoutItems( true );
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void ExpressionTool::OnScaleSamples()
+{
+ int t, i;
+
+ //Scale samples
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Scale selected samples" );
+ strcpy( params.m_szPrompt, "Factor:" );
+ strcpy( params.m_szInputText, "1.0" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ float scale_factor = atof( params.m_szInputText );
+ if( scale_factor <= 0.0f )
+ {
+ Con_Printf( "Can't scale to %.2f\n", scale_factor );
+ }
+
+ char const *undotext = "Scale samples";
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undotext );
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( t = 0; t < 2; t++ )
+ {
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ // Scale it
+ float curvalue = sample->value;
+ curvalue *= scale_factor;
+ // Clamp it
+ curvalue = clamp( curvalue, 0.0f, 1.0f );
+ sample->value = curvalue;
+ }
+ }
+ }
+
+ g_pChoreoView->PushRedo( undotext );
+
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+void ExpressionTool::OnModelChanged()
+{
+ SetEvent( NULL );
+ redraw();
+}
+
+void ExpressionTool::OnEdgeProperties()
+{
+ TimelineItem *item = m_pWorkspace->GetClickedItem();
+ if ( !item )
+ return;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ return;
+
+ CEdgePropertiesParams params;
+ Q_memset( &params, 0, sizeof( params ) );
+ Q_strcpy( params.m_szDialogTitle, "Edge Properties" );
+
+ params.SetFromFlexTrack( track );
+
+ if ( !EdgeProperties( &params ) )
+ {
+ return;
+ }
+
+ char const *undotext = "Change Edge Properties";
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undotext );
+
+ // Apply changes.
+ params.ApplyToTrack( track );
+
+ g_pChoreoView->PushRedo( undotext );
+
+ m_pWorkspace->redraw();
+ redraw();
+}
+
+float ExpressionTool::GetScrubberSceneTime()
+{
+ CChoreoEvent *ev = GetSafeEvent();
+ if ( !ev )
+ return 0.0f;
+
+ float curtime = GetScrub();
+ curtime += ev->GetStartTime();
+ return curtime;
+}
+
+void ExpressionTool::GetTimelineItems( CUtlVector< TimelineItem * >& list )
+{
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( i );
+ if ( !item )
+ continue;
+
+ list.AddToTail( item );
+ }
+}
+
+
+
+bool ExpressionTool::HasCopiedColumn()
+{
+ return m_ColumnCopy.m_bActive;
+}
+
+void ExpressionTool::OnCopyColumn()
+{
+ m_ColumnCopy.Reset();
+
+ m_ColumnCopy.m_bActive = true;
+ m_ColumnCopy.m_flCopyTimes[ 0 ] = m_flSelection[ 0 ];
+ m_ColumnCopy.m_flCopyTimes[ 1 ] = m_flSelection[ 1 ];
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; ++controller )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ for ( int i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ // Add to dictionary
+ CExpressionSample copy( *sample );
+ copy.selected = false;
+
+ int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
+ if ( tIndex == m_ColumnCopy.m_Data.InvalidIndex() )
+ {
+ tIndex = m_ColumnCopy.m_Data.Insert( track->GetFlexControllerName() );
+ }
+
+ CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
+ data.m_Samples[ t ].AddToTail( copy );
+ }
+ }
+ }
+}
+
+void ExpressionTool::OnPasteColumn()
+{
+ if ( !m_ColumnCopy.m_bActive )
+ {
+ Msg( "Nothing to paste\n" );
+ return;
+ }
+
+ float flPasteTime = GetTimeForClickedPos();
+
+ float flPasteEndTime = flPasteTime + m_ColumnCopy.m_flCopyTimes[ 1 ] - m_ColumnCopy.m_flCopyTimes[ 0 ];
+
+ // Clear selection and redraw()
+ DeselectAll();
+
+ // Select everthing in the paste region so we can delete the existing stuff
+ SelectPoints( flPasteTime, flPasteEndTime );
+
+ int i, t;
+
+ char const *undotext = "Paste column";
+
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undotext );
+
+ for ( int controller = 0; controller < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; controller++ )
+ {
+ TimelineItem *item = m_pWorkspace->GetItem( controller );
+ if ( !item )
+ continue;
+
+ CFlexAnimationTrack *track = item->GetSafeTrack();
+ if ( !track )
+ continue;
+
+ int tIndex = m_ColumnCopy.m_Data.Find( track->GetFlexControllerName() );
+
+ for ( t = 0; t < 2; t++ )
+ {
+ // Remove all selected samples
+ for ( i = track->GetNumSamples( t ) - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ track->RemoveSample( i, t );
+ }
+
+ // Now add the new samples, if any in the time selection
+ if ( tIndex != m_ColumnCopy.m_Data.InvalidIndex() )
+ {
+ CColumnCopier::CTrackData &data = m_ColumnCopy.m_Data[ tIndex ];
+
+ for ( int j = 0; j < data.m_Samples[ t ].Count(); ++j )
+ {
+ CExpressionSample *s = &data.m_Samples[ t ][ j ];
+ CExpressionSample *newSample = track->AddSample( s->time - m_ColumnCopy.m_flCopyTimes[ 0 ] + flPasteTime, s->value, t );
+ newSample->selected = true;
+ }
+ }
+ track->Resort( t );
+ }
+
+ item->DrawSelf();
+ }
+
+ g_pChoreoView->PushRedo( undotext );
+}
+
+void ExpressionTool::ClearColumnCopy()
+{
+ m_ColumnCopy.Reset();
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/expressiontool.h b/utils/hlfaceposer/expressiontool.h
new file mode 100644
index 0000000..49365b8
--- /dev/null
+++ b/utils/hlfaceposer/expressiontool.h
@@ -0,0 +1,368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef EXPRESSIONTOOL_H
+#define EXPRESSIONTOOL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "studio.h"
+#include "utlvector.h"
+#include "tier1/utldict.h"
+#include "faceposertoolwindow.h"
+
+class CChoreoEvent;
+class TimelineItem;
+class CFlexAnimationTrack;
+class CExpClass;
+class CChoreoWidgetDrawHelper;
+class CExpressionToolWorkspace;
+class CChoreoView;
+class CFlexTimingTag;
+class CExpression;
+class mxSlider;
+
+#define IDC_EXPRESSIONTOOLVSCROLL 1000
+#define IDC_ADDTRACKS 1001
+#define IDC_COLLAPSEALL 1002
+#define IDC_EXPANDALL 1003
+#define IDC_EXPANDVALID 1004
+#define IDC_INSERT_TIMING_TAG 1005
+#define IDC_DELETE_TIMING_TAG 1006
+#define IDC_LOCK_TIMING_TAG 1007
+#define IDC_UNLOCK_TIMING_TAG 1008
+
+#define IDC_COPY_TO_FLEX 1009
+#define IDC_COPY_FROM_FLEX 1010
+
+#define IDC_NEW_EXPRESSION_FROM_FLEXANIMATION 1011
+
+#define IDC_EXPORT_FA 1012
+#define IDC_IMPORT_FA 1013
+
+#define IDC_REDO_FA 1014
+#define IDC_UNDO_FA 1015
+
+#define IDC_TL_COPY 1016
+#define IDC_TL_PASTE 1017
+#define IDC_TL_DELETE 1018
+#define IDC_TL_DESELECT 1019
+#define IDC_TL_SELECTALL 1020
+
+#define IDC_TL_COLLAPSE 1021
+#define IDC_TL_EXPAND 1022
+#define IDC_TL_ENABLE 1023
+#define IDC_TL_DISABLE 1024
+
+#define IDC_TL_EDITNORMAL 1025
+#define IDC_TL_EDITLEFTRIGHT 1026
+
+#define IDC_COLLAPSE_ALL_EXCEPT 1027
+#define IDC_DISABLE_ALL_EXCEPT 1028
+#define IDC_ENABLE_ALL_VALID 1029
+
+#define IDC_TL_SNAPSELECTED 1030
+#define IDC_TL_SNAPPOINTS 1031
+#define IDC_TL_DELETECOLUMN 1032
+#define IDC_TL_SNAPALL 1033
+
+#define IDC_FLEX_CHANGESCALE 1034
+#define IDC_FLEXHSCROLL 1035
+
+#define IDC_ET_SORT_BY_USED 1036
+#define IDC_ET_SORT_BY_NAME 1037
+
+#define IDC_ET_SELECTION_DELETE 1038
+#define IDC_ET_SELECTION_EXCISE 1039
+
+#define IDC_ET_RESET_ITEM_SIZE 1040
+#define IDC_ET_RESET_ALL_ITEM_SIZES 1041
+
+#define IDC_FLEX_SCALESAMPLES 1042
+
+#define IDC_TL_KB_TENSION 1050
+#define IDC_TL_KB_BIAS 1051
+#define IDC_TL_KB_CONTINUITY 1052
+
+#define IDC_ET_EDGEPROPERTIES 1053
+#define IDC_ET_SELECTION_COPY 1054
+#define IDC_ET_SELECTION_PASTE 1055
+
+#include "ExpressionSample.h"
+
+class ExpressionTool : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ // Construction
+ ExpressionTool( mxWindow *parent );
+ ~ExpressionTool( void );
+
+ virtual void Think( float dt );
+ void ScrubThink( float dt, bool scrubbing );
+ virtual bool IsScrubbing( void ) const;
+ virtual bool IsProcessing( void );
+
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground();
+
+ bool SetFlexAnimationTrackFromExpression( int mx, int my, CExpClass *cl, CExpression *exp );
+
+ void SetEvent( CChoreoEvent *event );
+
+ bool HasCopyData( void );
+
+ void Copy( CFlexAnimationTrack *source );
+ void Paste( CFlexAnimationTrack *destination );
+
+ void GetScrubHandleRect( RECT& rcHandle, bool clipped = false );
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper, RECT& rcHandle );
+ void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
+
+ CChoreoEvent *GetSafeEvent( void );
+
+ void ExpandAll( void );
+ void ExpandValid( void );
+
+ void LayoutItems( bool force = false );
+
+ void OnCopyToFlex( bool isEdited );
+ void OnCopyFromFlex( bool isEdited );
+
+ void OnCopyToFlex( float scenetime, bool isEdited );
+ void OnCopyFromFlex( float scenetime, bool isEdited );
+ void OnSetSingleKeyFromFlex( char const *sliderName );
+
+ void OnNewExpression( void );
+ void ShowContextMenu( mxEvent *event, bool include_track_menus );
+
+ void ForceScrubPosition( float newtime );
+ void ForceScrubPositionFromSceneTime( float scenetime );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+
+ void DrawScrubHandles();
+
+ void SetClickedPos( int x, int y );
+ float GetTimeForClickedPos( void );
+
+ void SetMouseOverPos( int x, int y );
+ void GetMouseOverPos( int &x, int& y );
+ void GetMouseOverPosRect( RECT& rcPos );
+ void DrawMouseOverPos( CChoreoWidgetDrawHelper& drawHelper, RECT& rcPos );
+ void DrawMouseOverPos();
+
+
+ void MoveSelectedSamples( float dfdx, float dfdy, bool snap );
+ void DeleteSelectedSamples( void );
+ int CountSelectedSamples( void );
+ void DeselectAll( void );
+
+ void RepositionHSlider( void );
+
+ bool IsFocusItem( TimelineItem *item );
+ virtual void OnModelChanged();
+
+ float GetScrub() const { return m_flScrub; }
+ float GetScrubberSceneTime();
+
+ void GetTimelineItems( CUtlVector< TimelineItem * >& list );
+ void InvalidateLayout( void );
+
+private:
+ void DoTrackLookup( CChoreoEvent *event );
+
+ void AddFlexTimingTag( int mx );
+ void DeleteFlexTimingTag( int mx, int my );
+
+ void OnSortByUsed( void );
+ void OnSortByName( void );
+
+ void OnDeleteSelection( bool excise_time );
+ void OnResetItemSize();
+ void OnResetAllItemSizes();
+ void ResampleControlPoints( CFlexTimingTag *tag, float newposition );
+
+ void OnScaleSamples();
+
+ void LockTimingTag( void );
+ void UnlockTimingTag( void );
+
+ bool GetTimingTagRect( RECT& rcClient, CChoreoEvent *event, CFlexTimingTag *tag, RECT& rcTag );
+
+// float MouseToFrac( int mx );
+//float MouseToTime( int mx );
+// int TimeToMouse( float t );
+
+ void GetWorkspaceLeftRight( int& left, int& right );
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ CFlexTimingTag *IsMouseOverTag( int mx, int my );
+
+ void DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper );
+
+ void DrawFocusRect( void );
+
+ void ApplyBounds( int& mx, int& my );
+ void CalcBounds( int movetype );
+
+ void OnExportFlexAnimation( void );
+ void OnImportFlexAnimation( void );
+
+ void OnUndo( void );
+ void OnRedo( void );
+
+ void StartDragging( int dragtype, int startx, int starty, HCURSOR cursor );
+ void GetWorkspaceRect( RECT &rc );
+ void AddFocusRect( RECT& rc );
+ void OnMouseMove( mxEvent *event );
+
+ // Mouse control over selected samples
+ void SelectPoints( float starttime, float endtime );
+ void FinishSelect( int startx, int mx );
+ void FinishMoveSelection( int startx, int mx );
+ void FinishMoveSelectionStart( int startx, int mx );
+ void FinishMoveSelectionEnd( int startx, int mx );
+
+ // In general over the point area tray
+ bool IsMouseOverPoints( int mx, int my );
+ // Specifically over selected points
+ bool IsMouseOverSelection( int mx, int my );
+ bool IsMouseOverSelectionStartEdge( mxEvent *event );
+ bool IsMouseOverSelectionEndEdge( mxEvent *event );
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+ void OnChangeScale();
+ int ComputeHPixelsNeeded( void );
+ float GetTimeValueForMouse( int mx, bool clip = false );
+
+ void OnEdgeProperties();
+
+public:
+ int GetPixelForTimeValue( float time, bool *clipped = NULL );
+ float GetPixelsPerSecond( void );
+ void GetStartAndEndTime( float& st, float& ed );
+ float GetEventEndTime();
+
+private:
+
+ class CColumnCopier
+ {
+ public:
+ class CTrackData
+ {
+ public:
+ CTrackData() {};
+ CTrackData( const CTrackData& other )
+ {
+ m_Samples[ 0 ].CopyArray( other.m_Samples[ 0 ].Base(), other.m_Samples[ 0 ].Count() );
+ m_Samples[ 1 ].CopyArray( other.m_Samples[ 1 ].Base(), other.m_Samples[ 1 ].Count() );
+ }
+ CUtlVector< CExpressionSample > m_Samples[ 2 ];
+ };
+
+ bool m_bActive;
+ float m_flCopyTimes[ 2 ];
+ CUtlDict< CTrackData, int > m_Data;
+
+ CColumnCopier() : m_bActive( false )
+ {
+ m_flCopyTimes[ 0 ] = m_flCopyTimes[ 1 ] = 0.0f;
+ }
+
+ void Reset()
+ {
+ m_bActive = false;
+ m_flCopyTimes[ 0 ] = m_flCopyTimes[ 1 ] = 0.0f;
+ m_Data.Purge();
+ }
+ };
+
+ bool HasCopiedColumn();
+ void OnCopyColumn();
+ void OnPasteColumn();
+ void ClearColumnCopy();
+
+ CColumnCopier m_ColumnCopy;
+
+ int m_nFocusEventGlobalID;
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_SCRUBBER,
+ DRAGTYPE_FLEXTIMINGTAG,
+
+ DRAGTYPE_SELECTSAMPLES,
+ DRAGTYPE_MOVESELECTION,
+ DRAGTYPE_MOVESELECTIONSTART,
+ DRAGTYPE_MOVESELECTIONEND,
+ };
+
+ HCURSOR m_hPrevCursor;
+ int m_nDragType;
+
+ int m_nStartX;
+ int m_nStartY;
+ int m_nLastX;
+ int m_nLastY;
+
+ int m_nClickedX;
+ int m_nClickedY;
+
+ bool m_bUseBounds;
+ int m_nMinX;
+ int m_nMaxX;
+
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+
+ CUtlVector< CExpressionSample > m_CopyData[2];
+
+ CExpressionToolWorkspace *m_pWorkspace;
+
+ CChoreoEvent *m_pLastEvent;
+
+ int m_nMousePos[ 2 ];
+
+ float m_flSelection[ 2 ];
+ bool m_bSelectionActive;
+
+ bool m_bSuppressLayout;
+ // Height/width of scroll bars
+ int m_nScrollbarHeight;
+ float m_flLeftOffset;
+ mxScrollbar *m_pHorzScrollBar;
+ int m_nLastHPixelsNeeded;
+ // How many pixels per second we are showing in the UI
+ float m_flPixelsPerSecond;
+ // Do we need to move controls?
+ bool m_bLayoutIsValid;
+ float m_flLastDuration;
+ bool m_bInSetEvent;
+ float m_flScrubberTimeOffset;
+
+ friend class CChoreoView;
+
+
+};
+
+extern ExpressionTool *g_pExpressionTool;
+
+#endif // EXPRESSIONTOOL_H
diff --git a/utils/hlfaceposer/faceposer_models.cpp b/utils/hlfaceposer/faceposer_models.cpp
new file mode 100644
index 0000000..49ec500
--- /dev/null
+++ b/utils/hlfaceposer/faceposer_models.cpp
@@ -0,0 +1,1142 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "hlfaceposer.h"
+#include "StudioModel.h"
+#include "faceposer_models.h"
+#include "filesystem.h"
+#include "ifaceposerworkspace.h"
+#include <mxtk/mx.h>
+#include "mdlviewer.h"
+#include "mxexpressiontray.h"
+#include "ControlPanel.h"
+#include "checksum_crc.h"
+#include "ViewerSettings.h"
+#include "matsyswin.h"
+#include "KeyValues.h"
+#include "utlbuffer.h"
+#include "expression.h"
+#include "ProgressDialog.h"
+#include "tier1/UtlString.h"
+#include "tier1/FmtStr.h"
+#include "tier1/KeyValues.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+void SetupModelFlexcontrollerLinks( StudioModel *model );
+
+IFaceposerModels::CFacePoserModel::CFacePoserModel( char const *modelfile, StudioModel *model )
+{
+ m_pModel = model;
+ m_szActorName[ 0 ] = 0;
+ m_szShortName[ 0 ] = 0;
+ strcpy( m_szModelFileName, modelfile );
+ Q_FixSlashes( m_szModelFileName );
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( hdr )
+ {
+ Q_StripExtension( hdr->pszName(), m_szShortName, sizeof( m_szShortName ) );
+ }
+
+ m_bVisibileIn3DView = false;
+ m_bFirstBitmapLoad = true;
+
+ LoadBitmaps();
+}
+
+IFaceposerModels::CFacePoserModel::~CFacePoserModel()
+{
+ FreeBitmaps();
+}
+
+void IFaceposerModels::CFacePoserModel::LoadBitmaps()
+{
+ CStudioHdr *hdr = m_pModel ? m_pModel->GetStudioHdr() : NULL;
+ if ( hdr )
+ {
+ for ( int i = 0 ;i < hdr->GetNumSeq(); i++ )
+ {
+ mxbitmapdata_t *bm = new mxbitmapdata_t();
+
+ AnimBitmap *entry = new AnimBitmap();
+ entry->needsload = true;
+ entry->bitmap = bm;
+
+ // Need to load bitmap from disk image via crc, etc.
+ //Assert( 0 );
+
+ m_AnimationBitmaps.AddToTail( entry );
+ }
+ }
+}
+
+CRC32_t IFaceposerModels::CFacePoserModel::GetBitmapCRC( int sequence )
+{
+ CStudioHdr *hdr = m_pModel ? m_pModel->GetStudioHdr() : NULL;
+ if ( !hdr )
+ return (CRC32_t)-1;
+
+ if ( sequence < 0 || sequence >= hdr->GetNumSeq() )
+ return (CRC32_t)-1;
+
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( sequence );
+
+ CRC32_t crc;
+ CRC32_Init( &crc );
+
+ // For sequences, we'll checsum a bit of data
+
+ CRC32_ProcessBuffer( &crc, (void *)seqdesc.pszLabel(), Q_strlen( seqdesc.pszLabel() ) );
+ CRC32_ProcessBuffer( &crc, (void *)seqdesc.pszActivityName(), Q_strlen( seqdesc.pszActivityName() ) );
+ CRC32_ProcessBuffer( &crc, (void *)&seqdesc.flags, sizeof( seqdesc.flags ) );
+ //CRC32_ProcessBuffer( &crc, (void *)&seqdesc.numevents, sizeof( seqdesc.numevents ) );
+ CRC32_ProcessBuffer( &crc, (void *)&seqdesc.numblends, sizeof( seqdesc.numblends ) );
+ CRC32_ProcessBuffer( &crc, (void *)seqdesc.groupsize, sizeof( seqdesc.groupsize ) );
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( m_pModel->GetFileName( ), m_pModel->GetKeyValueText( sequence ) ) )
+ {
+ // Yuck, but I need it in a contiguous block of memory... oh well...
+ CUtlBuffer buf;
+ seqKeyValues->RecursiveSaveToFile( buf, 0 );
+ CRC32_ProcessBuffer( &crc, ( void * )buf.Base(), buf.TellPut() );
+ }
+
+ seqKeyValues->deleteThis();
+
+ CRC32_Final( &crc );
+
+ return crc;
+}
+
+const char *IFaceposerModels::CFacePoserModel::GetBitmapChecksum( int sequence )
+{
+ CRC32_t crc = GetBitmapCRC( sequence );
+
+ // Create string name out of binary data
+ static char filename[ 512 ];
+
+ char hex[ 16 ];
+ Q_binarytohex( (byte *)&crc, sizeof( crc ), hex, sizeof( hex ) );
+
+ Q_snprintf( filename, sizeof( filename ), "%s", hex );
+ return filename;
+}
+
+const char *IFaceposerModels::CFacePoserModel::GetBitmapFilename( int sequence )
+{
+ char *in, *out;
+ static char filename[ 256 ];
+ filename[ 0 ] = 0;
+
+ char modelName[512], modelNameTemp[512];
+ Q_strncpy( modelNameTemp, GetShortModelName(), sizeof( modelNameTemp ) );
+
+ in = modelNameTemp;
+ out = modelName;
+
+ while ( *in )
+ {
+ if ( V_isalnum( *in ) ||
+ *in == '_' ||
+ *in == '\\' ||
+ *in == '/' ||
+ *in == '.' ||
+ *in == ':' )
+ {
+ *out++ = *in;
+ }
+ in++;
+ }
+ *out = 0;
+
+
+ Q_snprintf( filename, sizeof( filename ), "expressions/%s/animation/%s.bmp", modelName, GetBitmapChecksum( sequence ) );
+
+ Q_FixSlashes( filename );
+ strlwr( filename );
+
+ CreatePath( filename );
+
+ return filename;
+}
+
+void IFaceposerModels::CFacePoserModel::FreeBitmaps()
+{
+ while ( m_AnimationBitmaps.Count() > 0 )
+ {
+ AnimBitmap *bm = m_AnimationBitmaps[ 0 ];
+ delete bm->bitmap;
+ delete bm;
+ m_AnimationBitmaps.Remove( 0 );
+ }
+}
+
+void IFaceposerModels::CFacePoserModel::LoadBitmapForSequence( mxbitmapdata_t *bitmap, int sequence )
+{
+ // See if it exists
+ char filename[ 512 ];
+ Q_strncpy( filename, GetBitmapFilename( sequence ), sizeof( filename ) );
+
+ if ( !LoadBitmapFromFile( filename, *bitmap ) )
+ {
+ CreateNewBitmap( filename, sequence, 256, false, NULL, bitmap );
+ }
+}
+
+static float FindPoseCycle( StudioModel *model, int sequence )
+{
+ float cycle = 0.0f;
+ if ( !model->GetStudioHdr() )
+ return cycle;
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ int thumbnail_frame = pkvAllFaceposer->GetInt( "thumbnail_frame", 0 );
+ if ( thumbnail_frame )
+ {
+ // Convert frame to cycle if we have valid data
+ int maxFrame = model->GetNumFrames( sequence ) - 1;
+
+ if ( maxFrame > 0 )
+ {
+ cycle = thumbnail_frame / (float)maxFrame;
+ }
+ }
+ }
+ }
+
+ seqKeyValues->deleteThis();
+
+ return cycle;
+}
+
+
+void EnableStickySnapshotMode( void )
+{
+ g_pMatSysWindow->EnableStickySnapshotMode( );
+}
+
+void DisableStickySnapshotMode( void )
+{
+ g_pMatSysWindow->DisableStickySnapshotMode( );
+}
+
+
+void IFaceposerModels::CreateNewBitmap( int modelindex, char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, CExpression *pExpression, mxbitmapdata_t *bitmap )
+{
+ CFacePoserModel *m = m_Models[ modelindex ];
+ if ( m )
+ {
+ m->CreateNewBitmap( pchBitmapFilename, sequence, nSnapShotSize, bZoomInOnFace, pExpression, bitmap );
+ }
+}
+
+void IFaceposerModels::CFacePoserModel::CreateNewBitmap( char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, CExpression *pExpression, mxbitmapdata_t *bitmap )
+{
+ MatSysWindow *pWnd = g_pMatSysWindow;
+ if ( !pWnd )
+ return;
+
+ StudioModel *model = m_pModel;
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+ if ( sequence < 0 || sequence >= hdr->GetNumSeq() )
+ return;
+
+ mstudioseqdesc_t &seqdesc = hdr->pSeqdesc( sequence );
+
+ Con_ColorPrintf( FILE_COLOR, "Creating bitmap %s for sequence '%s'\n", pchBitmapFilename, seqdesc.pszLabel() );
+
+ model->ClearOverlaysSequences();
+ int iLayer = model->GetNewAnimationLayer();
+ model->SetOverlaySequence( iLayer, sequence, 1.0 );
+ model->SetOverlayRate( iLayer, FindPoseCycle( model, sequence ), 0.0 );
+
+ for (int i = 0; i < hdr->GetNumPoseParameters(); i++)
+ {
+ model->SetPoseParameter( i, 0.0 );
+ }
+
+ float flexValues[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ] = { 0 };
+
+ if ( pExpression )
+ {
+ float *settings = pExpression->GetSettings();
+ float *weights = pExpression->GetWeights();
+
+ // Save existing settings from model
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+ if ( j == -1 )
+ continue;
+ flexValues[ i ] = model->GetFlexController( i );
+ // Set Value from passed in settings
+ model->SetFlexController( i, settings[ j ] * weights[ j ] );
+ }
+ }
+
+ model->ClearLookTargets( );
+
+ QAngle oldrot, oldLight;
+ Vector oldtrans;
+
+ VectorCopy( model->m_angles, oldrot );
+ VectorCopy( model->m_origin, oldtrans );
+ VectorCopy( g_viewerSettings.lightrot, oldLight );
+
+ model->m_angles.Init();
+ model->m_origin.Init();
+ g_viewerSettings.lightrot.Init();
+
+ g_viewerSettings.lightrot.y = -180;
+
+ bool bSaveGround = g_viewerSettings.showGround;
+ g_viewerSettings.showGround = false;
+
+ if ( bZoomInOnFace )
+ {
+ Vector size;
+ VectorSubtract( hdr->hull_max(), hdr->hull_min(), size );
+
+ float eyeheight = hdr->hull_min().z + 0.9 * size.z;
+ // float width = ( size.x + size.y ) / 2.0f;
+
+ model->m_origin.x = size.z * .6f;
+
+ if ( hdr->GetNumAttachments() > 0 )
+ {
+ for (int i = 0; i < hdr->GetNumAttachments(); i++)
+ {
+ const mstudioattachment_t &attachment = hdr->pAttachment( i );
+ int iBone = hdr->GetAttachmentBone( i );
+
+ if ( Q_stricmp( attachment.pszName(), "eyes" ) )
+ continue;
+
+ mstudiobone_t *bone = hdr->pBone( iBone );
+ if ( !bone )
+ continue;
+
+ matrix3x4_t boneToPose;
+ MatrixInvert( bone->poseToBone, boneToPose );
+
+ matrix3x4_t attachmentPoseToLocal;
+ ConcatTransforms( boneToPose, attachment.local, attachmentPoseToLocal );
+
+ Vector localSpaceEyePosition;
+ VectorITransform( vec3_origin, attachmentPoseToLocal, localSpaceEyePosition );
+
+ // Not sure why this must be negative?
+ eyeheight = -localSpaceEyePosition.z + hdr->hull_min().z;
+ break;
+ }
+ }
+
+ KeyValues *seqKeyValues = new KeyValues("");
+ if ( seqKeyValues->LoadFromBuffer( model->GetFileName( ), model->GetKeyValueText( sequence ) ) )
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ float flEyeheight = pkvAllFaceposer->GetFloat( "eye_height", -9999.0f );
+ if ( flEyeheight != -9999.0f )
+ {
+ eyeheight = flEyeheight;
+ }
+ }
+ }
+
+ model->m_origin.z += eyeheight;
+ }
+ else
+ {
+ Vector mins, maxs;
+ model->ExtractBbox(mins, maxs);
+ Vector size;
+ VectorSubtract( maxs, mins, size );
+
+ float maxdim = size.x;
+ if ( size.y > maxdim )
+ maxdim = size.y;
+ if ( size.z > maxdim )
+ maxdim = size.z;
+
+ float midpoint = mins.z + 0.5 * size.z;
+
+ model->m_origin.x = 3 * maxdim;
+ model->m_origin.z += midpoint;
+ }
+
+ g_pMatSysWindow->PushSnapshotMode( nSnapShotSize );
+
+ // Snapshots are taken of the back buffer;
+ // we need to render to the back buffer but not move it to the front
+ pWnd->SuppressBufferSwap( true );
+ pWnd->redraw();
+ pWnd->SuppressBufferSwap( false );
+
+ // make it square, assumes w > h
+ char fullpath[ 512 ];
+ Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", GetGameDirectory(), pchBitmapFilename );
+ pWnd->TakeSnapshotRect( fullpath, 0, 0, nSnapShotSize, nSnapShotSize );
+
+ g_pMatSysWindow->PopSnapshotMode( );
+
+ VectorCopy( oldrot, model->m_angles );
+ VectorCopy( oldtrans, model->m_origin );
+ VectorCopy( oldLight, g_viewerSettings.lightrot );
+
+ g_viewerSettings.showGround = bSaveGround;
+
+ if ( pExpression )
+ {
+ // Save existing settings from model
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); ++i )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+ if ( j == -1 )
+ continue;
+
+ model->SetFlexController( i, flexValues[ i ] );
+ }
+ }
+
+ model->ClearOverlaysSequences();
+
+ if ( bitmap->valid )
+ {
+ DeleteObject( bitmap->image );
+ bitmap->image = 0;
+ bitmap->valid = false;
+ }
+
+ LoadBitmapFromFile( pchBitmapFilename, *bitmap );
+}
+
+mxbitmapdata_t *IFaceposerModels::CFacePoserModel::GetBitmapForSequence( int sequence )
+{
+ static mxbitmapdata_t nullbitmap;
+ if ( sequence < 0 || sequence >= m_AnimationBitmaps.Count() )
+ return &nullbitmap;
+
+ /*
+ if ( m_bFirstBitmapLoad )
+ {
+ m_bFirstBitmapLoad = false;
+ ReconcileAnimationBitmaps();
+ }
+ */
+
+ AnimBitmap *slot = m_AnimationBitmaps[ sequence ];
+ if ( slot->needsload )
+ {
+ slot->needsload = false;
+ LoadBitmapForSequence( slot->bitmap, sequence );
+ }
+
+ return m_AnimationBitmaps[ sequence ]->bitmap;
+}
+
+void IFaceposerModels::CFacePoserModel::BuildValidChecksums( CUtlRBTree< CRC32_t > &tree )
+{
+ StudioModel *model = m_pModel;
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ for ( int i = 0; i < hdr->GetNumSeq(); i++ )
+ {
+ CRC32_t crc = GetBitmapCRC( i );
+ tree.Insert( crc );
+ }
+}
+
+void IFaceposerModels::CFacePoserModel::ReconcileAnimationBitmaps()
+{
+ // iterate files in directory and see if each checksum is valid and if not delete the .bmp
+ char path[ 512 ];
+ Q_snprintf( path, sizeof( path ), "expressions/%s/animation/*.bmp", GetShortModelName() );
+
+ FileFindHandle_t hFindFile;
+
+ char const *fn = filesystem->FindFirstEx( path, "MOD", &hFindFile );
+
+ g_pProgressDialog->Start( CFmtStr( "%s - Reconcile Animation Thumbnails", GetShortModelName() ), "", true );
+
+ CUtlVector< CUtlString > workList;
+
+ if ( fn )
+ {
+ while ( fn )
+ {
+ // Don't do anything with directories
+ if ( !filesystem->FindIsDirectory( hFindFile ) )
+ {
+ CUtlString s = fn;
+ workList.AddToTail( s );
+ }
+
+ fn = filesystem->FindNext( hFindFile );
+ }
+
+ filesystem->FindClose( hFindFile );
+ }
+
+ CUtlRBTree< CRC32_t > tree( 0, 0, DefLessFunc( CRC32_t ) );
+ BuildValidChecksums( tree );
+
+ for ( int i = 0 ; i < workList.Count(); ++i )
+ {
+ char testname[ 256 ];
+ Q_StripExtension( workList[ i ].String(), testname, sizeof( testname ) );
+
+ g_pProgressDialog->UpdateText( "%s", testname );
+ g_pProgressDialog->Update( (float)i / (float)workList.Count() );
+
+ CRC32_t check;
+ Q_hextobinary( testname, Q_strlen( testname ), (byte *)&check, sizeof( check ) );
+
+ if ( tree.Find( check ) == tree.InvalidIndex() )
+ {
+ Q_snprintf( testname, sizeof( testname ), "expressions/%s/animation/%s", GetShortModelName(), fn );
+
+ char fullpath[ 512 ];
+ filesystem->RelativePathToFullPath( testname, "MOD", fullpath, sizeof( fullpath ) );
+ // Delete it
+ Con_ErrorPrintf( "Removing unused bitmap file %s\n",
+ fullpath );
+
+ _unlink( fullpath );
+ }
+
+ if ( g_pProgressDialog->IsCancelled() )
+ {
+ Msg( "Cancelled\n" );
+ break;
+ }
+ }
+
+ g_pProgressDialog->Finish();
+}
+
+void IFaceposerModels::CFacePoserModel::RecreateAllAnimationBitmaps()
+{
+ StudioModel *model = m_pModel;
+ if ( !model )
+ return;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ g_pProgressDialog->Start( CFmtStr( "%s - Animation Thumbnails", GetShortModelName() ), "", true );
+
+ for ( int i = 0; i < hdr->GetNumSeq(); ++i )
+ {
+ const mstudioseqdesc_t &seq = hdr->pSeqdesc( i );
+
+ g_pProgressDialog->UpdateText( "%s", seq.pszLabel() );
+ g_pProgressDialog->Update( (float)i / (float)hdr->GetNumSeq() );
+
+ RecreateAnimationBitmap( i, false );
+
+ if ( g_pProgressDialog->IsCancelled() )
+ {
+ Msg( "Cancelling\n" );
+ break;
+ }
+ }
+
+ g_pProgressDialog->Finish();
+
+ ReconcileAnimationBitmaps();
+}
+
+void IFaceposerModels::CFacePoserModel::RecreateAnimationBitmap( int sequence, bool reconcile )
+{
+ if ( sequence < 0 || sequence >= m_AnimationBitmaps.Count() )
+ {
+ Assert( 0 );
+ return;
+ }
+ AnimBitmap *slot = m_AnimationBitmaps[ sequence ];
+ slot->needsload = true;
+ if ( slot->bitmap->valid )
+ {
+ DeleteObject( slot->bitmap->image );
+ slot->bitmap->image = 0;
+ slot->bitmap->valid = false;
+ }
+
+ char filename[ 512 ];
+ Q_snprintf( filename, sizeof( filename ), "%s", GetBitmapFilename( sequence ) );
+
+ if ( filesystem->FileExists( filename ) )
+ {
+ char fullpath[ 512 ];
+ filesystem->RelativePathToFullPath( filename, "MOD", fullpath, sizeof( fullpath ) );
+ _unlink( fullpath );
+ }
+
+ // Force recreation
+ GetBitmapForSequence( sequence );
+
+ if ( reconcile )
+ {
+ ReconcileAnimationBitmaps( );
+ }
+}
+
+void IFaceposerModels::CFacePoserModel::Release( void )
+{
+ m_pModel->FreeModel( true );
+}
+
+void IFaceposerModels::CFacePoserModel::Restore( void )
+{
+ StudioModel *save = g_pStudioModel;
+
+ g_pStudioModel = m_pModel;
+
+ if (m_pModel->LoadModel( m_pModel->GetFileName() ) )
+ {
+ m_pModel->PostLoadModel( m_pModel->GetFileName() );
+ m_pModel->SetSequence( m_pModel->LookupSequence( "idle_subtle" ) );
+ }
+
+ g_pStudioModel = save;
+
+ SetupModelFlexcontrollerLinks( m_pModel );
+}
+
+
+IFaceposerModels::IFaceposerModels()
+{
+ m_nLastRenderFrame = -1;
+ m_nForceModelIndex = -1;
+}
+
+IFaceposerModels::~IFaceposerModels()
+{
+ while ( m_Models.Count() > 0 )
+ {
+ delete m_Models[ 0 ];
+ m_Models.Remove( 0 );
+ }
+}
+
+IFaceposerModels::CFacePoserModel *IFaceposerModels::GetEntry( int index )
+{
+ if ( index < 0 || index >= Count() )
+ return NULL;
+
+ CFacePoserModel *m = m_Models[ index ];
+ if ( !m )
+ return NULL;
+ return m;
+}
+
+int IFaceposerModels::Count( void ) const
+{
+ return m_Models.Count();
+}
+
+char const *IFaceposerModels::GetModelName( int index )
+{
+ CFacePoserModel *entry = GetEntry( index );
+ if ( !entry )
+ return "";
+
+ return entry->GetShortModelName();
+}
+
+char const *IFaceposerModels::GetModelFileName( int index )
+{
+ CFacePoserModel *entry = GetEntry( index );
+ if ( !entry )
+ return "";
+
+ return entry->GetModelFileName();
+}
+
+void IFaceposerModels::ForceActiveModelIndex( int index )
+{
+ m_nForceModelIndex = index;
+}
+
+void IFaceposerModels::UnForceActiveModelIndex()
+{
+ m_nForceModelIndex = -1;
+}
+
+int IFaceposerModels::GetActiveModelIndex( void ) const
+{
+ if ( !g_MDLViewer )
+ return 0;
+
+ if ( m_nForceModelIndex != -1 )
+ return m_nForceModelIndex;
+
+ return g_MDLViewer->GetActiveModelTab();
+}
+
+char const *IFaceposerModels::GetActiveModelName( void )
+{
+ if ( !g_MDLViewer )
+ return NULL;
+
+ return GetModelName( GetActiveModelIndex() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : StudioModel
+//-----------------------------------------------------------------------------
+StudioModel *IFaceposerModels::GetActiveStudioModel( void )
+{
+ StudioModel *mdl = GetStudioModel( GetActiveModelIndex() );
+ if ( !mdl )
+ return g_pStudioModel;
+ return mdl;
+}
+
+int IFaceposerModels::FindModelByFilename( char const *filename )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ if ( !stricmp( m->GetModelFileName(), filename ) )
+ return i;
+ }
+
+ return -1;
+}
+
+void SetupModelFlexcontrollerLinks( StudioModel *model );
+
+int IFaceposerModels::LoadModel( char const *filename )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int idx = FindModelByFilename( filename );
+ if ( idx == -1 && Count() < MAX_FP_MODELS )
+ {
+ StudioModel *model = new StudioModel();
+
+ StudioModel *save = g_pStudioModel;
+ g_pStudioModel = model;
+ if ( !model->LoadModel( filename ) )
+ {
+ delete model;
+ g_pStudioModel = save;
+ return 0; // ?? ERROR
+ }
+ g_pStudioModel = save;
+
+ model->SetSequence( model->LookupSequence( "idle_subtle" ) );
+ int idx = model->GetSequence();
+ model->SetSequence( idx );
+
+ SetupModelFlexcontrollerLinks( model );
+
+ if (!LoadViewerSettings( filename, model ))
+ {
+ InitViewerSettings( "faceposer" );
+ }
+ model->ClearOverlaysSequences();
+
+
+ CFacePoserModel *newEntry = new CFacePoserModel( filename, model );
+
+ idx = m_Models.AddToTail( newEntry );
+
+ g_MDLViewer->InitModelTab();
+
+ g_MDLViewer->SetActiveModelTab( idx );
+
+ //g_pControlPanel->CenterOnFace();
+ }
+ return idx;
+}
+
+void IFaceposerModels::FreeModel( int index )
+{
+ CFacePoserModel *entry = GetEntry( index );
+ if ( !entry )
+ return;
+
+ StudioModel *m = entry->GetModel();
+
+ SaveViewerSettings( m->GetFileName(), m );
+
+ m->FreeModel( false );
+ delete m;
+
+ delete entry;
+ m_Models.Remove( index );
+
+ g_MDLViewer->InitModelTab();
+}
+
+void IFaceposerModels::CloseAllModels( void )
+{
+ int c = Count();
+ for ( int i = c - 1; i >= 0; i-- )
+ {
+ FreeModel( i );
+ }
+}
+
+StudioModel *IFaceposerModels::GetStudioModel( int index )
+{
+ CFacePoserModel *m = GetEntry( index );
+ if ( !m )
+ return NULL;
+
+ if ( !m->GetModel() )
+ return NULL;
+
+ return m->GetModel();
+}
+
+CStudioHdr *IFaceposerModels::GetStudioHeader( int index )
+{
+ StudioModel *m = GetStudioModel( index );
+ if ( !m )
+ return NULL;
+
+ CStudioHdr *hdr = m->GetStudioHdr();
+ if ( !hdr )
+ return NULL;
+ return hdr;
+}
+
+int IFaceposerModels::GetModelIndexForActor( char const *actorname )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ if ( !stricmp( m->GetActorName(), actorname ) )
+ return i;
+ }
+
+ return 0;
+}
+
+StudioModel *IFaceposerModels::GetModelForActor( char const *actorname )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ if ( !stricmp( m->GetActorName(), actorname ) )
+ return m->GetModel();
+ }
+
+ return NULL;
+}
+
+char const *IFaceposerModels::GetActorNameForModel( int modelindex )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return "";
+ return m->GetActorName();
+}
+
+void IFaceposerModels::SetActorNameForModel( int modelindex, char const *actorname )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return;
+
+ m->SetActorName( actorname );
+}
+
+void IFaceposerModels::SaveModelList( void )
+{
+ workspacefiles->StartStoringFiles( IWorkspaceFiles::MODELDATA );
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ workspacefiles->StoreFile( IWorkspaceFiles::MODELDATA, m->GetModelFileName() );
+ }
+ workspacefiles->FinishStoringFiles( IWorkspaceFiles::MODELDATA );
+}
+
+void IFaceposerModels::LoadModelList( void )
+{
+ int files = workspacefiles->GetNumStoredFiles( IWorkspaceFiles::MODELDATA );
+ for ( int i = 0; i < files; i++ )
+ {
+ char const *filename = workspacefiles->GetStoredFile( IWorkspaceFiles::MODELDATA, i );
+ LoadModel( filename );
+ }
+}
+
+void IFaceposerModels::ReleaseModels( void )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ m->Release();
+ }
+}
+
+void IFaceposerModels::RestoreModels( void )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ m->Restore();
+ }
+}
+
+
+/*
+void IFaceposerModels::RefreshModels( void )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ m->Refresh();
+ }
+}
+*/
+
+int IFaceposerModels::CountVisibleModels( void )
+{
+ int num = 0;
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ if ( m->GetVisibleIn3DView() )
+ {
+ num++;
+ }
+ }
+
+ return num;
+}
+
+void IFaceposerModels::ShowModelIn3DView( int modelindex, bool show )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return;
+
+ m->SetVisibleIn3DView( show );
+}
+
+bool IFaceposerModels::IsModelShownIn3DView( int modelindex )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return false;
+
+ return m->GetVisibleIn3DView();
+}
+
+int IFaceposerModels::GetIndexForStudioModel( StudioModel *model )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ if ( m->GetModel() == model )
+ return i;
+ }
+ return -1;
+}
+
+void IFaceposerModels::CheckResetFlexes( void )
+{
+ int current_render_frame = g_MDLViewer->GetCurrentFrame();
+ if ( current_render_frame == m_nLastRenderFrame )
+ return;
+
+ m_nLastRenderFrame = current_render_frame;
+
+ // the phoneme editor just adds to the face, so reset the controllers
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ StudioModel *model = m->GetModel();
+ if ( !model )
+ continue;
+
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ continue;
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
+ {
+ model->SetFlexController( i, 0.0f );
+ }
+ }
+}
+
+void IFaceposerModels::ClearOverlaysSequences( void )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ StudioModel *model = m->GetModel();
+ if ( !model )
+ continue;
+
+ model->ClearOverlaysSequences();
+ }
+}
+
+mxbitmapdata_t *IFaceposerModels::GetBitmapForSequence( int modelindex, int sequence )
+{
+ static mxbitmapdata_t nullbitmap;
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return &nullbitmap;
+
+ return m->GetBitmapForSequence( sequence );
+}
+
+void IFaceposerModels::RecreateAllAnimationBitmaps( int modelindex )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return;
+
+ m->RecreateAllAnimationBitmaps();
+
+}
+
+void IFaceposerModels::RecreateAnimationBitmap( int modelindex, int sequence )
+{
+ CFacePoserModel *m = GetEntry( modelindex );
+ if ( !m )
+ return;
+
+ m->RecreateAnimationBitmap( sequence, true );
+}
+
+int IFaceposerModels::CountActiveSources()
+{
+ int count = 0;
+
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ StudioModel *model = m->GetModel();
+ if ( !model )
+ continue;
+
+ count += model->m_mouth.GetNumVoiceSources();
+ }
+
+ return count;
+}
+
+void IFaceposerModels::ClearModelTargets( bool force /*=false*/ )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ StudioModel *mdl = m->GetModel();
+ if ( !mdl )
+ continue;
+
+ mdl->ClearLookTargets();
+ }
+}
+
+void IFaceposerModels::SetSolveHeadTurn( int solve )
+{
+ int c = Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CFacePoserModel *m = GetEntry( i );
+ if ( !m )
+ continue;
+
+ StudioModel *mdl = m->GetModel();
+ if ( !mdl )
+ continue;
+
+ mdl->SetSolveHeadTurn( solve );
+ }
+}
+
+
+static IFaceposerModels g_ModelManager;
+IFaceposerModels *models = &g_ModelManager;
diff --git a/utils/hlfaceposer/faceposer_models.h b/utils/hlfaceposer/faceposer_models.h
new file mode 100644
index 0000000..2d3db4e
--- /dev/null
+++ b/utils/hlfaceposer/faceposer_models.h
@@ -0,0 +1,193 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef FACEPOSER_MODELS_H
+#define FACEPOSER_MODELS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class StudioModel;
+
+#include "mxbitmaptools.h"
+
+typedef unsigned int CRC32_t;
+
+class IFaceposerModels
+{
+public:
+ IFaceposerModels();
+ virtual ~IFaceposerModels();
+
+ virtual int Count( void ) const;
+ virtual char const *GetModelName( int index );
+ virtual char const *GetModelFileName( int index );
+ virtual int GetActiveModelIndex( void ) const;
+ virtual char const *GetActiveModelName( void );
+ virtual StudioModel *GetActiveStudioModel( void );
+ virtual void ForceActiveModelIndex( int index );
+ virtual void UnForceActiveModelIndex();
+ virtual int FindModelByFilename( char const *filename );
+
+ virtual int LoadModel( char const *filename );
+ virtual void FreeModel( int index );
+
+ virtual void CloseAllModels( void );
+
+ virtual StudioModel *GetStudioModel( int index );
+ virtual CStudioHdr *GetStudioHeader( int index );
+ virtual int GetIndexForStudioModel( StudioModel *model );
+
+ virtual int GetModelIndexForActor( char const *actorname );
+ virtual StudioModel *GetModelForActor( char const *actorname );
+
+ virtual char const *GetActorNameForModel( int modelindex );
+ virtual void SetActorNameForModel( int modelindex, char const *actorname );
+
+ virtual int CountVisibleModels( void );
+ virtual void ShowModelIn3DView( int modelindex, bool show );
+ virtual bool IsModelShownIn3DView( int modelindex );
+
+ virtual void SaveModelList( void );
+ virtual void LoadModelList( void );
+
+ virtual void ReleaseModels( void );
+ virtual void RestoreModels( void );
+
+ //virtual void RefreshModels( void );
+
+ virtual void CheckResetFlexes( void );
+ virtual void ClearOverlaysSequences( void );
+
+ virtual mxbitmapdata_t *GetBitmapForSequence( int modelindex, int sequence );
+
+ virtual void RecreateAllAnimationBitmaps( int modelindex );
+ virtual void RecreateAnimationBitmap( int modelindex, int sequence );
+
+ virtual void CreateNewBitmap( int modelindex, char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, class CExpression *pExpression, mxbitmapdata_t *bitmap );
+
+ virtual int CountActiveSources();
+
+ virtual void SetSolveHeadTurn( int solve );
+
+ virtual void ClearModelTargets( bool force = false );
+
+private:
+ class CFacePoserModel
+ {
+ public:
+ CFacePoserModel( char const *modelfile, StudioModel *model );
+ ~CFacePoserModel();
+
+ void LoadBitmaps();
+ void FreeBitmaps();
+ mxbitmapdata_t *GetBitmapForSequence( int sequence );
+
+ const char *GetBitmapChecksum( int sequence );
+ CRC32_t GetBitmapCRC( int sequence );
+ const char *GetBitmapFilename( int sequence );
+ void RecreateAllAnimationBitmaps();
+ void RecreateAnimationBitmap( int sequence, bool reconcile );
+
+
+ void SetActorName( char const *actorname )
+ {
+ strcpy( m_szActorName, actorname );
+ }
+
+ char const *GetActorName( void ) const
+ {
+ return m_szActorName;
+ }
+
+ StudioModel *GetModel( void ) const
+ {
+ return m_pModel;
+ }
+
+ char const *GetModelFileName( void ) const
+ {
+ return m_szModelFileName;
+ }
+
+ char const *GetShortModelName( void ) const
+ {
+ return m_szShortName;
+ }
+
+ void SetVisibleIn3DView( bool visible )
+ {
+ m_bVisibileIn3DView = visible;
+ }
+
+ bool GetVisibleIn3DView( void ) const
+ {
+ return m_bVisibileIn3DView;
+ }
+
+ // For material system purposes
+ void Release( void );
+ void Restore( void );
+
+ void Refresh( void )
+ {
+ // Forces a reload from disk
+ Release();
+ Restore();
+ }
+
+ void CreateNewBitmap( char const *pchBitmapFilename, int sequence, int nSnapShotSize, bool bZoomInOnFace, class CExpression *pExpression, mxbitmapdata_t *bitmap );
+
+ private:
+
+ void LoadBitmapForSequence( mxbitmapdata_t *bitmap, int sequence );
+
+ void ReconcileAnimationBitmaps();
+ void BuildValidChecksums( CUtlRBTree< CRC32_t > &tree );
+
+ enum
+ {
+ MAX_ACTOR_NAME = 64,
+ MAX_MODEL_FILE = 128,
+ MAX_SHORT_NAME = 32,
+ };
+
+ char m_szActorName[ MAX_ACTOR_NAME ];
+ char m_szModelFileName[ MAX_MODEL_FILE ];
+ char m_szShortName[ MAX_SHORT_NAME ];
+ StudioModel *m_pModel;
+ bool m_bVisibileIn3DView;
+
+ struct AnimBitmap
+ {
+ AnimBitmap()
+ {
+ needsload = false;
+ bitmap = 0;
+ }
+ bool needsload;
+ mxbitmapdata_t *bitmap;
+ };
+
+ CUtlVector< AnimBitmap * > m_AnimationBitmaps;
+ bool m_bFirstBitmapLoad;
+ };
+
+ CFacePoserModel *GetEntry( int index );
+
+ CUtlVector< CFacePoserModel * > m_Models;
+
+ int m_nLastRenderFrame;
+ int m_nForceModelIndex;
+};
+
+extern IFaceposerModels *models;
+
+void EnableStickySnapshotMode( void );
+void DisableStickySnapshotMode( void );
+
+#endif // FACEPOSER_MODELS_H
diff --git a/utils/hlfaceposer/faceposer_vgui.cpp b/utils/hlfaceposer/faceposer_vgui.cpp
new file mode 100644
index 0000000..742c166
--- /dev/null
+++ b/utils/hlfaceposer/faceposer_vgui.cpp
@@ -0,0 +1,140 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cbase.h"
+#include "faceposer_vgui.h"
+#include <vgui/IVGui.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include "vgui/IInput.h"
+#include <VGuiMatSurface/IMatSystemSurface.h>
+#include <matsys_controls/matsyscontrols.h>
+#include <dme_controls/dmecontrols.h>
+//#include "material.h"
+#include "vgui_controls/AnimationController.h"
+#include "inputsystem/iinputsystem.h"
+#include "VGuiWnd.h"
+
+extern CreateInterfaceFn g_Factory;
+
+//-----------------------------------------------------------------------------
+// Purpose: singleton accessor
+//-----------------------------------------------------------------------------
+static CFacePoserVGui s_FaceposerVGui;
+
+CFacePoserVGui *FaceposerVGui()
+{
+ return &s_FaceposerVGui;
+}
+
+CFacePoserVGui::CFacePoserVGui(void)
+{
+ m_pActiveWindow = NULL;
+ m_hMainWindow = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Setup the base vgui panels
+//-----------------------------------------------------------------------------
+bool CFacePoserVGui::Init( HWND hWindow )
+{
+ // initialize vgui_control interfaces
+ if (!vgui::VGui_InitInterfacesList( "FACEPOSER", &g_Factory, 1 ))
+ return false;
+
+// if ( !vgui::VGui_InitMatSysInterfacesList( "FACEPOSER", &g_Factory, 1 ) )
+// return false;
+
+ // All of the various tools .dlls expose GetVGuiControlsModuleName() to us to make sure we don't have communication across .dlls
+// if ( !vgui::VGui_InitDmeInterfacesList( "FACEPOSER", &g_Factory, 1 ) )
+// return false;
+
+ if ( !g_pMatSystemSurface )
+ return false;
+
+ // configuration settings
+ vgui::system()->SetUserConfigFile("faceposer.vdf", "EXECUTABLE_PATH");
+
+ // Are we trapping input?
+ g_pMatSystemSurface->EnableWindowsMessages( true );
+
+ // Need to be able to play sounds through vgui
+ // g_pMatSystemSurface->InstallPlaySoundFunc( VGui_PlaySound );
+
+ // load scheme
+ if (!vgui::scheme()->LoadSchemeFromFile("Resource/SourceScheme.res", "FacePoser"))
+ {
+ return false;
+ }
+
+ m_hMainWindow = hWindow;
+
+ // Start the App running
+ vgui::ivgui()->Start();
+ vgui::ivgui()->SetSleep(false);
+
+ return true;
+}
+
+void CFacePoserVGui::SetFocus( CVGuiWnd *pVGuiWnd )
+{
+ if ( pVGuiWnd == m_pActiveWindow )
+ return;
+
+ g_pInputSystem->PollInputState();
+ vgui::ivgui()->RunFrame();
+
+ g_pMatSystemSurface->AttachToWindow( NULL, false );
+ g_pInputSystem->DetachFromWindow( );
+
+ if ( pVGuiWnd )
+ {
+ HWND hWnd = (HWND)pVGuiWnd->GetParentWnd()->getHandle();
+
+ m_pActiveWindow = pVGuiWnd;
+ g_pInputSystem->AttachToWindow( hWnd );
+ g_pMatSystemSurface->AttachToWindow( hWnd, false );
+ vgui::ivgui()->ActivateContext( pVGuiWnd->GetVGuiContext() );
+ }
+ else
+ {
+ m_pActiveWindow = NULL;
+ vgui::ivgui()->ActivateContext( vgui::DEFAULT_VGUI_CONTEXT );
+ }
+}
+
+bool CFacePoserVGui::HasFocus( CVGuiWnd *pWnd )
+{
+ return m_pActiveWindow == pWnd;
+}
+
+void CFacePoserVGui::Simulate()
+{
+ // VPROF( "CFacePoserVGui::Simulate" );
+
+ if ( !IsInitialized() )
+ return;
+
+ g_pInputSystem->PollInputState();
+ vgui::ivgui()->RunFrame();
+
+ // run vgui animations
+ vgui::GetAnimationController()->UpdateAnimations( vgui::system()->GetCurrentTime() );
+}
+
+void CFacePoserVGui::Shutdown()
+{
+ // Give panels a chance to settle so things
+ // Marked for deletion will actually get deleted
+
+ if ( !IsInitialized() )
+ return;
+
+ g_pInputSystem->PollInputState();
+ vgui::ivgui()->RunFrame();
+
+ // stop the App running
+ vgui::ivgui()->Stop();
+}
+
+CFacePoserVGui::~CFacePoserVGui(void)
+{
+}
diff --git a/utils/hlfaceposer/faceposer_vgui.h b/utils/hlfaceposer/faceposer_vgui.h
new file mode 100644
index 0000000..f905fae
--- /dev/null
+++ b/utils/hlfaceposer/faceposer_vgui.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef FACEPOSER_VGUI_H
+#define FACEPOSER_VGUI_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class IMatSystemSurface;
+class CVGuiWnd;
+
+extern IMatSystemSurface *g_pMatSystemSurface;
+
+class CFacePoserVGui
+{
+public:
+ CFacePoserVGui(void);
+ ~CFacePoserVGui(void);
+
+ bool Init( HWND hWindow );
+ void Simulate();
+ void Shutdown();
+ bool HasFocus( CVGuiWnd *pWnd );
+ void SetFocus( CVGuiWnd *pWnd );
+ bool IsInitialized() { return m_hMainWindow != NULL; };
+
+
+protected:
+
+ HWND m_hMainWindow;
+ CVGuiWnd *m_pActiveWindow; // the VGUI window that has the focus
+};
+
+CFacePoserVGui *FaceposerVGui();
+
+#endif // FACEPOSER_VGUI_H
diff --git a/utils/hlfaceposer/faceposertoolwindow.cpp b/utils/hlfaceposer/faceposertoolwindow.cpp
new file mode 100644
index 0000000..7997a35
--- /dev/null
+++ b/utils/hlfaceposer/faceposertoolwindow.cpp
@@ -0,0 +1,746 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include "faceposertoolwindow.h"
+#include "utlvector.h"
+#include "tier1/strtools.h"
+#include "MDLViewer.h"
+#include "choreowidgetdrawhelper.h"
+#include "StudioModel.h"
+#include "faceposer_models.h"
+
+extern MDLViewer *g_MDLViewer;
+
+static CUtlVector< IFacePoserToolWindow * > g_Tools;
+IFacePoserToolWindow *IFacePoserToolWindow::s_pActiveTool = NULL;
+
+bool IFacePoserToolWindow::s_bToolsCanDraw;
+static CUtlVector< IFacePoserToolWindow * > s_NeedRedraw;
+
+
+IFacePoserToolWindow::IFacePoserToolWindow( char const *toolname, char const *displaynameroot )
+{
+ m_bAutoProcess = false;
+ m_bUseForMainWindowTitle = false;
+ SetToolName( toolname );
+ m_szPrefix[0]=0;
+ m_szSuffix[0]=0;
+
+ SetDisplayNameRoot( displaynameroot );
+
+ g_Tools.AddToTail( this );
+
+ m_nToolFrameCount = 0;
+}
+
+mxWindow *IFacePoserToolWindow::GetMxWindow( void )
+{
+ return dynamic_cast< mxWindow * >( this );
+}
+
+IFacePoserToolWindow *IFacePoserToolWindow::GetActiveTool( void )
+{
+ if ( s_pActiveTool )
+ return s_pActiveTool;
+
+ if ( GetToolCount() > 0 )
+ return GetTool( 0 );
+
+ return NULL;
+}
+
+void IFacePoserToolWindow::SetActiveTool( IFacePoserToolWindow *tool )
+{
+ if ( tool != s_pActiveTool && s_pActiveTool )
+ {
+ InvalidateRect( (HWND)s_pActiveTool->GetMxWindow()->getHandle(), NULL, TRUE );
+ InvalidateRect( (HWND)tool->GetMxWindow()->getHandle(), NULL, TRUE );
+ }
+ s_pActiveTool = tool;
+}
+
+void IFacePoserToolWindow::Think( float dt )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetToolName( char const *name )
+{
+ Q_strncpy( m_szToolName, name, sizeof( m_szToolName ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *IFacePoserToolWindow::GetToolName( void ) const
+{
+ return m_szToolName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetDisplayNameRoot( char const *name )
+{
+ Q_snprintf( m_szDisplayRoot, sizeof( m_szDisplayRoot ), "%s", name );
+ ComputeNewTitle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *IFacePoserToolWindow::GetDisplayNameRoot( void ) const
+{
+ return m_szDisplayRoot;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *suffix -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetSuffix( char const *suffix )
+{
+ Q_snprintf( m_szSuffix, sizeof( m_szSuffix ), "%s", suffix );
+ ComputeNewTitle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *prefix -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetPrefix( char const *prefix )
+{
+ Q_snprintf( m_szPrefix, sizeof( m_szPrefix ), "%s", prefix );
+ ComputeNewTitle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *IFacePoserToolWindow::GetWindowTitle( void ) const
+{
+ return m_szWindowTitle;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : use -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetUseForMainWindowTitle( bool use )
+{
+ m_bUseForMainWindowTitle = use;
+ if ( use )
+ {
+ g_MDLViewer->setLabel( m_szWindowTitle );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::ComputeNewTitle( void )
+{
+ Q_snprintf( m_szWindowTitle, sizeof( m_szWindowTitle ), "%s%s%s", m_szPrefix, m_szDisplayRoot, m_szSuffix );
+ if ( GetMxWindow() )
+ {
+ GetMxWindow()->setLabel( m_szWindowTitle );
+ }
+ if ( !m_bUseForMainWindowTitle )
+ return;
+
+ g_MDLViewer->setLabel( m_szWindowTitle );
+}
+
+IFacePoserToolWindow::~IFacePoserToolWindow( void )
+{
+ g_Tools.FindAndRemove( this );
+}
+
+struct ToolTranslate
+{
+ char const *toolname;
+ float xfrac;
+ float yfrac;
+ float wfrac;
+ float hfrac;
+ bool locked;
+};
+
+static ToolTranslate s_ToolTranslate[]=
+{
+ { "3D View", 0.0, 0.0, 0.4, 0.5, false },
+ { "ControlPanel", 0.4, 0.0, 0.2, 0.25, false },
+ { "FlexPanel", 0.6, 0.0, 0.4, 0.25, false },
+ { "RampTool", 0.4, 0.25, 0.6, 0.25, false },
+ { "CChoreoView", 0.0, 0.5, 1.0, 0.45, false },
+// { "Status Window", 0.0, 0.85, 1.0, 0.15, false },
+};
+
+static bool TranslateToolPos( char const *toolname, int workspacew, int workspaceh, int& x, int& y, int &w, int &h, bool& locked )
+{
+ int c = ARRAYSIZE( s_ToolTranslate );
+
+ for ( int i = 0; i < c; ++i )
+ {
+ ToolTranslate& tt = s_ToolTranslate[ i ];
+
+ if ( !Q_stricmp( toolname, tt.toolname ) )
+ {
+ x = (int)((float)workspacew * tt.xfrac + 0.5f );
+ y = (int)((float)workspaceh * tt.yfrac + 0.5f );
+ w = (int)((float)workspacew * tt.wfrac + 0.5f );
+ h = (int)((float)workspaceh * tt.hfrac + 0.5f );
+ locked = tt.locked;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int s_nToolCount = 0;
+
+void IFacePoserToolWindow::LoadPosition( void )
+{
+ bool visible;
+ bool locked;
+ bool zoomed;
+ int x, y, w, h;
+
+ FacePoser_LoadWindowPositions( GetToolName(), visible, x, y, w, h, locked, zoomed );
+
+ if ( w == 0 || h == 0 )
+ {
+ int idx = g_Tools.Find( this );
+ Assert( idx != g_Tools.InvalidIndex() );
+ if ( idx == 0 )
+ {
+ s_nToolCount = 0;
+ }
+
+ zoomed = false;
+ locked = false;
+ visible = true;
+
+ // Just do a simple tiling
+ w = g_MDLViewer->w2() * 0.5;
+ h = g_MDLViewer->h2() * 0.5;
+
+ x = g_MDLViewer->w2() * 0.25f + s_nToolCount * 20;
+ y = s_nToolCount * 20;
+
+ bool translated = TranslateToolPos
+ (
+ GetToolName(),
+ g_MDLViewer->w2(),
+ g_MDLViewer->h2(),
+ x,
+ y,
+ w,
+ h,
+ locked
+ );
+ if ( !translated )
+ {
+ ++s_nToolCount;
+ visible = false;
+ }
+ }
+
+ GetMxWindow()->setBounds( x, y, w, h );
+ if ( locked ^ IsLocked() )
+ {
+ ToggleLockedState();
+ }
+ GetMxWindow()->setVisible( visible );
+}
+
+void IFacePoserToolWindow::SavePosition( void )
+{
+ bool visible;
+ int xpos, ypos, width, height;
+
+ visible = GetMxWindow()->isVisible();
+ xpos = GetMxWindow()->x();
+ ypos = GetMxWindow()->y();
+ width = GetMxWindow()->w();
+ height = GetMxWindow()->h();
+
+ // xpos and ypos are screen space
+ POINT pt;
+ pt.x = xpos;
+ pt.y = ypos;
+
+ // Convert from screen space to relative to client area of parent window so
+ // the setBounds == MoveWindow call will offset to the same location
+ if ( GetMxWindow()->getParent() )
+ {
+ ScreenToClient( (HWND)GetMxWindow()->getParent()->getHandle(), &pt );
+ xpos = (short)pt.x;
+ ypos = (short)pt.y;
+ }
+
+ FacePoser_SaveWindowPositions( GetToolName(), visible, xpos, ypos, width, height, IsLocked(), false );
+}
+
+int IFacePoserToolWindow::GetToolCount( void )
+{
+ return g_Tools.Count();
+}
+
+IFacePoserToolWindow *IFacePoserToolWindow::GetTool( int index )
+{
+ return g_Tools[ index ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::InitTools( void )
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+
+ FacePoser_MakeToolWindow( tool->GetMxWindow(), true );
+ tool->GetMxWindow()->setLabel( tool->GetWindowTitle() );
+ }
+}
+
+void IFacePoserToolWindow::ShutdownTools( void )
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+ tool->Shutdown();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::ToolThink( float dt )
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+ tool->Think( dt );
+ }
+
+ // Don't self animate, all animation driven by thinking of various tools now
+
+ if ( !ShouldAutoProcess() )
+ {
+ c = models->Count();
+ for ( i = 0; i < c; i++ )
+ {
+ StudioModel *m = models->GetStudioModel( i );
+ if ( m )
+ {
+ m->AdvanceFrame ( dt );
+ }
+ }
+ }
+}
+
+bool IFacePoserToolWindow::IsLocked( void )
+{
+ mxWindow *w = GetMxWindow();
+ if ( !w )
+ return false;
+
+ return !FacePoser_HasWindowStyle( w, WS_SYSMENU );
+}
+
+void IFacePoserToolWindow::ToggleLockedState( void )
+{
+ mxWindow *w = GetMxWindow();
+ if ( !w )
+ return;
+
+ bool visible = w->isVisible();
+
+ bool islocked = IsLocked();
+ if ( islocked )
+ {
+ FacePoser_MakeToolWindow( w, true );
+ }
+ else
+ {
+ FacePoser_RemoveWindowStyle( w, WS_OVERLAPPEDWINDOW );
+ FacePoser_AddWindowExStyle( w, WS_EX_OVERLAPPEDWINDOW );
+ }
+
+ w->setVisible( false );
+
+ // If visible, force it to redraw, etc.
+ if ( visible )
+ {
+ w->setVisible( true );
+ }
+}
+
+#define LOCK_INSET 2
+#define LOCK_SIZE 8
+
+void IFacePoserToolWindow:: GetLockRect( RECT& rc )
+{
+ mxWindow *w = GetMxWindow();
+ Assert( w );
+ if ( !w )
+ return;
+
+ GetCloseRect( rc );
+
+ OffsetRect( &rc, - ( LOCK_SIZE + 2 * LOCK_INSET ), 0 );
+}
+
+void IFacePoserToolWindow::GetCloseRect( RECT& rc )
+{
+ mxWindow *w = GetMxWindow();
+ Assert( w );
+ if ( !w )
+ return;
+
+ rc.right = w->w2() - LOCK_INSET;
+ rc.left = rc.right - LOCK_SIZE;
+ rc.top = LOCK_INSET;
+ rc.bottom = rc.top + LOCK_SIZE;
+}
+
+bool IFacePoserToolWindow::HandleToolEvent( mxEvent *event )
+{
+ bool handled = false;
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Close:
+ {
+ g_MDLViewer->UpdateWindowMenu();
+ handled = true;
+ }
+ break;
+ case mxEvent::ParentNotify:
+ {
+ mxWindow *w = GetMxWindow();
+ if ( w )
+ {
+ HWND wnd = (HWND)w->getHandle();
+ SetFocus( wnd );
+ SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+ SetActiveTool( this );
+ }
+ handled = true;
+ }
+ break;
+ case mxEvent::PosChanged:
+ {
+ SetActiveTool( this );
+ mxWindow *w = GetMxWindow();
+ if ( w )
+ {
+ SetFocus( (HWND)w->getHandle() );
+ }
+ handled = true;
+ }
+ break;
+ case mxEvent::MouseDown:
+ case mxEvent::MouseUp:
+ {
+ bool isup = event->event == mxEvent::MouseUp;
+
+ mxWindow *w = GetMxWindow();
+
+ if ( !isup && w )
+ {
+ SetFocus( (HWND)w->getHandle() );
+ SetWindowPos( (HWND)w->getHandle(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+ SetActiveTool( this );
+ }
+
+ if ( w && IsLocked() )
+ {
+ RECT captionRect;
+ captionRect.left = 0;
+ captionRect.right = w->w2();
+ captionRect.top = 0;
+ captionRect.bottom = GetCaptionHeight();
+
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+
+ if ( PtInRect( &captionRect, pt ) )
+ {
+ handled = !isup;
+
+ // Right button anywhere
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ if ( isup )
+ {
+ ToggleLockedState();
+ }
+ }
+
+ // Left button on lock icon
+ RECT lockRect, closeRect;
+ GetLockRect( lockRect );
+ GetCloseRect( closeRect );
+
+ if ( PtInRect( &lockRect, pt ) )
+ {
+ if ( isup )
+ {
+ ToggleLockedState();
+ }
+ }
+
+ if ( PtInRect( &closeRect, pt ) )
+ {
+ if ( isup )
+ {
+ w->setVisible( !w->isVisible() );
+ g_MDLViewer->UpdateWindowMenu();
+ }
+ }
+ }
+ }
+ }
+ break;
+ case mxEvent::NCMouseUp:
+ {
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ToggleLockedState();
+ handled = true;
+ }
+ }
+ break;
+
+ case mxEvent::NCMouseDown:
+ case mxEvent::Focus:
+ {
+ SetActiveTool( this );
+ // don't mark handled = true, do this passively
+ }
+ break;
+ }
+
+ return handled;
+}
+
+void IFacePoserToolWindow::HandleToolRedraw( CChoreoWidgetDrawHelper& helper )
+{
+ if ( !IsLocked() )
+ return;
+
+ mxWindow *w = GetMxWindow();
+ if ( !w )
+ return;
+
+ ++m_nToolFrameCount;
+
+ RECT lockRect, closeRect;
+ GetLockRect( lockRect );
+ GetCloseRect( closeRect );
+
+ RECT captionRect;
+ helper.GetClientRect( captionRect );
+ RECT rcClient = captionRect;
+ captionRect.bottom = captionRect.top + LOCK_SIZE + 2 * LOCK_INSET;
+
+ COLORREF textColor = GetSysColor( COLOR_MENUTEXT ); //GetSysColor( COLOR_INACTIVECAPTIONTEXT );
+
+ if ( IsActiveTool() )
+ {
+ helper.DrawFilledRect( GetSysColor( COLOR_ACTIVECAPTION ), captionRect );
+ }
+ else
+ {
+ helper.DrawFilledRect( GetSysColor( COLOR_INACTIVECAPTION ), captionRect );
+ }
+
+ captionRect.top += 1;
+
+ InflateRect( &captionRect, -LOCK_INSET, 0 );
+
+ helper.DrawColoredText( "Small Fonts", 9, FW_NORMAL, textColor, captionRect,
+ GetWindowTitle() );
+
+ //RECT rcFrame = captionRect;
+ //rcFrame.left = rcFrame.right - 50;
+ //rcFrame.right = rcFrame.left + 30;
+ // helper.DrawColoredText( "Small Fonts", 9, FW_NORMAL, textColor, rcFrame, va( "%i", m_nToolFrameCount ) );
+
+ lockRect.bottom++;
+ OffsetRect( &lockRect, 1, 1 );
+ helper.DrawColoredTextCharset( "Marlett", 8, FW_NORMAL, SYMBOL_CHARSET, textColor, lockRect, "v" );
+
+ closeRect.bottom++;
+ helper.DrawOutlinedRect( textColor, PS_SOLID, 1, closeRect );
+ OffsetRect( &closeRect, 1, 1 );
+ helper.DrawColoredTextCharset( "Marlett", 8, FW_NORMAL, SYMBOL_CHARSET, textColor, closeRect, "r" );
+
+ rcClient.top += captionRect.bottom;
+
+ helper.StartClipping( rcClient );
+}
+
+int IFacePoserToolWindow::GetCaptionHeight( void )
+{
+ if ( !IsLocked() )
+ return 0;
+
+ return LOCK_SIZE + 2 * LOCK_INSET;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : autoprocess -
+//-----------------------------------------------------------------------------
+void IFacePoserToolWindow::SetAutoProcess( bool autoprocess )
+{
+ m_bAutoProcess = autoprocess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IFacePoserToolWindow::GetAutoProcess( void ) const
+{
+ return m_bAutoProcess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IFacePoserToolWindow::IsActiveTool( void )
+{
+ if ( this == s_pActiveTool )
+ return true;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IFacePoserToolWindow::IsAnyToolScrubbing( void )
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+ if ( tool->IsScrubbing() )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IFacePoserToolWindow::IsAnyToolProcessing( void )
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+ if ( tool->IsProcessing() )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IFacePoserToolWindow::ShouldAutoProcess( void )
+{
+ IFacePoserToolWindow *tool = GetActiveTool();
+ if ( !tool )
+ return false;
+
+ return tool->GetAutoProcess();
+}
+
+void IFacePoserToolWindow::EnableToolRedraw( bool enabled )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ s_bToolsCanDraw = enabled;
+
+ if ( s_bToolsCanDraw )
+ {
+ int c = s_NeedRedraw.Count();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *tool = s_NeedRedraw[ i ];
+ tool->GetMxWindow()->redraw();
+ }
+
+ s_NeedRedraw.Purge();
+ }
+}
+
+bool IFacePoserToolWindow::ToolCanDraw()
+{
+ if ( !s_bToolsCanDraw )
+ {
+ if ( s_NeedRedraw.Find( this ) == s_NeedRedraw.InvalidIndex() )
+ {
+ s_NeedRedraw.AddToTail( this );
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+void IFacePoserToolWindow::OnModelChanged()
+{
+}
+
+void IFacePoserToolWindow::ModelChanged()
+{
+ int c = GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = GetTool( i );
+ tool->OnModelChanged();
+ }
+}
+
+
diff --git a/utils/hlfaceposer/faceposertoolwindow.h b/utils/hlfaceposer/faceposertoolwindow.h
new file mode 100644
index 0000000..ab4dd32
--- /dev/null
+++ b/utils/hlfaceposer/faceposertoolwindow.h
@@ -0,0 +1,108 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef FACEPOSERTOOLWINDOW_H
+#define FACEPOSERTOOLWINDOW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "mxtk/mxWindow.h"
+
+class CChoreoWidgetDrawHelper;
+
+class IFacePoserToolWindow
+{
+public:
+ IFacePoserToolWindow( char const *toolname, char const *displaynameroot );
+ virtual ~IFacePoserToolWindow( void );
+
+ virtual mxWindow *GetMxWindow( void );
+ virtual void Shutdown() { }
+
+ virtual void Think( float dt );
+ virtual bool IsScrubbing( void ) const { return false; }
+ virtual bool IsProcessing( void ) { return false; }
+
+ bool IsActiveTool( void );
+
+ virtual bool IsLocked( void );
+ virtual bool HandleToolEvent( mxEvent *event );
+ virtual void HandleToolRedraw( CChoreoWidgetDrawHelper& helper );
+ virtual int GetCaptionHeight( void );
+ void ToggleLockedState( void );
+
+ void LoadPosition( void );
+ void SavePosition( void );
+
+ char const *GetToolName( void ) const;
+ char const *GetWindowTitle( void ) const;
+ char const *GetDisplayNameRoot( void ) const;
+
+ void SetDisplayNameRoot( char const *name );
+ void SetSuffix( char const *suffix );
+ void SetPrefix( char const *prefix );
+
+ void SetUseForMainWindowTitle( bool use );
+
+ void SetAutoProcess( bool autoprocess );
+ bool GetAutoProcess( void ) const;
+
+ virtual void OnModelChanged();
+
+ static int GetToolCount( void );
+ static IFacePoserToolWindow *GetTool( int index );
+
+ static IFacePoserToolWindow *GetActiveTool( void );
+ static void SetActiveTool( IFacePoserToolWindow *tool );
+ static IFacePoserToolWindow *s_pActiveTool;
+ static void ToolThink( float dt );
+ static void ModelChanged();
+ static bool IsAnyToolScrubbing( void );
+ static bool IsAnyToolProcessing( void );
+
+ static bool ShouldAutoProcess( void );
+
+ static void InitTools( void );
+ static void ShutdownTools( void );
+
+ static void EnableToolRedraw( bool enabled );
+ static bool s_bToolsCanDraw;
+
+ bool ToolCanDraw( void );
+
+private:
+ void GetLockRect( RECT& rc );
+ void GetCloseRect( RECT& rc );
+
+ void ComputeNewTitle( void );
+
+ void SetToolName( char const *name );
+
+ enum
+ {
+ MAX_TOOL_NAME = 128,
+ PREFIX_LENGTH = 32,
+ SUFFIX_LENGTH = 128,
+ };
+
+ char m_szToolName[ MAX_TOOL_NAME ];
+ char m_szDisplayRoot[ MAX_TOOL_NAME ];
+ char m_szPrefix[ PREFIX_LENGTH ];
+ char m_szSuffix[ SUFFIX_LENGTH ];
+
+ char m_szWindowTitle[ MAX_TOOL_NAME + PREFIX_LENGTH + PREFIX_LENGTH ];
+
+ bool m_bUseForMainWindowTitle;
+
+ bool m_bAutoProcess;
+
+ int m_nToolFrameCount;
+};
+
+#endif // FACEPOSERTOOLWINDOW_H
diff --git a/utils/hlfaceposer/faceposerworkspace.cpp b/utils/hlfaceposer/faceposerworkspace.cpp
new file mode 100644
index 0000000..75b8137
--- /dev/null
+++ b/utils/hlfaceposer/faceposerworkspace.cpp
@@ -0,0 +1,258 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <windows.h>
+#include <stdio.h>
+#include "tier1/strtools.h"
+#include "ifaceposerworkspace.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWorkspaceFiles : public IWorkspaceFiles
+{
+public:
+ CWorkspaceFiles( void );
+ ~CWorkspaceFiles( void );
+
+ virtual void Init( char const *pchShortName );
+
+ // Restore
+ int GetNumStoredFiles( int type );
+ const char *GetStoredFile( int type, int number );
+
+ // Save
+ void StartStoringFiles( int type );
+ void FinishStoringFiles( int type );
+ void StoreFile( int type, const char *filename );
+
+private:
+ static const char *NameForType( int type );
+ static int TypeForName( const char *name );
+
+ LONG CreateWorkspaceKey( char const *pchGameName, PHKEY phKey );
+ bool ReadInt( const char *szSubKey, int *value );
+ bool WriteInt( const char *szSubKey, int value );
+ bool ReadString( const char *szSubKey, char *value, int bufferlen );
+ bool WriteString( const char *szSubKey, const char *value );
+
+ HKEY m_hKeyMain;
+
+ int m_nStoredFiles[ NUM_FILE_TYPES ];
+};
+
+static CWorkspaceFiles g_WorkspaceFiles;
+IWorkspaceFiles *workspacefiles = ( IWorkspaceFiles * )&g_WorkspaceFiles;
+
+CWorkspaceFiles::CWorkspaceFiles( void ) :
+ m_hKeyMain( (HKEY)0 )
+{
+ memset( m_nStoredFiles, 0, sizeof( m_nStoredFiles ) );
+}
+
+CWorkspaceFiles::~CWorkspaceFiles( void )
+{
+ if ( (HKEY)0 != m_hKeyMain )
+ {
+ RegCloseKey( m_hKeyMain );
+ }
+}
+
+void CWorkspaceFiles::Init( char const *pchShortName )
+{
+ CreateWorkspaceKey( pchShortName, &m_hKeyMain );
+}
+
+const char *CWorkspaceFiles::NameForType( int type )
+{
+ switch ( type )
+ {
+ case EXPRESSION:
+ return "expressionfiles";
+ case CHOREODATA:
+ return "choreodatafiles";
+ case MODELDATA:
+ return "modelfiles";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+int CWorkspaceFiles::TypeForName( const char *name )
+{
+ if ( !Q_stricmp( name, "expressionfiles" ) )
+ {
+ return EXPRESSION;
+ }
+ else if ( !Q_stricmp( name, "choreodatafiles" ) )
+ {
+ return CHOREODATA;
+ }
+ else if ( !Q_stricmp( name, "modelfiles" ) )
+ {
+ return MODELDATA;
+ }
+ return -1;
+}
+
+
+int CWorkspaceFiles::GetNumStoredFiles( int type )
+{
+ char szKeyName[ 256 ];
+ Q_snprintf( szKeyName, sizeof( szKeyName ), "%s\\total", NameForType( type ) );
+
+ int num = 0;
+ ReadInt( szKeyName, &num );
+ return num;
+}
+
+const char *CWorkspaceFiles::GetStoredFile( int type, int number )
+{
+ char szKeyName[ 256 ];
+ sprintf( szKeyName, "%s\\%04i", NameForType( type ), number );
+
+ static char filename[ 256 ];
+ filename[ 0 ] = 0;
+ ReadString( szKeyName, filename, 256 );
+ return filename;
+}
+
+void CWorkspaceFiles::StartStoringFiles( int type )
+{
+ m_nStoredFiles[ type ] = 0;
+}
+
+void CWorkspaceFiles::FinishStoringFiles( int type )
+{
+ char szKeyName[ 256 ];
+ sprintf( szKeyName, "%s\\total", NameForType( type ) );
+
+ WriteInt( szKeyName, m_nStoredFiles[ type ] );
+}
+
+void CWorkspaceFiles::StoreFile( int type, const char *filename )
+{
+ char szKeyName[ 256 ];
+ sprintf( szKeyName, "%s\\%04i", NameForType( type ), m_nStoredFiles[ type ]++ );
+
+ WriteString( szKeyName, filename );
+}
+
+LONG CWorkspaceFiles::CreateWorkspaceKey( char const *pchGameName, PHKEY phKey )
+{
+ DWORD disp;
+
+ char sz[ 512 ];
+ Q_snprintf( sz, sizeof( sz ), "Software\\Valve\\faceposer\\workspace\\%s", pchGameName );
+
+ return RegCreateKeyEx(
+ HKEY_CURRENT_USER, // handle of open key
+ sz, // address of name of subkey to open
+ 0, // DWORD ulOptions, // reserved
+ NULL, // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ phKey, // Key we are creating
+ &disp ); // Type of creation
+}
+
+bool CWorkspaceFiles::ReadInt( const char *szSubKey, int *value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( DWORD );
+
+ lResult = RegQueryValueEx(
+ m_hKeyMain, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)value, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (dwType != REG_DWORD)
+ return false;
+
+ return true;
+}
+
+
+bool CWorkspaceFiles::WriteInt( const char *szSubKey, int value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( DWORD );
+
+ lResult = RegSetValueEx(
+ m_hKeyMain, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_DWORD, // type buffer
+ (LPBYTE)&value, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+bool CWorkspaceFiles::ReadString( const char *szSubKey, char *value, int buffersize )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = buffersize;
+
+ lResult = RegQueryValueEx(
+ m_hKeyMain, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)value, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (dwType != REG_SZ)
+ return false;
+
+ return true;
+}
+
+
+bool CWorkspaceFiles::WriteString( const char *szSubKey, const char *value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwSize; // Size of element data
+
+ dwSize = strlen( value ) + 1;
+
+ lResult = RegSetValueEx(
+ m_hKeyMain, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_SZ, // type buffer
+ (LPBYTE)value, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/fileloaderthread.cpp b/utils/hlfaceposer/fileloaderthread.cpp
new file mode 100644
index 0000000..df70e31
--- /dev/null
+++ b/utils/hlfaceposer/fileloaderthread.cpp
@@ -0,0 +1,422 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+#include "sentence.h"
+#include "wavefile.h"
+#include "tier2/riff.h"
+#include "filesystem.h"
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include "IFileLoader.h"
+
+bool SceneManager_LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io );
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the RIFF i/o interface on stdio
+//-----------------------------------------------------------------------------
+class ThreadIOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName )
+ {
+ char filename[ 512 ];
+ // POSSIBLE BUG: THIS MIGHT NOT BE THREAD SAFE!!!
+ filesystem->RelativePathToFullPath( pFileName, "GAME", filename, sizeof( filename ) );
+ return (int)_open( filename, _O_BINARY | _O_RDONLY );
+ }
+
+ int read( void *pOutput, int size, int file )
+ {
+ if ( !file )
+ return 0;
+
+ return _read( file, pOutput, size );
+ }
+
+ void seek( int file, int pos )
+ {
+ if ( !file )
+ return;
+
+ _lseek( file, pos, SEEK_SET );
+ }
+
+ unsigned int tell( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return _tell( file );
+ }
+
+ unsigned int size( int file )
+ {
+ if ( !file )
+ return 0;
+
+ long curpos = tell( file );
+ _lseek( file, 0, SEEK_END );
+ int s = tell( file );
+ _lseek( file, curpos, SEEK_SET );
+
+ return s;
+ }
+
+ void close( int file )
+ {
+ if ( !file )
+ return;
+
+ _close( file );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: All wavefile I/O occurs on a thread
+//-----------------------------------------------------------------------------
+class CFileLoaderThread : public IFileLoader
+{
+public:
+ struct SentenceRequest
+ {
+ SentenceRequest()
+ {
+ filename[ 0 ] = 0;
+ sentence.Reset();
+ wavefile = NULL;
+ valid = false;
+ }
+
+ bool valid;
+ char filename[ 256 ];
+ CSentence sentence;
+
+ CWaveFile *wavefile;
+ };
+
+ // Construction
+ CFileLoaderThread( void );
+ virtual ~CFileLoaderThread( void );
+
+ // Sockets add/remove themselves via their constructor
+ virtual void AddWaveFilesToThread( CUtlVector< CWaveFile * >& wavefiles );
+
+ // Lock changes to wavefile list, etc.
+ virtual void Lock( void );
+ // Unlock wavefile list, etc.
+ virtual void Unlock( void );
+
+ // Retrieve handle to shutdown event
+ virtual HANDLE GetShutdownHandle( void );
+
+ // Caller should call lock before accessing any of these methods and unlock afterwards!!!
+ virtual int ProcessCompleted();
+
+ int DoThreadWork();
+
+ virtual void Start();
+
+ virtual int GetPendingLoadCount();
+private:
+ // Critical section used for synchronizing access to wavefile list
+ CRITICAL_SECTION cs;
+ CRITICAL_SECTION m_CountCS;
+
+ // List of wavefiles we are listening on
+ CUtlVector< SentenceRequest * > m_FileList;
+
+ CUtlVector< SentenceRequest * > m_Pending;
+ CUtlVector< SentenceRequest * > m_Completed;
+ // Thread handle
+ HANDLE m_hThread;
+ // Thread id
+ DWORD m_nThreadId;
+ // Event to set when we want to tell the thread to shut itself down
+ HANDLE m_hShutdown;
+
+ ThreadIOReadBinary m_ThreadIO;
+ bool m_bLocked;
+
+ int m_nTotalAdds;
+ int m_nTotalPending;
+ int m_nTotalProcessed;
+ int m_nTotalCompleted;
+
+ HANDLE m_hNewItems;
+};
+
+// Singleton handler
+static CFileLoaderThread g_WaveLoader;
+extern IFileLoader *fileloader = &g_WaveLoader;
+
+int CFileLoaderThread::DoThreadWork()
+{
+ int i;
+ // Check for shutdown event
+ if ( WAIT_OBJECT_0 == WaitForSingleObject( GetShutdownHandle(), 0 ) )
+ {
+ return 0;
+ }
+
+ // No changes to list right now
+ Lock();
+ // Move new items to work list
+ int newItems = m_FileList.Count();
+ for ( i = 0; i < newItems; i++ )
+ {
+ // Move to pending and issue async i/o calls
+ m_Pending.AddToHead( m_FileList[ i ] );
+
+ EnterCriticalSection( &m_CountCS );
+ m_nTotalPending++;
+ LeaveCriticalSection( &m_CountCS );
+ }
+ m_FileList.RemoveAll();
+ // Done adding new work items
+ Unlock();
+
+ int remaining = m_Pending.Count();
+ if ( !remaining )
+ return 1;
+
+ int workitems = remaining; // min( remaining, 1000 );
+
+ CUtlVector< SentenceRequest * > transfer;
+
+ for ( i = 0; i < workitems; i++ )
+ {
+ SentenceRequest *r = m_Pending[ 0 ];
+ m_Pending.Remove( 0 );
+
+ transfer.AddToTail( r );
+
+ // Do the work
+ EnterCriticalSection( &m_CountCS );
+ m_nTotalProcessed++;
+ LeaveCriticalSection( &m_CountCS );
+
+ Lock();
+ bool load = !r->wavefile->HasLoadedSentenceInfo();
+ Unlock();
+
+ if ( load )
+ {
+ r->valid = SceneManager_LoadSentenceFromWavFileUsingIO( r->filename, r->sentence, m_ThreadIO );
+ }
+ else
+ {
+ r->valid = true;
+ }
+
+ if ( WaitForSingleObject( m_hNewItems, 0 ) == WAIT_OBJECT_0 )
+ {
+ ResetEvent( m_hNewItems );
+ break;
+ }
+ }
+
+ // Now move to completed list
+ Lock();
+ int c = transfer.Count();
+
+ for ( i = 0; i < c; ++i )
+ {
+ SentenceRequest *r = transfer[ i ];
+ if ( r->valid )
+ {
+
+ m_nTotalCompleted++;
+
+
+ m_Completed.AddToTail( r );
+ }
+ else
+ {
+ delete r;
+ }
+ }
+ Unlock();
+ return 1;
+}
+
+int CFileLoaderThread::ProcessCompleted()
+{
+ Lock();
+ int c = m_Completed.Count();
+ for ( int i = c - 1; i >= 0 ; i-- )
+ {
+ SentenceRequest *r = m_Completed[ i ];
+
+ if ( !r->wavefile->HasLoadedSentenceInfo() )
+ {
+ r->wavefile->SetThreadLoadedSentence( r->sentence );
+ }
+
+ delete r;
+ }
+ m_Completed.RemoveAll();
+ Unlock();
+ return c;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Main winsock processing thread
+// Input : threadobject -
+// Output : static DWORD WINAPI
+//-----------------------------------------------------------------------------
+static DWORD WINAPI FileLoaderThreadFunc( LPVOID threadobject )
+{
+ // Get pointer to CFileLoaderThread object
+ CFileLoaderThread *wavefilethread = ( CFileLoaderThread * )threadobject;
+ Assert( wavefilethread );
+ if ( !wavefilethread )
+ {
+ return 0;
+ }
+
+ // Keep looking for data until shutdown event is triggered
+ while ( 1 )
+ {
+ if( !wavefilethread->DoThreadWork() )
+ break;
+
+ // Yield a small bit of time to main app
+ Sleep( 10 );
+ }
+
+ ExitThread( 0 );
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Construction
+//-----------------------------------------------------------------------------
+CFileLoaderThread::CFileLoaderThread( void )
+{
+ m_nTotalAdds = 0;
+ m_nTotalProcessed = 0;
+ m_nTotalCompleted = 0;
+ m_nTotalPending = 0;
+
+ m_bLocked = false;
+
+ InitializeCriticalSection( &cs );
+ InitializeCriticalSection( &m_CountCS );
+
+ m_hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL );
+ Assert( m_hShutdown );
+
+ m_hThread = NULL;
+
+ m_hNewItems = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+ Start();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFileLoaderThread::Start()
+{
+ m_hThread = CreateThread( NULL, 0, FileLoaderThreadFunc, (void *)this, 0, &m_nThreadId );
+ Assert( m_hThread );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CFileLoaderThread::~CFileLoaderThread( void )
+{
+ Lock();
+ {
+ SetEvent( m_hShutdown );
+ Sleep( 2 );
+ TerminateThread( m_hThread, 0 );
+ }
+ Unlock();
+
+ // Kill the wavefile
+//!! need to validate this line
+// Assert( !m_FileList );
+
+ CloseHandle( m_hThread );
+
+ CloseHandle( m_hShutdown );
+
+ DeleteCriticalSection( &cs );
+ DeleteCriticalSection( &m_CountCS );
+
+ CloseHandle( m_hNewItems );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns handle of shutdown event
+// Output : HANDLE
+//-----------------------------------------------------------------------------
+HANDLE CFileLoaderThread::GetShutdownHandle( void )
+{
+ return m_hShutdown;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Locks object and adds wavefile to thread
+// Input : *wavefile -
+//-----------------------------------------------------------------------------
+void CFileLoaderThread::AddWaveFilesToThread( CUtlVector< CWaveFile * >& wavefiles )
+{
+ Lock();
+ int c = wavefiles.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ SentenceRequest *request = new SentenceRequest;
+ request->wavefile = wavefiles[ i ];
+ Q_strncpy( request->filename, request->wavefile->GetFileName(), sizeof( request->filename ) );
+
+ m_FileList.AddToTail( request );
+
+ m_nTotalAdds++;
+ }
+
+ SetEvent( m_hNewItems );
+ Unlock();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFileLoaderThread::Lock( void )
+{
+ EnterCriticalSection( &cs );
+ Assert( !m_bLocked );
+ m_bLocked = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFileLoaderThread::Unlock( void )
+{
+ Assert( m_bLocked );
+ m_bLocked = false;
+ LeaveCriticalSection( &cs );
+}
+
+int CFileLoaderThread::GetPendingLoadCount()
+{
+ int iret = 0;
+
+ EnterCriticalSection( &m_CountCS );
+
+ iret = m_nTotalPending - m_nTotalProcessed;
+
+ LeaveCriticalSection( &m_CountCS );
+
+ return iret;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/flexpanel.cpp b/utils/hlfaceposer/flexpanel.cpp
new file mode 100644
index 0000000..c268d51
--- /dev/null
+++ b/utils/hlfaceposer/flexpanel.cpp
@@ -0,0 +1,1092 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+
+#include "hlfaceposer.h"
+#include "FlexPanel.h"
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "MatSysWin.h"
+#include "ControlPanel.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include <mxtk/mxBmp.h>
+
+#include "mxbitmapwindow.h"
+#include "mxExpressionTray.h"
+#include "expressions.h"
+#include "expressiontool.h"
+#include "filesystem.h"
+#include "mdlviewer.h"
+#include "ExpressionProperties.h"
+#include "expclass.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoview.h"
+#include "choreoscene.h"
+#include "mxExpressionSlider.h"
+#include "faceposer_models.h"
+
+LocalFlexController_t FindFlexControllerIndexByName( StudioModel *model, char const *searchname );
+char const *GetGlobalFlexControllerName( int index );
+
+extern char g_appTitle[];
+
+#define LINE_HEIGHT 20
+
+#define FLEXSLIDER_INVALID_INDEX -1
+
+FlexPanel *g_pFlexPanel = 0;
+
+void FlexPanel::PositionControls( int width, int height )
+{
+ int buttonwidth = 80;
+ int buttonx = 3;
+ int row = height - 18;
+ int buttonheight = 18;
+
+ btnResetSliders->setBounds( buttonx, row, buttonwidth, buttonheight );
+
+ buttonx += buttonwidth + 5;
+ btnCopyToSliders->setBounds( buttonx, row, buttonwidth, buttonheight );
+
+ buttonx += buttonwidth + 5;
+ buttonwidth = 100;
+ btnCopyFromSliders->setBounds( buttonx, row, buttonwidth, buttonheight );
+
+ buttonx += buttonwidth + 5;
+ buttonwidth = 100;
+
+ btnMenu->setBounds( buttonx, row, buttonwidth, buttonheight );
+}
+
+FlexPanel::FlexPanel (mxWindow *parent)
+: IFacePoserToolWindow( "FlexPanel", "Flex Sliders" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ m_nViewableFlexControllerCount = 0;
+
+ m_bNewExpressionMode = true;
+
+ btnResetSliders = new mxButton( this, 0, 0, 100, 20, "Zero Sliders", IDC_EXPRESSIONRESET );
+
+ btnCopyToSliders = new mxButton( this, 0, 0, 100, 20, "Get Tracks", IDC_COPY_TO_FLEX );
+ btnCopyFromSliders = new mxButton( this, 0, 0, 100, 20, "Make Keyframe", IDC_COPY_FROM_FLEX );
+ btnMenu = new mxButton( this, 0, 0, 100, 20, "Menu", IDC_FP_MENU );
+
+ mxWindow *wFlex = this;
+ for (int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++)
+ {
+ int w = 5; // (i / 4) * 156 + 5;
+ int h = i * LINE_HEIGHT + 5; // (i % 4) * 20 + 5;
+
+ slFlexScale[i] = new mxExpressionSlider (wFlex, w, h, 220, LINE_HEIGHT, IDC_FLEXSCALE + i);
+ }
+
+ slScrollbar = new mxScrollbar( wFlex, 0, 0, 18, 100, IDC_FLEXSCROLL, mxScrollbar::Vertical );
+ slScrollbar->setRange( 0, GLOBAL_STUDIO_FLEX_CONTROL_COUNT * LINE_HEIGHT );
+ slScrollbar->setPagesize( 100 );
+}
+
+
+
+FlexPanel::~FlexPanel()
+{
+}
+
+void FlexPanel::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
+ HandleToolRedraw( helper );
+
+ BaseClass::redraw();
+}
+
+void FlexPanel::PositionSliders( int sboffset )
+{
+ int reservedheight = GetCaptionHeight() + 5 /*gap at top*/ + 1 * 20 /* space for buttons/edit controls*/;
+
+ int widthofslidercolumn = slFlexScale[ 0 ]->w() + 10;
+
+ int colsavailable = ( this->w2() - 20 /*scrollbar*/ - 10 /*left edge gap + right gap*/ ) / widthofslidercolumn;
+ // Need at least one column
+ colsavailable = max( colsavailable, 1 );
+
+ int rowsneeded = GLOBAL_STUDIO_FLEX_CONTROL_COUNT;
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ rowsneeded = m_nViewableFlexControllerCount;
+ }
+
+ int rowsvisible = ( this->h2() - reservedheight ) / LINE_HEIGHT;
+
+ int rowspercol = rowsvisible;
+
+ if ( rowsvisible * colsavailable < rowsneeded )
+ {
+ // Figure out how many controls should go in each available column
+ rowspercol = (rowsneeded + (colsavailable - 1)) / colsavailable;
+
+ slScrollbar->setPagesize( rowsvisible * LINE_HEIGHT );
+ slScrollbar->setRange( 0, rowspercol * LINE_HEIGHT );
+ }
+
+ int row = 0;
+ int col = 0;
+ for (int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++)
+ {
+ int x = 5 + col * widthofslidercolumn;
+ int y = row * LINE_HEIGHT + 5 + GetCaptionHeight() - sboffset; // (i % 4) * 20 + 5;
+
+ slFlexScale[ i ]->setBounds( x, y, slFlexScale[i]->w(), slFlexScale[i]->h() );
+
+ if ( i >= rowsneeded ||
+ ( y + LINE_HEIGHT - 5 > ( this->h2() - reservedheight ) ) )
+ {
+ slFlexScale[ i ]->setVisible( false );
+ }
+ else
+ {
+ slFlexScale[ i ]->setVisible( true );
+ }
+
+ row++;
+ if ( row >= rowspercol )
+ {
+ col++;
+ row = 0;
+ }
+ }
+}
+
+int FlexPanel::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Size:
+ {
+ int trueh = h2() - GetCaptionHeight();
+ PositionControls( w2(), h2() );
+ slScrollbar->setPagesize( trueh );
+ slScrollbar->setBounds( w2() - 18, GetCaptionHeight(), 18, trueh );
+ PositionSliders( 0 );
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_FLEXSCROLL:
+ {
+ if ( event->event == mxEvent::Action &&
+ event->modifiers == SB_THUMBTRACK)
+ {
+ int offset = event->height; // ((mxScrollbar *) event->widget)->getValue( );
+
+ slScrollbar->setValue( offset ); // if (offset > slScrollbar->getPagesize()
+
+ PositionSliders( offset );
+
+ IFacePoserToolWindow::SetActiveTool( this );
+ }
+ }
+ break;
+ case IDC_EXPRESSIONRESET:
+ {
+ ResetSliders( true, true );
+ IFacePoserToolWindow::SetActiveTool( this );
+ }
+ break;
+
+ case IDC_COPY_TO_FLEX:
+ {
+ g_pExpressionTool->OnCopyToFlex( g_pChoreoView->GetScene()->GetTime(), true );
+ }
+ break;
+ case IDC_COPY_FROM_FLEX:
+ {
+ g_pExpressionTool->OnCopyFromFlex( g_pChoreoView->GetScene()->GetTime(), false );
+ }
+ break;
+ case IDC_FP_UNCHECK_ALL:
+ {
+ OnSetAll( FP_STATE_UNCHECK );
+ }
+ break;
+ case IDC_FP_CHECK_ALL:
+ {
+ OnSetAll( FP_STATE_CHECK );
+ }
+ break;
+ case IDC_FP_INVERT:
+ {
+ OnSetAll( FP_STATE_INVERT );
+ }
+ break;
+ case IDC_FP_MENU:
+ {
+ OnMenu();
+ }
+ break;
+ }
+
+ if ( event->action >= IDC_FLEXSCALE && event->action < IDC_FLEXSCALE + GLOBAL_STUDIO_FLEX_CONTROL_COUNT)
+ {
+ iret = 1;
+
+ bool pushundo = false;
+
+ mxExpressionSlider *slider = ( mxExpressionSlider * )event->widget;
+ int barnumber = event->height;
+ int slidernum = ( event->action - IDC_FLEXSCALE );
+
+ float value = slider->getValue ( barnumber );
+ float influ = slider->getInfluence( );
+
+ switch( event->modifiers )
+ {
+ case SB_THUMBPOSITION:
+ case SB_THUMBTRACK:
+ break;
+ case SB_ENDSCROLL:
+ pushundo = true;
+ break;
+ }
+ int flex = LookupFlex( slidernum, barnumber );
+ int flex2 = LookupPairedFlex( flex );
+ float value2 = GetSlider( flex2 );
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ int index = active->GetSelectedExpression();
+ if ( pushundo && index != -1 )
+ {
+ CExpression *exp = active->GetExpression( index );
+ if ( exp )
+ {
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+ Assert( settings );
+
+ if ( settings[ flex ] != value ||
+ settings[ flex2 ] != value2 ||
+ weights[ flex ] != influ )
+ {
+ exp->PushUndoInformation();
+
+ active->SetDirty( true );
+
+ settings[ flex ] = value;
+ settings[ flex2 ] = value2;
+ weights[ flex ] = influ;
+ weights[ flex2 ] = influ;
+
+ exp->PushRedoInformation();
+
+ g_pExpressionTrayTool->redraw();
+ }
+ }
+ }
+ }
+
+ // FIXME: Needs to drive the current actor, not model
+
+ // Go from global to local indices
+ LocalFlexController_t localflex = FindFlexControllerIndexByName( models->GetActiveStudioModel(), GetGlobalFlexControllerName( flex ) );
+ if ( localflex >= 0 )
+ {
+ // Update the face
+ // FIXME: I'm not sure this is needed anymore....
+ models->GetActiveStudioModel()->SetFlexController( localflex, value * influ );
+ if (flex2 != flex)
+ {
+ LocalFlexController_t localflex2 = FindFlexControllerIndexByName( models->GetActiveStudioModel(), GetGlobalFlexControllerName( flex2 ) );
+ if ( localflex2 >= 0 )
+ {
+ models->GetActiveStudioModel()->SetFlexController( localflex2, value2 * influ );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+ }
+ }
+ else
+ {
+ Assert( 0 );
+ }
+
+ models->SetSolveHeadTurn( 1 );
+ IFacePoserToolWindow::SetActiveTool( this );
+ }
+ }
+ }
+
+ return iret;
+}
+
+void FlexPanel::initFlexes()
+{
+ m_nViewableFlexControllerCount = 0;
+
+ memset( nFlexSliderIndex, 0, sizeof( nFlexSliderIndex ) );
+ memset( nFlexSliderBarnum, 0, sizeof( nFlexSliderBarnum ) );
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if (hdr)
+ {
+ for (int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++)
+ {
+ slFlexScale[i]->setVisible( false );
+ slFlexScale[i]->setLabel( "" );
+ slFlexScale[i]->SetMode( false );
+ // init to invalid slider index
+ nFlexSliderIndex[i] = FLEXSLIDER_INVALID_INDEX;
+ }
+
+ // J is the slider number we're filling in
+ int j = 0;
+ for ( LocalFlexController_t k = LocalFlexController_t(0); k < hdr->numflexcontrollers(); k++ )
+ {
+ // Lookup global flex controller index
+ int controller = hdr->pFlexcontroller( k )->localToGlobal;
+ Assert( controller != -1 );
+
+ // Con_Printf( "%i Setting up %s global %i\n", k, hdr->pFlexcontroller(k)->pszName(), controller );
+
+ slFlexScale[j]->setLabel( hdr->pFlexcontroller(k)->pszName() );
+
+ if ( nFlexSliderIndex[controller] == FLEXSLIDER_INVALID_INDEX )
+ {
+ //Con_Printf( "Assigning bar %i to barnum %i of slider %s for controller %i\n",
+ // j, 0, hdr->pFlexcontroller(k)->pszName(), controller );
+
+ nFlexSliderIndex[controller] = j;
+ nFlexSliderBarnum[controller] = 0;
+ }
+ else
+ {
+ Assert( 0 );
+ }
+
+ if (hdr->pFlexcontroller(k)->min != hdr->pFlexcontroller(k)->max)
+ slFlexScale[j]->setRange( 0, hdr->pFlexcontroller(k)->min, hdr->pFlexcontroller(k)->max );
+
+ if (strncmp( "right_", hdr->pFlexcontroller(k)->pszName(), 6 ) == 0)
+ {
+ if (hdr->pFlexcontroller(k)->min != hdr->pFlexcontroller(k)->max)
+ slFlexScale[j]->setRange( 1, 0.0f, 1.0f );
+ slFlexScale[j]->setLabel( &hdr->pFlexcontroller(k)->pszName()[6] );
+
+ slFlexScale[j]->SetMode( true );
+ k++;
+ controller = hdr->pFlexcontroller( k )->localToGlobal;
+ Assert( controller != -1 );
+ if ( nFlexSliderIndex[controller] == FLEXSLIDER_INVALID_INDEX )
+ {
+ nFlexSliderIndex[controller] = j;
+ nFlexSliderBarnum[controller] = 1;
+
+ //Con_Printf( "Assigning stereo side of bar %i to barnum %i of slider %s for controller\n",
+ // j, 1, hdr->pFlexcontroller(k)->pszName(), controller );
+
+ }
+ else
+ {
+ Assert(0);
+ }
+ }
+ m_nViewableFlexControllerCount++;
+
+ slFlexScale[j]->setVisible( true );
+ slFlexScale[j]->redraw();
+
+ j++;
+ }
+ }
+
+ slScrollbar->setRange( 0, m_nViewableFlexControllerCount * LINE_HEIGHT + 5 );
+
+ int trueh = h2() - GetCaptionHeight();
+ PositionControls( w2(), h2() );
+ slScrollbar->setPagesize( trueh );
+ slScrollbar->setBounds( w2() - 18, GetCaptionHeight(), 18, trueh );
+ PositionSliders( 0 );
+}
+
+
+void FlexPanel::OnModelChanged()
+{
+ ResetSliders( true, false );
+ SetEvent( NULL );
+ redraw();
+}
+
+void FlexPanel::SetEvent( CChoreoEvent *event )
+{
+ bool bUpdateSliders = false;
+
+ if ( event != NULL )
+ {
+ CChoreoScene *scene = event->GetScene();
+ StudioModel *model = FindAssociatedModel( scene, event->GetActor() );
+
+ if (model == models->GetActiveStudioModel())
+ {
+ bUpdateSliders = true;
+ }
+ }
+
+ btnCopyToSliders->setEnabled( bUpdateSliders );
+ btnCopyFromSliders->setEnabled( bUpdateSliders );
+ return;
+}
+
+
+bool FlexPanel::IsValidSlider( int iFlexController ) const
+{
+ if ( nFlexSliderIndex[ iFlexController ] == FLEXSLIDER_INVALID_INDEX )
+ return false;
+
+ return true;
+}
+
+float
+FlexPanel::GetSlider( int iFlexController )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "GetSlider(%d) invalid controller index\n", iFlexController );
+ return 0.0f;
+ }
+
+ return slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->getValue( nFlexSliderBarnum[ iFlexController ] );
+}
+
+float FlexPanel::GetSliderRawValue( int iFlexController )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "GetSliderRawValue(%d) invalid controller index\n", iFlexController );
+ return 0.0f;
+ }
+
+ return slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->getRawValue( nFlexSliderBarnum[ iFlexController ] );
+}
+
+void FlexPanel::GetSliderRange( int iFlexController, float& minvalue, float& maxvalue )
+{
+ int barnum = nFlexSliderBarnum[ iFlexController ];
+
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "GetSliderRange(%d) invalid controller index\n", iFlexController );
+
+ minvalue = 0.0f;
+ maxvalue = 1.0f;
+ return;
+ }
+
+ mxExpressionSlider *sl = slFlexScale[ nFlexSliderIndex[ iFlexController ] ];
+ Assert( sl );
+ minvalue = sl->getMinValue( barnum );
+ maxvalue = sl->getMaxValue( barnum );
+}
+
+void
+FlexPanel::SetSlider( int iFlexController, float value )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "SetSlider(%d) invalid controller index\n", iFlexController );
+ return;
+ }
+
+ slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->setValue( nFlexSliderBarnum[ iFlexController ], value );
+}
+
+float
+FlexPanel::GetInfluence( int iFlexController )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "GetInfluence(%d) invalid controller index\n", iFlexController );
+ return 0.0f;
+ }
+
+ return slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->getInfluence( );
+}
+
+void
+FlexPanel::SetEdited( int iFlexController, bool isEdited )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "IsEdited(%d) invalid controller index\n", iFlexController );
+ return;
+ }
+
+ slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->setEdited( nFlexSliderBarnum[ iFlexController ], isEdited );
+}
+
+bool
+FlexPanel::IsEdited( int iFlexController )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "IsEdited(%d) invalid controller index\n", iFlexController );
+ return 0.0f;
+ }
+
+ return slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->isEdited( nFlexSliderBarnum[ iFlexController ] );
+}
+
+void
+FlexPanel::SetInfluence( int iFlexController, float value )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "SetInfluence(%d) invalid controller index\n", iFlexController );
+ return;
+ }
+
+ // Con_Printf( "SetInfluence( %d, %.0f ) : %d %d\n", iFlexController, value, nFlexSliderIndex[ iFlexController ], nFlexSliderBarnum[ iFlexController ] );
+ if ( nFlexSliderBarnum[ iFlexController ] == 0)
+ {
+ slFlexScale[ nFlexSliderIndex[ iFlexController ] ]->setInfluence( value );
+ }
+}
+
+int
+FlexPanel::LookupFlex( int iSlider, int barnum )
+{
+ for (int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++)
+ {
+ if (nFlexSliderIndex[i] == iSlider && nFlexSliderBarnum[i] == barnum)
+ {
+ // char const *name = GetGlobalFlexControllerName( i );
+ //Con_Printf( "lookup slider %i bar %i == %s\n",
+ //iSlider, barnum, name );
+
+ return i;
+ }
+ }
+
+ Con_Printf( "lookup slider %i bar %i failed\n",
+ iSlider, barnum);
+ return 0;
+}
+
+
+int
+FlexPanel::LookupPairedFlex( int iFlexController )
+{
+ if ( !IsValidSlider( iFlexController ) )
+ {
+ Msg( "LookupPairedFlex(%d) invalid controller index\n", iFlexController );
+ return iFlexController;
+ }
+
+ if (nFlexSliderBarnum[ iFlexController ] == 1)
+ {
+ return iFlexController - 1;
+ }
+ else if (nFlexSliderIndex[ iFlexController + 1 ] == nFlexSliderIndex[ iFlexController ])
+ {
+ return iFlexController + 1;
+ }
+ return iFlexController;
+}
+
+void
+FlexPanel::setExpression( int index )
+{
+ if ( !models->GetActiveStudioModel() )
+ return;
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( !exp )
+ return;
+
+ // Con_Printf( "Setting expression to %i:'%s'\n", index, exp->name );
+
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+ if ( j == -1 )
+ continue;
+
+ //if ( weights[j] > 0.0f )
+ //{
+ // Con_Printf( "%i Setting %s to %f\n", j, GetGlobalFlexControllerName( j ),
+ // settings[ j ] );
+ //}
+
+ SetSlider( j, settings[j] );
+ SetInfluence( j, weights[j] );
+ models->GetActiveStudioModel()->SetFlexController( i, settings[j] * weights[j] );
+ }
+}
+
+void FlexPanel::DeleteExpression( int index )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return;
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( !exp )
+ return;
+
+ active->DeleteExpression( exp->name );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+//-----------------------------------------------------------------------------
+void FlexPanel::RevertExpression( int index )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( !exp )
+ return;
+
+ exp->Revert();
+ setExpression( index );
+ g_pExpressionTrayTool->redraw();
+}
+
+void FlexPanel::SaveExpression( int index )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ CExpression *exp = active->GetExpression( index );
+ if ( !exp )
+ return;
+
+ int retval = mxMessageBox( this, "Overwrite existing expression?", g_appTitle, MX_MB_YESNO | MX_MB_QUESTION );
+ if ( retval != 0 )
+ return;
+
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+ Assert( settings );
+ Assert( weights );
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ settings[ j ] = GetSlider( j );
+ weights[ j ] = GetInfluence( j );
+ }
+
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+
+ exp->ResetUndo();
+
+ exp->SetDirty( false );
+
+ g_pExpressionTrayTool->redraw();
+}
+
+void FlexPanel::CopyControllerSettingsToStructure( CExpression *exp )
+{
+ Assert( exp );
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ settings[ j ] = GetSlider( j );
+ weights[ j ] = GetInfluence( j );
+ }
+ }
+}
+
+void FlexPanel::OnSetAll( int state )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ float setting = GetSlider( j );
+ float influence = GetInfluence( j );
+ switch ( state )
+ {
+ default:
+ Assert( 0 );
+ break;
+ case FP_STATE_UNCHECK:
+ influence = 0.0f;
+ break;
+ case FP_STATE_CHECK:
+ influence = 1.0f;
+ break;
+ case FP_STATE_INVERT:
+ influence = 1.0f - influence;
+ break;
+ }
+
+ SetInfluence( j, influence );
+ models->GetActiveStudioModel()->SetFlexController( i, setting * influence );
+ }
+ }
+}
+
+void FlexPanel::ResetSliders( bool preserveundo, bool bDirtyClass )
+{
+ CExpClass *active = expressions->GetActiveClass();
+
+ bool needredo = false;
+ CExpression zeroes;
+
+ CExpression *exp = NULL;
+ if ( active )
+ {
+ int index = active->GetSelectedExpression();
+ if ( index != -1 )
+ {
+ exp = active->GetExpression( index );
+ if ( exp )
+ {
+ float *settings = exp->GetSettings();
+ Assert( settings );
+
+ if ( memcmp( settings, zeroes.GetSettings(), GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) ) )
+ {
+ if ( preserveundo )
+ {
+ exp->PushUndoInformation();
+ needredo = true;
+ }
+
+ if ( bDirtyClass )
+ {
+ active->SetDirty( true );
+ }
+
+ g_pExpressionTrayTool->redraw();
+ }
+ }
+ }
+ }
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ if( exp )
+ {
+ float *settings = exp->GetSettings();
+ float *weights = exp->GetWeights();
+
+ Assert( settings && weights );
+
+ for ( int i = 0; i < GLOBAL_STUDIO_FLEX_CONTROL_COUNT; i++ )
+ {
+ settings[ i ] = 0.0f;
+ weights[ i ] = 0.0f;
+ }
+ }
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ if ( j == -1 )
+ continue;
+
+ SetSlider( j, 0.0f );
+ SetInfluence( j, 0.0f );
+ SetEdited( j, false );
+ models->GetActiveStudioModel()->SetFlexController( i, 0.0f );
+ }
+ }
+
+ if ( exp && needredo && preserveundo )
+ {
+ exp->PushRedoInformation();
+ }
+}
+
+void FlexPanel::CopyControllerSettings( void )
+{
+ CExpression *exp = expressions->GetCopyBuffer();
+ memset( exp, 0, sizeof( *exp ) );
+ CopyControllerSettingsToStructure( exp );
+}
+
+void FlexPanel::PasteControllerSettings( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ bool needredo = false;
+ CExpression *paste = expressions->GetCopyBuffer();
+ if ( !paste )
+ return;
+
+ CExpression *exp = NULL;
+ int index = active->GetSelectedExpression();
+ if ( index != -1 )
+ {
+ exp = active->GetExpression( index );
+ if ( exp )
+ {
+ float *settings = exp->GetSettings();
+ Assert( settings );
+
+ // UPDATEME
+ if ( memcmp( settings, paste->GetSettings(), GLOBAL_STUDIO_FLEX_CONTROL_COUNT * sizeof( float ) ) )
+ {
+ exp->PushUndoInformation();
+ needredo = true;
+
+ active->SetDirty( true );
+
+ g_pExpressionTrayTool->redraw();
+ }
+ }
+ }
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( hdr )
+ {
+ float *settings = paste->GetSettings();
+ float *weights = paste->GetWeights();
+ Assert( settings );
+ Assert( weights );
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ SetSlider( j, settings[j] );
+ SetInfluence( j, weights[j] );
+ models->GetActiveStudioModel()->SetFlexController( i, settings[j] * weights[j] );
+ }
+ }
+
+ if ( exp && needredo )
+ {
+ exp->PushRedoInformation();
+ }
+
+}
+
+void FlexPanel::EditExpression( void )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ Con_ErrorPrintf( "Can't edit face pose, must load a model first!\n" );
+ return;
+ }
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ int index = active->GetSelectedExpression();
+ if ( index == -1 )
+ {
+ Con_ErrorPrintf( "Can't edit face pose, must select a face from list first!\n" );
+ return;
+ }
+
+ CExpression *exp = active->GetExpression( index );
+ if ( !exp )
+ {
+ return;
+ }
+
+ bool namechanged = false;
+ CExpressionParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Edit Expression" );
+ strcpy( params.m_szName, exp->name );
+ strcpy( params.m_szDescription, exp->description );
+
+ if ( !ExpressionProperties( &params ) )
+ return;
+
+ namechanged = stricmp( exp->name, params.m_szName ) ? true : false;
+
+ if ( ( strlen( params.m_szName ) <= 0 ) ||
+ !stricmp( params.m_szName, "unnamed" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid name\n" );
+ return;
+ }
+
+ if ( ( strlen( params.m_szDescription ) <= 0 ) ||
+ !stricmp( params.m_szDescription, "description" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid description\n" );
+ return;
+ }
+
+ if ( namechanged )
+ {
+ Con_Printf( "Deleting old bitmap %s\n", exp->GetBitmapFilename( models->GetActiveModelIndex() ) );
+
+ // Remove old bitmap
+ _unlink( exp->GetBitmapFilename( models->GetActiveModelIndex() ) );
+ }
+
+ strcpy( exp->name, params.m_szName );
+ strcpy( exp->description, params.m_szDescription );
+
+ if ( namechanged )
+ {
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+ }
+
+ active->SetDirty( true );
+
+ g_pExpressionTrayTool->redraw();
+}
+
+void FlexPanel::NewExpression( void )
+{
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ Con_ErrorPrintf( "Can't create new face pose, must load a model first!\n" );
+ return;
+ }
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ g_pExpressionTrayTool->Deselect();
+
+ CExpressionParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Add Expression" );
+ strcpy( params.m_szName, "" );
+ strcpy( params.m_szDescription, "" );
+
+ if ( !ExpressionProperties( &params ) )
+ return;
+
+ if ( ( strlen( params.m_szName ) <= 0 ) ||
+ !stricmp( params.m_szName, "unnamed" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid name\n" );
+ return;
+ }
+
+ if ( ( strlen( params.m_szDescription ) <= 0 ) ||
+ !stricmp( params.m_szDescription, "description" ) )
+ {
+ Con_ErrorPrintf( "You must type in a valid description\n" );
+ return;
+ }
+
+ float settings[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ float weights[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ memset( settings, 0, sizeof( settings ) );
+ memset( weights, 0, sizeof( settings ) );
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++ )
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ settings[ j ] = GetSlider( j );
+ weights[ j ] = GetInfluence( j );
+ }
+
+ active->AddExpression( params.m_szName, params.m_szDescription, settings, weights, true, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool FlexPanel::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+void FlexPanel::OnMenu()
+{
+ POINT pt;
+ pt.x = btnMenu->x();
+ pt.y = btnMenu->y();
+ pt.y -= 3 * btnMenu->h2();
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ pop->add( "Check All", IDC_FP_CHECK_ALL );
+ pop->add( "Uncheck All", IDC_FP_UNCHECK_ALL );
+ pop->add( "Invert Selection", IDC_FP_INVERT );
+
+ pop->popup( this, pt.x, pt.y );
+}
diff --git a/utils/hlfaceposer/flexpanel.h b/utils/hlfaceposer/flexpanel.h
new file mode 100644
index 0000000..015a5de
--- /dev/null
+++ b/utils/hlfaceposer/flexpanel.h
@@ -0,0 +1,144 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef FLEXPANEL_H
+#define FLEXPANEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+
+#define IDC_FLEX 7001
+#define IDC_FLEXSCROLL 7101
+#define IDC_EXPRESSIONRESET 7102
+
+// NOTE THIS THIS TAKES UP 4 * 96 entries (384)
+// #define NEXT_AVAIL 7457 ...etc.
+#define IDC_FLEXSCALE 7200
+
+#define IDC_FLEXSCALE_LAST 7584
+
+#define IDC_FP_UNCHECK_ALL 7800
+#define IDC_FP_CHECK_ALL 7801
+#define IDC_FP_INVERT 7802
+#define IDC_FP_MENU 7803
+
+#include "studio.h"
+
+class mxTab;
+class mxChoice;
+class mxCheckBox;
+class mxSlider;
+class mxScrollbar;
+class mxLineEdit;
+class mxLabel;
+class mxButton;
+class MatSysWindow;
+class TextureWindow;
+class mxExpressionSlider;
+
+
+#include "expressions.h"
+#include "faceposertoolwindow.h"
+
+/*
+ int nameindex;
+ int numkeys;
+ int keyindex;
+ { char key, char weight }
+*/
+
+class ControlPanel;
+
+class FlexPanel : public mxWindow, public IFacePoserToolWindow
+{
+ typedef mxWindow BaseClass;
+
+ mxExpressionSlider *slFlexScale[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ mxScrollbar *slScrollbar;
+
+ mxButton *btnResetSliders;
+ mxButton *btnCopyToSliders;
+ mxButton *btnCopyFromSliders;
+ mxButton *btnMenu;
+
+
+public:
+ // CREATORS
+ FlexPanel (mxWindow *parent);
+ virtual ~FlexPanel ();
+
+ virtual void redraw();
+ virtual bool PaintBackground( void );
+
+ void SetEvent( CChoreoEvent *event );
+ virtual void OnModelChanged();
+
+ // MANIPULATORS
+ int handleEvent (mxEvent *event);
+
+ void initFlexes ();
+
+ bool IsValidSlider( int iFlexController ) const;
+
+ float GetSlider( int iFlexController );
+ float GetSliderRawValue( int iFlexController );
+ void GetSliderRange( int iFlexController, float& minvalue, float& maxvalue );
+
+ void SetSlider( int iFlexController, float value );
+ float GetInfluence( int iFlexController );
+ void SetInfluence( int iFlexController, float value );
+ void SetEdited( int iFlexController, bool isEdited );
+ bool IsEdited( int iFlexController );
+ int LookupFlex( int iSlider, int barnum );
+ int LookupPairedFlex( int iFlexController );
+
+ // maps global flex_controller index to UI slider
+ int nFlexSliderIndex[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+ int nFlexSliderBarnum[ GLOBAL_STUDIO_FLEX_CONTROL_COUNT ];
+
+ void PositionSliders( int sboffset );
+ void PositionControls( int width, int height );
+
+ void EditExpression( void );
+ void NewExpression( void );
+
+ void setExpression( int index );
+ void DeleteExpression( int index );
+ void SaveExpression( int index );
+ void RevertExpression( int index );
+
+ void CopyControllerSettings( void );
+ void PasteControllerSettings( void );
+
+ void ResetSliders( bool preserveundo, bool bDirtyClass );
+
+ void CopyControllerSettingsToStructure( CExpression *exp );
+
+private:
+ enum
+ {
+ FP_STATE_UNCHECK = 0,
+ FP_STATE_CHECK,
+ FP_STATE_INVERT
+ };
+
+ void OnSetAll( int state );
+ void OnMenu();
+
+ bool m_bNewExpressionMode;
+
+ // Since we combine left/right into one, this will be less than hdr->numflexcontrollers
+ int m_nViewableFlexControllerCount;
+};
+
+extern FlexPanel *g_pFlexPanel;
+
+#endif // FLEXPANEL_H
diff --git a/utils/hlfaceposer/globaleventproperties.cpp b/utils/hlfaceposer/globaleventproperties.cpp
new file mode 100644
index 0000000..d5623f6
--- /dev/null
+++ b/utils/hlfaceposer/globaleventproperties.cpp
@@ -0,0 +1,205 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <mxtk/mx.h>
+#include <stdio.h>
+#include "resource.h"
+#include "GlobalEventProperties.h"
+#include "mdlviewer.h"
+#include "hlfaceposer.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "expressions.h"
+#include "choreoactor.h"
+#include "ifaceposersound.h"
+#include "expclass.h"
+#include "scriplib.h"
+
+static CGlobalEventParams g_Params;
+
+static void ExtractAutoStateFromParams( CGlobalEventParams *params )
+{
+ ParseFromMemory( params->m_szAction, strlen( params->m_szAction ) );
+
+ params->m_bAutomate = false;
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ params->m_bAutomate = !stricmp( token, "automate" ) ? true : false;
+ }
+
+ if ( params->m_bAutomate )
+ {
+ params->m_szType[ 0 ] = 0;
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ strcpy( params->m_szType, token );
+ }
+
+ params->m_flWaitTime = 0.0f;
+ if ( TokenAvailable() )
+ {
+ GetToken( false );
+ params->m_flWaitTime = (float)atof( token );
+ }
+ }
+}
+
+static void CreateAutoStateFromControls( CGlobalEventParams *params )
+{
+ if ( params->m_bAutomate )
+ {
+ sprintf( params->m_szAction, "automate %s %f", params->m_szType, params->m_flWaitTime );
+ }
+ else
+ {
+ sprintf( params->m_szAction, "noaction" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK GlobalEventPropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_EVENTNAME, g_Params.m_szName );
+
+ SetDlgItemText( hwndDlg, IDC_STARTTIME, va( "%f", g_Params.m_flStartTime ) );
+
+ switch ( g_Params.m_nType )
+ {
+ default:
+ Assert(0);
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ ShowWindow( GetDlgItem( hwndDlg, IDC_LOOPCOUNT ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_LOOPCOUNT ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_LOOPTIME ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_LOOPTIME ), SW_HIDE );
+
+ ExtractAutoStateFromParams( &g_Params );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ SendMessage( GetDlgItem( hwndDlg, IDC_LOOPCOUNT ), WM_SETTEXT , 0, (LPARAM)va( "%i", g_Params.m_nLoopCount ) );
+ SendMessage( GetDlgItem( hwndDlg, IDC_LOOPTIME ), WM_SETTEXT , 0, (LPARAM)va( "%f", g_Params.m_flLoopTime ) );
+
+ ShowWindow( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_DURATION ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK_AUTOCHECK ), SW_HIDE );
+
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_AFTER ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_SECONDS ), SW_HIDE );
+ }
+ break;
+ case CChoreoEvent::STOPPOINT:
+ {
+ ShowWindow( GetDlgItem( hwndDlg, IDC_LOOPCOUNT ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_LOOPCOUNT ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_LOOPTIME ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_LOOPTIME ), SW_HIDE );
+
+ ShowWindow( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_DURATION ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK_AUTOCHECK ), SW_HIDE );
+
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_AFTER ), SW_HIDE );
+ ShowWindow( GetDlgItem( hwndDlg, IDC_STATIC_SECONDS ), SW_HIDE );
+ }
+ break;
+ }
+
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_CHECK_AUTOCHECK ), BM_SETCHECK,
+ ( WPARAM ) g_Params.m_bAutomate ? BST_CHECKED : BST_UNCHECKED,
+ ( LPARAM )0 );
+
+ SetDlgItemText( hwndDlg, IDC_DURATION, va( "%f", g_Params.m_flWaitTime ) );
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), WM_SETTEXT , 0, (LPARAM)g_Params.m_szType );
+ // add text to combo box
+ SendMessage( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), CB_ADDSTRING, 0, (LPARAM)"Cancel" );
+ SendMessage( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), CB_ADDSTRING, 0, (LPARAM)"Resume" );
+
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_EVENTNAME ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ char szTime[ 32 ];
+
+ SendMessage( GetDlgItem( hwndDlg, IDC_CB_AUTOACTION ), WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szType ), (LPARAM)g_Params.m_szType );
+
+ GetDlgItemText( hwndDlg, IDC_DURATION, szTime, sizeof( szTime ) );
+ g_Params.m_flWaitTime = atof( szTime );
+
+ g_Params.m_bAutomate = SendMessage( GetDlgItem( hwndDlg, IDC_CHECK_AUTOCHECK ), BM_GETCHECK, 0, 0 ) == BST_CHECKED ? true : false;
+
+ CreateAutoStateFromControls( &g_Params );
+
+ GetDlgItemText( hwndDlg, IDC_EVENTNAME, g_Params.m_szName, sizeof( g_Params.m_szName ) );
+
+ GetDlgItemText( hwndDlg, IDC_STARTTIME, szTime, sizeof( szTime ) );
+ g_Params.m_flStartTime = atof( szTime );
+
+ char szLoop[ 32 ];
+ GetDlgItemText( hwndDlg, IDC_LOOPCOUNT, szLoop, sizeof( szLoop ) );
+ g_Params.m_nLoopCount = atoi( szLoop );
+ GetDlgItemText( hwndDlg, IDC_LOOPTIME, szLoop, sizeof( szLoop ) );
+ g_Params.m_flLoopTime = (float)atof( szLoop );
+
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int GlobalEventProperties( CGlobalEventParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_GLOBALEVENTPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)GlobalEventPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/globaleventproperties.h b/utils/hlfaceposer/globaleventproperties.h
new file mode 100644
index 0000000..c74160d
--- /dev/null
+++ b/utils/hlfaceposer/globaleventproperties.h
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GLOBALEVENTPROPERTIES_H
+#define GLOBALEVENTPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CChoreoScene;
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CGlobalEventParams : public CBaseDialogParams
+{
+ int m_nType;
+
+ // GlobalEvent descriptive name
+ char m_szName[ 256 ];
+
+ // Pause start time
+ float m_flStartTime;
+
+ // Pause Scene or Cancel Scene ( pause/cancel )
+ char m_szAction[ 256 ];
+
+ bool m_bAutomate;
+
+ char m_szType[ 256 ];
+
+ // Idle/paused time before action is taken
+ float m_flWaitTime;
+
+ // For loop events
+ int m_nLoopCount;
+ float m_flLoopTime;
+};
+
+int GlobalEventProperties( CGlobalEventParams *params );
+
+#endif // GLOBALEVENTPROPERTIES_H
diff --git a/utils/hlfaceposer/hlfaceposer.cpp b/utils/hlfaceposer/hlfaceposer.cpp
new file mode 100644
index 0000000..8b8a961
--- /dev/null
+++ b/utils/hlfaceposer/hlfaceposer.cpp
@@ -0,0 +1,744 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include "cbase.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "filesystem.h"
+#include "mxtk/mx.h"
+#include "mxStatusWindow.h"
+#include "filesystem.h"
+#include "StudioModel.h"
+#include "ControlPanel.h"
+#include "MDLViewer.h"
+#include "mxExpressionTray.H"
+#include "viewersettings.h"
+#include "tier1/strtools.h"
+#include "faceposer_models.h"
+#include "expressions.h"
+#include "choreoview.h"
+#include "choreoscene.h"
+#include "vstdlib/random.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "soundchars.h"
+#include "sentence.h"
+#include "PhonemeEditor.h"
+#include <vgui/ILocalize.h>
+#include "filesystem_init.h"
+#include "tier2/p4helpers.h"
+
+
+extern vgui::ILocalize *g_pLocalize;
+
+StudioModel *FindAssociatedModel( CChoreoScene *scene, CChoreoActor *a );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes a full path and determines if the file exists on the disk
+// Input : *filename -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool FPFullpathFileExists( const char *filename )
+{
+ // Should be a full path
+ Assert( strchr( filename, ':' ) );
+
+ struct _stat buf;
+ int result = _stat( filename, &buf );
+ if ( result != -1 )
+ return true;
+
+ return false;
+}
+
+// Utility functions mostly
+char *FacePoser_MakeWindowsSlashes( char *pname )
+{
+ static char returnString[ 4096 ];
+ strcpy( returnString, pname );
+ pname = returnString;
+
+ while ( *pname )
+ {
+ if ( *pname == '/' )
+ {
+ *pname = '\\';
+ }
+ pname++;
+ }
+
+ return returnString;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int GetCloseCaptionLanguageId()
+{
+ return g_viewerSettings.cclanguageid;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : id -
+//-----------------------------------------------------------------------------
+void SetCloseCaptionLanguageId( int id, bool force /* = false */ )
+{
+ Assert( id >= 0 && id < CC_NUM_LANGUAGES );
+ bool changed = g_viewerSettings.cclanguageid != id;
+ g_viewerSettings.cclanguageid = id;
+ if ( changed || force )
+ {
+ // Switch languages
+ char const *suffix = CSentence::NameForLanguage( id );
+ if ( Q_stricmp( suffix, "unknown_language" ) )
+ {
+ char fn[ MAX_PATH ];
+ Q_snprintf( fn, sizeof( fn ), "resource/closecaption_%s.txt", suffix );
+
+ g_pLocalize->RemoveAll();
+
+ if ( Q_stricmp( suffix, "english" )&&
+ filesystem->FileExists( "resource/closecaption_english.txt" ) )
+ {
+ g_pLocalize->AddFile( "resource/closecaption_english.txt", "GAME", true );
+ }
+
+ if ( filesystem->FileExists( fn ) )
+ {
+ g_pLocalize->AddFile( fn, "GAME", true );
+ }
+ else
+ {
+ Con_ErrorPrintf( "PhonemeEditor::SetCloseCaptionLanguageId Warning, can't find localization file %s\n", fn );
+ }
+
+ // Need to redraw the choreoview at least
+ if ( g_pChoreoView )
+ {
+ g_pChoreoView->InvalidateLayout();
+ }
+ }
+ }
+
+ if ( g_MDLViewer )
+ {
+ g_MDLViewer->UpdateLanguageMenu( id );
+ }
+}
+
+
+char *va( const char *fmt, ... )
+{
+ va_list args;
+ static char output[32][1024];
+ static int outbuffer = 0;
+
+ outbuffer++;
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output[ outbuffer & 31 ], fmt, args );
+ return output[ outbuffer & 31 ];
+}
+
+void Con_Printf( const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ if ( !g_pStatusWindow )
+ {
+ return;
+ }
+
+ g_pStatusWindow->StatusPrint( CONSOLE_COLOR, false, output );
+}
+
+void Con_ColorPrintf( COLORREF rgb, const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ if ( !g_pStatusWindow )
+ {
+ return;
+ }
+
+ g_pStatusWindow->StatusPrint( rgb, false, output );
+}
+
+void Con_ErrorPrintf( const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ if ( !g_pStatusWindow )
+ {
+ return;
+ }
+
+ g_pStatusWindow->StatusPrint( ERROR_COLOR, false, output );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+void MakeFileWriteable( const char *filename )
+{
+ Assert( filesystem );
+ char pFullPathBuf[ 512 ];
+ char *pFullPath;
+ if ( !Q_IsAbsolutePath( filename ) )
+ {
+ pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) );
+ }
+ else
+ {
+ Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) );
+ pFullPath = pFullPathBuf;
+ }
+
+ if ( pFullPath )
+ {
+ Q_FixSlashes( pFullPath );
+ SetFileAttributes( pFullPath, FILE_ATTRIBUTE_NORMAL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool IsFileWriteable( const char *filename )
+{
+ Assert( filesystem );
+ char pFullPathBuf[ 512 ];
+ char *pFullPath;
+ if ( !Q_IsAbsolutePath( filename ) )
+ {
+ pFullPath = (char*)filesystem->RelativePathToFullPath( filename, NULL, pFullPathBuf, sizeof(pFullPathBuf) );
+ }
+ else
+ {
+ Q_strncpy( pFullPathBuf, filename, sizeof(pFullPathBuf) );
+ pFullPath = pFullPathBuf;
+ }
+
+ if ( pFullPath )
+ {
+ Q_FixSlashes( pFullPath );
+ DWORD attrib = GetFileAttributes( pFullPath );
+ return ( ( attrib & FILE_ATTRIBUTE_READONLY ) == 0 );
+ }
+
+ // Doesn't seem to exist, so yeah, it's writable
+ return true;
+}
+
+bool MakeFileWriteablePrompt( const char *filename, char const *promptTitle )
+{
+ if ( !IsFileWriteable( filename ) )
+ {
+ int retval = mxMessageBox( NULL, va( "File '%s' is Read-Only, make writable?", filename ),
+ promptTitle, MX_MB_WARNING | MX_MB_YESNO );
+
+ // Didn't pick yes, bail
+ if ( retval != 0 )
+ return false;
+
+ MakeFileWriteable( filename );
+ }
+
+ return true;
+}
+
+void FPCopyFile( const char *source, const char *dest, bool bCheckOut )
+{
+ Assert( filesystem );
+ char fullpaths[ MAX_PATH ];
+ char fullpathd[ MAX_PATH ];
+
+ if ( !Q_IsAbsolutePath( source ) )
+ {
+ filesystem->RelativePathToFullPath( source, NULL, fullpaths, sizeof(fullpaths) );
+ }
+ else
+ {
+ Q_strncpy( fullpaths, source, sizeof(fullpaths) );
+ }
+
+ Q_strncpy( fullpathd, fullpaths, MAX_PATH );
+ char *pSubdir = Q_stristr( fullpathd, source );
+ if ( pSubdir )
+ {
+ *pSubdir = 0;
+ }
+ Q_AppendSlash( fullpathd, MAX_PATH );
+ Q_strncat( fullpathd, dest, MAX_PATH, MAX_PATH );
+
+ Q_FixSlashes( fullpaths );
+ Q_FixSlashes( fullpathd );
+
+ if ( bCheckOut )
+ {
+ CP4AutoEditAddFile checkout( fullpathd );
+ CopyFile( fullpaths, fullpathd, FALSE );
+ }
+ else
+ {
+ CopyFile( fullpaths, fullpathd, FALSE );
+ }
+}
+
+bool FacePoser_HasWindowStyle( mxWindow *w, int bits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ return ( style & bits ) ? true : false;
+}
+
+bool FacePoser_HasWindowExStyle( mxWindow *w, int bits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
+ return ( style & bits ) ? true : false;
+}
+
+void FacePoser_AddWindowStyle( mxWindow *w, int addbits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ style |= addbits;
+ SetWindowLong( wnd, GWL_STYLE, style );
+}
+
+void FacePoser_AddWindowExStyle( mxWindow *w, int addbits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
+ style |= addbits;
+ SetWindowLong( wnd, GWL_EXSTYLE, style );
+}
+
+void FacePoser_RemoveWindowStyle( mxWindow *w, int removebits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ style &= ~removebits;
+ SetWindowLong( wnd, GWL_STYLE, style );
+}
+
+void FacePoser_RemoveWindowExStyle( mxWindow *w, int removebits )
+{
+ HWND wnd = (HWND)w->getHandle();
+ DWORD style = GetWindowLong( wnd, GWL_EXSTYLE );
+ style &= ~removebits;
+ SetWindowLong( wnd, GWL_EXSTYLE, style );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *w -
+//-----------------------------------------------------------------------------
+void FacePoser_MakeToolWindow( mxWindow *w, bool smallcaption )
+{
+ FacePoser_AddWindowStyle( w, WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
+ if ( smallcaption )
+ {
+ FacePoser_AddWindowExStyle( w, WS_EX_OVERLAPPEDWINDOW );
+ FacePoser_AddWindowExStyle( w, WS_EX_TOOLWINDOW );
+ }
+}
+
+bool LoadViewerSettingsInt( char const *keyname, int *value );
+bool SaveViewerSettingsInt ( const char *keyname, int value );
+
+void FacePoser_LoadWindowPositions( char const *name, bool& visible, int& x, int& y, int& w, int& h, bool& locked, bool& zoomed )
+{
+ char subkey[ 512 ];
+ int v;
+
+ Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name );
+ LoadViewerSettingsInt( subkey, &v );
+ visible = v ? true : false;
+
+ Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name );
+ LoadViewerSettingsInt( subkey, &v );
+ locked = v ? true : false;
+
+ Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name );
+ LoadViewerSettingsInt( subkey, &v );
+ zoomed = v ? true : false;
+
+ Q_snprintf( subkey, sizeof( subkey ), "%s - x", name );
+ LoadViewerSettingsInt( subkey, &x );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - y", name );
+ LoadViewerSettingsInt( subkey, &y );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - width", name );
+ LoadViewerSettingsInt( subkey, &w );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - height", name );
+ LoadViewerSettingsInt( subkey, &h );
+}
+
+void FacePoser_SaveWindowPositions( char const *name, bool visible, int x, int y, int w, int h, bool locked, bool zoomed )
+{
+ char subkey[ 512 ];
+ Q_snprintf( subkey, sizeof( subkey ), "%s - visible", name );
+ SaveViewerSettingsInt( subkey, visible );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - locked", name );
+ SaveViewerSettingsInt( subkey, locked );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - x", name );
+ SaveViewerSettingsInt( subkey, x );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - y", name );
+ SaveViewerSettingsInt( subkey, y );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - width", name );
+ SaveViewerSettingsInt( subkey, w );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - height", name );
+ SaveViewerSettingsInt( subkey, h );
+ Q_snprintf( subkey, sizeof( subkey ), "%s - zoomed", name );
+ SaveViewerSettingsInt( subkey, zoomed );
+}
+
+static char g_PhonemeRoot[ MAX_PATH ] = { 0 };
+void FacePoser_SetPhonemeRootDir( char const *pchRootDir )
+{
+ Q_strncpy( g_PhonemeRoot, pchRootDir, sizeof( g_PhonemeRoot ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void FacePoser_EnsurePhonemesLoaded( void )
+{
+ // Don't bother unless a model is loaded, at least...
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ {
+ return;
+ }
+
+ char const *ext[] =
+ {
+ "",
+ "_strong",
+ "_weak",
+ };
+
+ for ( int i = 0 ; i < ARRAYSIZE( ext ); ++i )
+ {
+ char clname[ 256 ];
+ Q_snprintf( clname, sizeof( clname ), "%sphonemes%s", g_PhonemeRoot, ext[ i ] );
+ Q_FixSlashes( clname );
+ Q_strlower( clname );
+
+ if ( !expressions->FindClass( clname, false ) )
+ {
+ char clfile[ MAX_PATH ];
+ Q_snprintf( clfile, sizeof( clfile ), "expressions/%sphonemes%s.txt", g_PhonemeRoot, ext[ i ] );
+ Q_FixSlashes( clfile );
+ Q_strlower( clfile );
+
+ if ( g_pFileSystem->FileExists( clfile ) )
+ {
+ expressions->LoadClass( clfile );
+ CExpClass *cl = expressions->FindClass( clname, false );
+ if ( !cl )
+ {
+ Con_Printf( "FacePoser_EnsurePhonemesLoaded: %s missing!!!\n", clfile );
+ }
+ }
+ }
+ }
+}
+
+bool FacePoser_ShowFileNameDialog( bool openFile, char *relative, size_t bufsize, char const *subdir, char const *wildcard )
+{
+ Assert( relative );
+ relative[ 0 ] = 0 ;
+ Assert( subdir );
+ Assert( wildcard );
+
+ char workingdir[ 256 ];
+ Q_getwd( workingdir, sizeof( workingdir ) );
+ strlwr( workingdir );
+ Q_FixSlashes( workingdir, '/' );
+
+ // Show file io
+ bool inWorkingDirectoryAlready = false;
+ if ( Q_stristr_slash( workingdir, va( "%s%s", GetGameDirectory(), subdir ) ) )
+ {
+ inWorkingDirectoryAlready = true;
+ }
+
+// Show file io
+ const char *fullpath = NULL;
+
+ if ( openFile )
+ {
+ fullpath = mxGetOpenFileName(
+ 0,
+ inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ),
+ wildcard );
+ }
+ else
+ {
+ fullpath = mxGetSaveFileName(
+ 0,
+ inWorkingDirectoryAlready ? "." : FacePoser_MakeWindowsSlashes( va( "%s%s/", GetGameDirectory(), subdir ) ),
+ wildcard );
+ }
+ if ( !fullpath || !fullpath[ 0 ] )
+ return false;
+
+ Q_strncpy( relative, fullpath, bufsize );
+ return true;
+}
+
+bool FacePoser_ShowOpenFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard )
+{
+ return FacePoser_ShowFileNameDialog( true, relative, bufsize, subdir, wildcard );
+}
+
+bool FacePoser_ShowSaveFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard )
+{
+ return FacePoser_ShowFileNameDialog( false, relative, bufsize, subdir, wildcard );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: converts an english string to unicode
+//-----------------------------------------------------------------------------
+int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize)
+{
+ return ::MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, unicodeBufferSize);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: converts an unicode string to an english string
+//-----------------------------------------------------------------------------
+int ConvertUnicodeToANSI(const wchar_t *unicode, char *ansi, int ansiBufferSize)
+{
+ return ::WideCharToMultiByte(CP_ACP, 0, unicode, -1, ansi, ansiBufferSize, NULL, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If FPS is set and "using grid", snap to proper fractional time value
+// Input : t -
+// Output : float
+//-----------------------------------------------------------------------------
+float FacePoser_SnapTime( float t )
+{
+ if ( !g_pChoreoView )
+ return t;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return t;
+
+ return scene->SnapTime( t );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : t -
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *FacePoser_DescribeSnappedTime( float t )
+{
+ static char desc[ 128 ];
+ Q_snprintf( desc, sizeof( desc ), "%.3f", t );
+
+ if ( !g_pChoreoView )
+ return desc;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return desc;
+
+ t = scene->SnapTime( t );
+
+ int fps = scene->GetSceneFPS();
+
+ int ipart = (int)t;
+ int fracpart = (int)( ( t - (float)ipart ) * (float)fps + 0.5f );
+
+ int frame = ipart * fps + fracpart;
+
+ if ( fracpart == 0 )
+ {
+ Q_snprintf( desc, sizeof( desc ), "frame %i (time %i s.)", frame, ipart );
+ }
+ else
+ {
+ Q_snprintf( desc, sizeof( desc ), "frame %i (time %i + %i/%i s.)",
+ frame, ipart,fracpart, fps );
+ }
+
+ return desc;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int FacePoser_GetSceneFPS( void )
+{
+ if ( !g_pChoreoView )
+ return 1000;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return 1000;
+
+ return scene->GetSceneFPS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool FacePoser_IsSnapping( void )
+{
+ if ( !g_pChoreoView )
+ return false;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return false;
+
+ return scene->IsUsingFrameSnap();
+}
+
+char const *FacePoser_TranslateSoundNameGender( char const *soundname, gender_t gender )
+{
+ if ( Q_stristr( soundname, ".wav" ) )
+ return PSkipSoundChars( soundname );
+
+ return PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, gender ) );
+}
+
+char const *FacePoser_TranslateSoundName( char const *soundname, StudioModel *model /*= NULL*/ )
+{
+ if ( Q_stristr( soundname, ".wav" ) )
+ return PSkipSoundChars( soundname );
+
+ static char temp[ 256 ];
+
+ if ( model )
+ {
+ Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model->GetFileName() ) ), sizeof( temp ) );
+ }
+ else
+ {
+ Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, NULL ) ), sizeof( temp ) );
+ }
+ return temp;
+}
+
+char const *FacePoser_TranslateSoundName( CChoreoEvent *event )
+{
+ char const *soundname = event->GetParameters();
+ if ( Q_stristr( soundname, ".wav" ) )
+ return PSkipSoundChars( soundname );
+
+ // See if we can figure out the .mdl associated to this event's actor
+ static char temp[ 256 ];
+ temp[ 0 ] = 0;
+ StudioModel *model = NULL;
+
+ CChoreoActor *a = event->GetActor();
+ CChoreoScene *s = event->GetScene();
+
+ if ( a != NULL &&
+ s != NULL )
+ {
+ model = FindAssociatedModel( s, a );
+ }
+
+ Q_strncpy( temp, PSkipSoundChars( soundemitter->GetWavFileForSound( soundname, model ? model->GetFileName() : NULL ) ), sizeof( temp ) );
+ return temp;
+}
+
+
+#if defined( _WIN32 ) || defined( WIN32 )
+#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
+#else //_WIN32
+#define PATHSEPARATOR(c) ((c) == '/')
+#endif //_WIN32
+
+static bool charsmatch( char c1, char c2 )
+{
+ if ( tolower( c1 ) == tolower( c2 ) )
+ return true;
+ if ( PATHSEPARATOR( c1 ) && PATHSEPARATOR( c2 ) )
+ return true;
+ return false;
+}
+
+
+char *Q_stristr_slash( char const *pStr, char const *pSearch )
+{
+ AssertValidStringPtr(pStr);
+ AssertValidStringPtr(pSearch);
+
+ if (!pStr || !pSearch)
+ return 0;
+
+ char const* pLetter = pStr;
+
+ // Check the entire string
+ while (*pLetter != 0)
+ {
+ // Skip over non-matches
+ if ( charsmatch( *pLetter, *pSearch ) )
+ {
+ // Check for match
+ char const* pMatch = pLetter + 1;
+ char const* pTest = pSearch + 1;
+ while (*pTest != 0)
+ {
+ // We've run off the end; don't bother.
+ if (*pMatch == 0)
+ return 0;
+
+ if ( !charsmatch( *pMatch, *pTest ) )
+ break;
+
+ ++pMatch;
+ ++pTest;
+ }
+
+ // Found a match!
+ if (*pTest == 0)
+ return (char *)pLetter;
+ }
+
+ ++pLetter;
+ }
+
+ return 0;
+}
+
+static CUniformRandomStream g_Random;
+IUniformRandomStream *random = &g_Random;
diff --git a/utils/hlfaceposer/hlfaceposer.h b/utils/hlfaceposer/hlfaceposer.h
new file mode 100644
index 0000000..fdbce8c
--- /dev/null
+++ b/utils/hlfaceposer/hlfaceposer.h
@@ -0,0 +1,83 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( HLFACEPOSER_H )
+#define HLFACEPOSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <windows.h>
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+#define CONSOLE_COLOR RGB( 82, 173, 216 )
+
+#define ERROR_COLOR RGB( 255, 50, 20 )
+
+#define FILE_COLOR RGB( 0, 63, 200 )
+
+#define MAX_FP_MODELS 16
+
+#define SCRUBBER_HANDLE_WIDTH 40
+#define SCRUBBER_HANDLE_HEIGHT 10
+
+char *va( PRINTF_FORMAT_STRING const char *fmt, ... );
+
+char const *GetGameDirectory(); // e.g. u:\main\game\ep2
+char const *GetGameDirectorySimple(); // e.g. ep2
+
+void Con_Printf( PRINTF_FORMAT_STRING const char *fmt, ... );
+void Con_ColorPrintf( COLORREF clr, PRINTF_FORMAT_STRING const char *fmt, ... );
+void Con_ErrorPrintf( PRINTF_FORMAT_STRING const char *fmt, ... );
+
+bool FPFullpathFileExists( const char *filename );
+void MakeFileWriteable( const char *filename );
+bool MakeFileWriteablePrompt( const char *filename, char const *promptTitle );
+bool IsFileWriteable( const char *filename );
+void FPCopyFile( const char *source, const char *dest, bool bCheckOut );
+class mxWindow;
+void FacePoser_MakeToolWindow( mxWindow *w, bool smallcaption );
+void FacePoser_LoadWindowPositions( char const *name, bool& visible, int& x, int& y, int& w, int& h, bool& locked, bool& zoomed );
+void FacePoser_SaveWindowPositions( char const *name, bool visible, int x, int y, int w, int h, bool locked, bool zoomed );
+void FacePoser_AddWindowStyle( mxWindow *w, int addbits );
+void FacePoser_AddWindowExStyle( mxWindow *w, int addbits );
+void FacePoser_RemoveWindowStyle( mxWindow *w, int removebits );
+void FacePoser_RemoveWindowExStyle( mxWindow *w, int removebits );
+bool FacePoser_HasWindowStyle( mxWindow *w, int bits );
+bool FacePoser_HasWindowExStyle( mxWindow *w, int bits );
+
+void FacePoser_EnsurePhonemesLoaded( void );
+void FacePoser_SetPhonemeRootDir( char const *pchRootDir );
+
+int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize);
+int ConvertUnicodeToANSI(const wchar_t *unicode, char *ansi, int ansiBufferSize);
+
+float FacePoser_SnapTime( float t );
+char const *FacePoser_DescribeSnappedTime( float t );
+int FacePoser_GetSceneFPS( void );
+bool FacePoser_IsSnapping( void );
+
+class StudioModel;
+char const *FacePoser_TranslateSoundName( char const *soundname, StudioModel *model = NULL );
+class CChoreoEvent;
+
+char const *FacePoser_TranslateSoundName( CChoreoEvent *event );
+char const *FacePoser_TranslateSoundNameGender( char const *soundname, gender_t gender );
+
+extern class IFileSystem *filesystem;
+extern class ISceneTokenProcessor *tokenprocessor;
+
+char *Q_stristr_slash( char const *pStr, char const *pSearch );
+
+void SetCloseCaptionLanguageId( int id, bool force = false ); // from sentence.h enum
+int GetCloseCaptionLanguageId();
+
+bool FacePoser_ShowOpenFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard );
+bool FacePoser_ShowSaveFileNameDialog( char *relative, size_t bufsize, char const *subdir, char const *wildcard );
+
+#endif // HLFACEPOSER_H
diff --git a/utils/hlfaceposer/hlfaceposer.rc b/utils/hlfaceposer/hlfaceposer.rc
new file mode 100644
index 0000000..928255a
--- /dev/null
+++ b/utils/hlfaceposer/hlfaceposer.rc
@@ -0,0 +1,898 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ACTORPROPERTIES DIALOG 0, 0, 194, 81
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Actor Properties"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,42,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,60,50,14
+ EDITTEXT IDC_ACTORNAME,7,20,180,14,ES_AUTOHSCROLL
+ LTEXT "Name:",IDC_STATIC,7,7,180,9
+END
+
+IDD_INPUTDIALOG DIALOG 0, 0, 380, 61
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,40,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,66,40,50,14
+ EDITTEXT IDC_INPUTSTRING,7,20,366,14,ES_AUTOHSCROLL
+ LTEXT "Prompt:",IDC_STATIC_PROMPT,7,7,366,9
+END
+
+IDD_EXPRESSIONPROPERTIES DIALOG 0, 0, 207, 114
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Expression Properties:"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,73,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,93,50,14
+ EDITTEXT IDC_EXPRESSIONNAME,7,21,193,14,ES_AUTOHSCROLL
+ LTEXT "Name:",IDC_STATIC,7,9,193,9
+ EDITTEXT IDC_EXPRESSIONDESC,7,53,193,14,ES_AUTOHSCROLL
+ LTEXT "Description:",IDC_STATIC,7,40,193,9
+END
+
+IDD_CHANNELPROPERTIES DIALOG 0, 0, 255, 82
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Channel Properties"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,42,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,61,50,14
+ EDITTEXT IDC_CHANNELNAME,7,20,106,14,ES_AUTOHSCROLL
+ LTEXT "Name:",IDC_STATIC,7,7,53,9
+ COMBOBOX IDC_ACTORCHOICE,139,20,109,97,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Add to Actor:",IDC_STATIC_ACTOR,139,7,53,9
+END
+
+IDD_EVENTPROPERTIES DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ EDITTEXT IDC_FILENAME,65,30,256,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_EVENTCHOICES2,65,50,256,184,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ PUSHBUTTON "Choose File...",IDC_SELECTWAV,7,50,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ LTEXT "Pitch:",IDC_STATIC_PITCH,156,61,23,10
+ LTEXT "Yaw:",IDC_STATIC_YAW,156,76,23,10
+ CONTROL "Slider1",IDC_SLIDER_PITCH,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,178,61,135,12
+ CONTROL "Slider1",IDC_SLIDER_YAW,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,178,76,135,12
+ LTEXT "PitchVal",IDC_STATIC_PITCHVAL,333,61,28,10
+ LTEXT "YawVal",IDC_STATIC_YAWVAL,333,76,28,10
+ CONTROL "Use Pitch/Yaw",IDC_CHECK_LOOKAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,81,67,76,11
+ CONTROL "Show All Sounds",IDC_SHOW_ALL_SOUNDS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,69,10
+ COMBOBOX IDC_EVENTCHOICES3,65,68,256,184,CBS_DROPDOWN | CBS_AUTOHSCROLL | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
+ LTEXT "",IDC_CHOICES2PROMPT,7,52,57,9,NOT WS_VISIBLE
+ LTEXT "",IDC_CHOICES3PROMPT,7,69,57,9,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,106,74,10
+END
+
+IDD_PHONEMEPROPERTIES DIALOG 0, 0, 323, 151
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Phoneme/Viseme Properties"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_EDIT_PHONEME,39,130,161,14,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,205,130,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,266,130,50,14
+ LTEXT "Phoneme/Viseme:",IDC_PHONEMENAME,7,7,309,9
+ LTEXT "Code:",IDC_PHONEMETEXTPROMPT,7,129,24,15
+ LTEXT "",IDC_STATIC_HELPTEXT,83,110,194,15
+END
+
+IDD_GLOBALEVENTPROPERTIES DIALOG 0, 0, 307, 114
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Pause"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_EVENTNAME,66,9,234,15,ES_AUTOHSCROLL
+ EDITTEXT IDC_STARTTIME,112,29,109,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_DURATION,171,45,46,12,ES_AUTOHSCROLL
+ CONTROL "Automatically",IDC_CHECK_AUTOCHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,47,57,10
+ DEFPUSHBUTTON "OK",IDOK,7,74,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,93,50,14
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,67,31,43,9
+ COMBOBOX IDC_CB_AUTOACTION,74,44,70,62,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "after",IDC_STATIC_AFTER,150,47,20,9
+ LTEXT "seconds",IDC_STATIC_SECONDS,225,47,41,9
+ EDITTEXT IDC_LOOPCOUNT,159,60,61,12,ES_AUTOHSCROLL
+ LTEXT "Loop Count (-1 = forever)",IDC_STATIC_LOOPCOUNT,67,62,88,9
+ LTEXT "Loop to:",IDC_STATIC_LOOPTIME,67,48,43,9
+ EDITTEXT IDC_LOOPTIME,112,46,109,12,ES_AUTOHSCROLL
+END
+
+IDD_FLEXSLIDERS DIALOG 0, 0, 239, 196
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Activate Sliders"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,182,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,182,24,50,14
+ CONTROL "Tree1",IDC_SLIDERS,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_CHECKBOXES | TVS_FULLROWSELECT | WS_BORDER | WS_TABSTOP,7,7,150,182
+END
+
+IDD_CHOICEDIALOG DIALOG 0, 0, 240, 111
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Choose"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,183,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,183,24,50,14
+ LTEXT "Prompt:",IDC_STATIC_PROMPT,7,7,170,9
+ COMBOBOX IDC_CHOICE,7,20,165,84,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_EDITPHRASE DIALOG 0, 0, 237, 139
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Edit Phrase"
+FONT 8, "Tahoma"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,118,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,67,118,50,14
+ LTEXT "Prompt:",IDC_STATIC_PROMPT,7,7,223,9
+ EDITTEXT IDC_INPUTSTRING,7,20,223,14,ES_AUTOHSCROLL
+ LTEXT "Legend:\r\n<clr:r,g,b> start color\r\n<clr> end color\r\n<I> start/end italic\r\n<B> start/end bold\r\n<linger:num> number of seconds to linger\r\n<cr> linebreak",IDC_STATIC,7,38,141,64
+END
+
+IDD_WAVELOOKUP DIALOG 0, 0, 451, 177
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Sound Lookup"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,394,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,394,25,50,14
+ LTEXT "Sound entries for .wav file xxx:",IDC_STATIC_PROMPT,7,7,168,14
+ LISTBOX IDC_SOUNDENTRYLIST,7,49,437,104,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Choose one of the following entries instead?",IDC_STATIC,7,34,145,12
+ PUSHBUTTON "Add New Entry...",IDC_ADDENTRY,7,156,66,14
+END
+
+IDD_ADDSOUNDENTRY DIALOG 0, 0, 273, 94
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add Sound Entry"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,216,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,216,24,50,14
+ LTEXT "Sound Name:",IDC_STATIC,7,7,182,12
+ EDITTEXT IDC_SOUNDNAME,7,19,191,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_SOUNDSCRIPT,7,52,191,99,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Script File for Sound:",IDC_STATIC,7,39,66,8
+END
+
+IDD_CCLOOKUP DIALOG 0, 0, 620, 350
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Close Caption Lookup"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,563,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,563,25,50,14
+ LTEXT "Close Caption Token:",IDC_STATIC,7,10,145,12
+ EDITTEXT IDC_CCTOKEN,7,22,392,12,ES_AUTOHSCROLL
+ CONTROL "List1",IDC_CCTOKENLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,40,606,303
+END
+
+IDD_EVENTPROPERTIES_EXPRESSION DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ COMBOBOX IDC_EVENTCHOICES2,65,50,256,184,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ LTEXT "",IDC_CHOICES2PROMPT,7,52,57,9,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_LOOKAT DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ LTEXT "Pitch:",IDC_STATIC_PITCH,156,61,23,10
+ LTEXT "Yaw:",IDC_STATIC_YAW,156,76,23,10
+ CONTROL "Slider1",IDC_SLIDER_PITCH,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,178,61,135,12
+ CONTROL "Slider1",IDC_SLIDER_YAW,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,178,76,135,12
+ LTEXT "PitchVal",IDC_STATIC_PITCHVAL,333,61,28,10
+ LTEXT "YawVal",IDC_STATIC_YAWVAL,333,76,28,10
+ CONTROL "Use Pitch/Yaw",IDC_CHECK_LOOKAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,81,67,76,11
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_MOVETO DIALOGEX 0, 0, 368, 259
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ COMBOBOX IDC_EVENTCHOICES2,65,50,256,184,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,108,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,129,52,8
+ EDITTEXT IDC_STARTTIME,219,126,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,143,54,8
+ COMBOBOX IDC_TAGS,219,140,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,167,62,10
+ EDITTEXT IDC_ENDTIME,219,166,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,209,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,228,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,129,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,188,295,48,NOT WS_VISIBLE
+ LTEXT "",IDC_CHOICES2PROMPT,7,52,57,9,NOT WS_VISIBLE
+ CONTROL "Slider1",IDC_SLIDER_DISTANCE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,99,68,165,12
+ LTEXT "Stop Distance:",IDC_STATIC_DISTANCE,7,68,57,10
+ LTEXT "DistVal",IDC_STATIC_DISTANCEVAL,65,68,28,10
+ CONTROL "Force Short Movements",IDC_CHECK_FORCESHORTMOVEMENT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,270,69,91,10
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,108,74,10
+ COMBOBOX IDC_EVENTCHOICES3,66,86,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Closest To:",IDC_TYPENAME2,7,87,57,9
+END
+
+IDD_EVENTPROPERTIES_SPEAK DIALOGEX 0, 0, 371, 380
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,63,9,265,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,242,213,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,262,52,8
+ EDITTEXT IDC_STARTTIME,222,259,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,277,54,8
+ COMBOBOX IDC_TAGS,222,274,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,300,62,10
+ EDITTEXT IDC_ENDTIME,222,299,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,342,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,359,50,14
+ LTEXT "Type",IDC_TYPENAME,7,31,51,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,262,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,69,319,295,48,NOT WS_VISIBLE
+ CONTROL "Show All Sounds",IDC_SHOW_ALL_SOUNDS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,156,225,69,10
+ LISTBOX IDC_SOUNDLIST,63,29,265,108,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_FILTER,63,188,265,120,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "WAVEFILENAME",IDC_STATIC_WAVEFILENAME,63,160,265,11
+ LTEXT "Wave File:",IDC_STATIC,7,160,50,11
+ LTEXT "Script File:",IDC_STATIC,7,176,50,11
+ LTEXT "SCRIPTFILE",IDC_STATIC_SCRIPTFILE,63,176,265,11
+ PUSHBUTTON "Play Sound",IDC_PLAY_SOUND,7,223,66,13
+ PUSHBUTTON "Open Source",IDC_OPENSOURCE,83,223,66,13
+ EDITTEXT IDC_SOUNDNAME,63,143,265,13,ES_AUTOHSCROLL
+ LTEXT "Filter:",IDC_STATIC,7,190,50,11
+ LTEXT "Sound:",IDC_STATIC,7,144,50,11
+ LTEXT "Volume:",IDC_STATIC,7,208,50,11
+ COMBOBOX IDC_EVENTCHOICES2,63,206,265,120,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Don't Attenuate Captions",IDC_CAPTION_ATTENUATION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,242,95,10
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,261,74,10
+END
+
+IDD_EVENTPROPERTIES_GESTURE DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Sync Exit tag to next gestures Entry tag",IDC_CHECK_SYNCTOFOLLOWINGGESTURE,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,74,170,10
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_SEQUENCE DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+ CONTROL "Play Sequence Overtop Script",IDC_CHECK_PLAYOVERSCRIPT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,73,223,10
+END
+
+IDD_EVENTPROPERTIES_FACE DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Lock facing (twist but don't turn)",IDC_CHECK_LOCKBODYFACING,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,65,53,230,10
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_FIRETRIGGER DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_FLEXANIMATION DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_SUBSCENE DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ EDITTEXT IDC_FILENAME,65,30,256,14,ES_AUTOHSCROLL
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ PUSHBUTTON "Choose File...",IDC_SELECTWAV,7,50,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_INTERRUPT DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_PERMITRESPONSES DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EVENTPROPERTIES_GENERIC DIALOGEX 0, 0, 368, 241
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_EVENTNAME,65,9,256,15,ES_AUTOHSCROLL
+ COMBOBOX IDC_EVENTCHOICES2,65,50,256,184,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Event must complete for paused scene to resume",IDC_CHECK_RESUMECONDITION,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,98,90,230,10
+ CONTROL "Absolute:",IDC_ABSOLUTESTART,"Button",BS_AUTORADIOBUTTON,161,110,52,8
+ EDITTEXT IDC_STARTTIME,219,107,142,12,ES_AUTOHSCROLL
+ CONTROL "Relative:",IDC_RELATIVESTART,"Button",BS_AUTORADIOBUTTON | WS_GROUP,161,125,54,8
+ COMBOBOX IDC_TAGS,219,122,142,85,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "End Time:",IDC_CHECK_ENDTIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,99,149,62,10
+ EDITTEXT IDC_ENDTIME,219,147,142,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,190,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,209,50,14
+ COMBOBOX IDC_EVENTCHOICES,65,31,256,144,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type",IDC_TYPENAME,7,31,57,9
+ LTEXT "Name:",IDC_STATIC,8,10,51,12
+ LTEXT "Start Time:",IDC_STATIC,111,110,47,9
+ LTEXT "INVISIBLE SPLINE PLACEHOLDER",IDC_STATIC_SPLINE,66,170,295,48,NOT WS_VISIBLE
+ CONTROL "Disabled",IDC_CHECK_DISABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,74,10
+END
+
+IDD_EDGEPROPERTIES DIALOGEX 0, 0, 341, 115
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Edge Properties"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,275,28,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,275,46,50,14
+ COMBOBOX IDC_LEFT_CURVETYPE,7,37,118,71,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_RIGHT_CURVETYPE,135,37,122,71,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Left Edge Interpolator Type:",IDC_STATIC,7,23,116,8
+ LTEXT "Right Edge Interpolator Type:",IDC_STATIC,135,23,123,8
+ CONTROL "Left Edge Active",IDC_LEFT_ACTIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,115,10
+ CONTROL "Right Edge Active",IDC_RIGHT_ACTIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,135,7,123,10
+ LTEXT "Edge Value:",IDC_STATIC,7,52,102,8
+ EDITTEXT IDC_LEFT_ZEROVALUE,7,64,118,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_RIGHT_ZEROVALUE,135,64,122,14,ES_AUTOHSCROLL
+ PUSHBUTTON "Reset",IDC_LEFT_RESET,35,94,50,14
+ PUSHBUTTON "Reset",IDC_RIGHT_RESET,174,94,50,14
+END
+
+IDD_PROGRESS DIALOGEX 0, 0, 293, 71
+STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_BORDER
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ LTEXT "",IDC_FP_PROGRESS_TITLE,7,7,279,12
+ CONTROL "",IDC_FP_PROGRESS,"msctls_progress32",0x0,7,41,279,8
+ LTEXT "",IDC_FP_PROGRESS_TEXT,7,25,222,9
+ PUSHBUTTON "Cancel",IDCANCEL,236,51,50,13
+ LTEXT "",IDC_FP_PROGRESS_PERCENT,244,25,42,9
+ LTEXT "",IDC_FP_PROGRESS_ETA,7,55,203,9
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_ACTORPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 187
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 74
+ END
+
+ IDD_INPUTDIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 373
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 54
+ END
+
+ IDD_EXPRESSIONPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 200
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 107
+ END
+
+ IDD_CHANNELPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 248
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 75
+ END
+
+ IDD_EVENTPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_PHONEMEPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 316
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 144
+ END
+
+ IDD_GLOBALEVENTPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 300
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 107
+ END
+
+ IDD_FLEXSLIDERS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 232
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 189
+ END
+
+ IDD_CHOICEDIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 233
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 104
+ END
+
+ IDD_EDITPHRASE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 230
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 132
+ END
+
+ IDD_WAVELOOKUP, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 444
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 170
+ END
+
+ IDD_ADDSOUNDENTRY, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 266
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 87
+ END
+
+ IDD_CCLOOKUP, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 613
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 343
+ END
+
+ IDD_EVENTPROPERTIES_EXPRESSION, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_LOOKAT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_MOVETO, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 252
+ END
+
+ IDD_EVENTPROPERTIES_SPEAK, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 364
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 373
+ END
+
+ IDD_EVENTPROPERTIES_GESTURE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_SEQUENCE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_FACE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_FIRETRIGGER, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_FLEXANIMATION, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_SUBSCENE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_INTERRUPT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_PERMITRESPONSES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EVENTPROPERTIES_GENERIC, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 361
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 234
+ END
+
+ IDD_EDGEPROPERTIES, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 334
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 108
+ END
+
+ IDD_PROGRESS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 286
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 64
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_GLOBALEVENTPROPERTIES DLGINIT
+BEGIN
+ IDC_CB_AUTOACTION, 0x403, 13, 0
+0x6552, 0x7573, 0x656d, 0x5320, 0x6563, 0x656e, "\000"
+ IDC_CB_AUTOACTION, 0x403, 13, 0
+0x6143, 0x636e, 0x6c65, 0x5320, 0x6563, 0x656e, "\000"
+ 0
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// German (Switzerland) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DES)
+#ifdef _WIN32
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_SWISS
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+MX_ICON ICON "icon1.ico"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // German (Switzerland) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/utils/hlfaceposer/hlfaceposer.vpc b/utils/hlfaceposer/hlfaceposer.vpc
new file mode 100644
index 0000000..8cf2054
--- /dev/null
+++ b/utils/hlfaceposer/hlfaceposer.vpc
@@ -0,0 +1,411 @@
+//-----------------------------------------------------------------------------
+// HLFACEPOSER.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,$SRCDIR\utils\hlfaceposer,$SRCDIR\hlfaceposer,..\common,..\hlmv,$SRCDIR\game\shared,..\SAPI51\Include,.\lipsinc;..\common\shaderdll"
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE comctl32.lib winmm.lib Msimg32.lib"
+ $EntryPoint "mainCRTStartup"
+ }
+}
+
+$Project "hlfaceposer"
+{
+ $Folder "Source Files"
+ {
+ $File "ActorProperties.cpp"
+ $File "ActorProperties.h"
+ $File "addsoundentry.cpp"
+ $File "addsoundentry.h"
+ $File "AnimationBrowser.cpp"
+ $File "AnimationBrowser.h"
+ $File "basedialogparams.cpp"
+ $File "basedialogparams.h"
+ $File "cbase.h"
+ $File "cclookup.cpp"
+ $File "cclookup.h"
+ $File "ChannelProperties.cpp"
+ $File "ChannelProperties.h"
+ $File "choiceproperties.cpp"
+ $File "choiceproperties.h"
+ $File "ChoreoView.cpp"
+ $File "ChoreoView.h"
+ $File "ChoreoViewColors.h"
+ $File "CloseCaptionTool.cpp"
+ $File "CloseCaptionTool.h"
+ $File "ControlPanel.cpp"
+ $File "ControlPanel.h"
+ $File "curveeditorhelpers.h"
+ $File "..\hlmv\debugdrawmodel.cpp"
+ $File "EdgeProperties.cpp"
+ $File "EdgeProperties.h"
+ $File "EditPhrase.cpp"
+ $File "EditPhrase.h"
+ $File "EventProperties.cpp"
+ $File "EventProperties.h"
+ $File "eventproperties_expression.cpp"
+ $File "eventproperties_expression.h"
+ $File "eventproperties_face.cpp"
+ $File "eventproperties_face.h"
+ $File "eventproperties_firetrigger.cpp"
+ $File "eventproperties_firetrigger.h"
+ $File "eventproperties_flexanimation.cpp"
+ $File "eventproperties_flexanimation.h"
+ $File "eventproperties_generic.cpp"
+ $File "eventproperties_generic.h"
+ $File "eventproperties_gesture.cpp"
+ $File "eventproperties_gesture.h"
+ $File "eventproperties_interrupt.cpp"
+ $File "eventproperties_interrupt.h"
+ $File "eventproperties_lookat.cpp"
+ $File "eventproperties_lookat.h"
+ $File "eventproperties_moveto.cpp"
+ $File "eventproperties_moveto.h"
+ $File "eventproperties_permitresponses.cpp"
+ $File "eventproperties_permitresponses.h"
+ $File "eventproperties_sequence.cpp"
+ $File "eventproperties_sequence.h"
+ $File "eventproperties_speak.cpp"
+ $File "eventproperties_speak.h"
+ $File "eventproperties_subscene.cpp"
+ $File "eventproperties_subscene.h"
+ $File "expclass.cpp"
+ $File "expclass.h"
+ $File "expression.cpp"
+ $File "expression.h"
+ $File "ExpressionProperties.cpp"
+ $File "ExpressionProperties.h"
+ $File "expressions.cpp"
+ $File "expressions.h"
+ $File "$SRCDIR\game\shared\expressionsample.h"
+ $File "ExpressionTool.cpp"
+ $File "ExpressionTool.h"
+ $File "faceposer_models.cpp"
+ $File "faceposer_models.h"
+ $File "faceposertoolwindow.cpp"
+ $File "faceposertoolwindow.h"
+ $File "FacePoserWorkspace.cpp"
+ $File "fileloaderthread.cpp"
+ $File "FlexPanel.cpp"
+ $File "FlexPanel.h"
+ $File "GestureTool.cpp"
+ $File "GestureTool.h"
+ $File "GlobalEventProperties.cpp"
+ $File "GlobalEventProperties.h"
+ $File "hlfaceposer.cpp"
+ $File "hlfaceposer.h"
+ $File "ICloseCaptionManager.h"
+ $File "ifaceposersound.h"
+ $File "ifaceposerworkspace.h"
+ $File "ifileloader.h"
+ $File "InputProperties.cpp"
+ $File "InputProperties.h"
+ $File "$SRCDIR\public\interpolatortypes.cpp"
+ $File "$SRCDIR\game\shared\interval.cpp"
+ $File "matsyswin.cpp"
+ $File "matsyswin.h"
+ $File "mdlviewer.cpp"
+ $File "mdlviewer.h"
+ $File "mxbitmapbutton.cpp"
+ $File "mxbitmapbutton.h"
+ $File "mxbitmaptools.cpp"
+ $File "mxbitmaptools.h"
+ $File "mxbitmapwindow.cpp"
+ $File "mxbitmapwindow.h"
+ $File "mxexpressionslider.cpp"
+ $File "mxexpressionslider.h"
+ $File "mxExpressionTab.cpp"
+ $File "mxexpressiontab.h"
+ $File "mxexpressiontray.cpp"
+ $File "mxexpressiontray.h"
+ $File "mxstatuswindow.cpp"
+ $File "mxstatuswindow.h"
+ $File "$SRCDIR\public\phonemeconverter.cpp"
+ $File "$SRCDIR\public\phonemeconverter.h"
+ $File "PhonemeEditor.cpp"
+ $File "PhonemeEditor.h"
+ $File "PhonemeEditorColors.h"
+ $File "PhonemeProperties.cpp"
+ $File "PhonemeProperties.h"
+ $File "ProgressDialog.cpp"
+ $File "ProgressDialog.h"
+ $File "RampTool.cpp"
+ $File "RampTool.h"
+ $File "SceneRampTool.cpp"
+ $File "SceneRampTool.h"
+ $File "$SRCDIR\public\sentence.cpp"
+ $File "$SRCDIR\public\sentence.h"
+ $File "soundlookup.cpp"
+ $File "soundlookup.h"
+ $File "$SRCDIR\public\SoundParametersInternal.cpp"
+ $File "tabwindow.cpp"
+ $File "tabwindow.h"
+ $File "TimelineItem.cpp"
+ $File "TimelineItem.h"
+ $File "vcdbrowser.cpp"
+ $File "vcdbrowser.h"
+ $File "wavebrowser.cpp"
+ $File "wavebrowser.h"
+ $File "wavefile.cpp"
+ $File "wavefile.h"
+
+ $Folder "Choreo Widgets"
+ {
+ $File "ChoreoActorWidget.cpp"
+ $File "ChoreoActorWidget.h"
+ $File "ChoreoChannelWidget.cpp"
+ $File "ChoreoChannelWidget.h"
+ $File "ChoreoEventWidget.cpp"
+ $File "ChoreoEventWidget.h"
+ $File "ChoreoGlobalEventWidget.cpp"
+ $File "ChoreoGlobalEventWidget.h"
+ $File "ChoreoWidget.cpp"
+ $File "ChoreoWidget.h"
+ $File "ChoreoWidgetDrawHelper.cpp"
+ $File "ChoreoWidgetDrawHelper.h"
+ }
+
+ $Folder "Vgui"
+ {
+ $File "faceposer_vgui.cpp"
+ $File "faceposer_vgui.h"
+ $File "VGuiWnd.cpp"
+ $File "VGuiWnd.h"
+ $File "$SRCDIR\public\vgui_controls\vgui_controls.cpp"
+ }
+ }
+
+ $Folder "Resource Files"
+ {
+ $File "hlfaceposer.rc"
+ $File "icon1.ico"
+ $File "resource.h"
+ }
+
+ $Folder "Shared Source Files"
+ {
+ $File "$SRCDIR\public\bone_setup.cpp"
+ $File "$SRCDIR\public\CollisionUtils.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "$SRCDIR\game\shared\iscenetokenprocessor.h"
+ $File "$SRCDIR\public\jigglebones.cpp"
+ $File "..\hlmv\physmesh.cpp"
+ $File "..\common\scriplib.cpp"
+ $File "$SRCDIR\public\soundcombiner.cpp"
+ $File "$SRCDIR\public\studio.cpp"
+ $File "..\hlmv\studio_flex.cpp"
+ $File "..\hlmv\studio_render.cpp"
+ $File "..\hlmv\studio_utils.cpp"
+ $File "..\hlmv\ViewerSettings.cpp"
+ }
+
+ $Folder "Shared Header Files"
+ {
+ $File "$SRCDIR\public\mathlib\amd3dx.h"
+ $File "$SRCDIR\public\basehandle.h"
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\public\bitvec.h"
+ $File "$SRCDIR\public\bone_accessor.h"
+ $File "$SRCDIR\public\bone_setup.h"
+ $File "$SRCDIR\public\BSPFILE.H"
+ $File "$SRCDIR\public\bspflags.h"
+ $File "$SRCDIR\public\mathlib\bumpvects.h"
+ $File "$SRCDIR\public\tier1\characterset.h"
+ $File "$SRCDIR\public\tier1\checksum_crc.h"
+ $File "..\common\cmdlib.h"
+ $File "$SRCDIR\public\cmodel.h"
+ $File "$SRCDIR\public\CollisionUtils.h"
+ $File "$SRCDIR\public\Color.h"
+ $File "$SRCDIR\public\tier0\commonmacros.h"
+ $File "$SRCDIR\public\mathlib\compressed_light_cube.h"
+ $File "$SRCDIR\public\mathlib\compressed_vector.h"
+ $File "$SRCDIR\public\const.h"
+ $File "$SRCDIR\public\vphysics\constraints.h"
+ $File "$SRCDIR\public\tier1\convar.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "..\hlmv\debugdrawmodel.h"
+ $File "$SRCDIR\public\tier0\fasttimer.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ $File "..\common\FileSystem_Tools.h"
+ $File "$SRCDIR\public\tier1\fmtstr.h"
+ $File "$SRCDIR\public\gametrace.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\tier0\icommandline.h"
+ $File "$SRCDIR\public\engine\IEngineSound.h"
+ $File "$SRCDIR\public\ihandleentity.h"
+ $File "$SRCDIR\public\vstdlib\IKeyValuesSystem.h"
+ $File "$SRCDIR\public\vgui\ILocalize.h"
+ $File "$SRCDIR\public\materialsystem\imaterial.h"
+ $File "$SRCDIR\public\materialsystem\imaterialproxyfactory.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystem.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystemhardwareconfig.h"
+ $File "$SRCDIR\public\materialsystem\imaterialvar.h"
+ $File "$SRCDIR\public\materialsystem\imesh.h"
+ $File "$SRCDIR\public\tier1\interface.h"
+ $File "$SRCDIR\public\interpolatortypes.h"
+ $File "$SRCDIR\game\shared\interval.h"
+ $File "$SRCDIR\public\irecipientfilter.h"
+ $File "$SRCDIR\public\isoundcombiner.h"
+ $File "$SRCDIR\public\SoundEmitterSystem\isoundemittersystembase.h"
+ $File "$SRCDIR\public\ISpatialPartition.h"
+ $File "$SRCDIR\public\istudiorender.h"
+ $File "$SRCDIR\public\materialsystem\itexture.h"
+ $File "$SRCDIR\public\jigglebones.h"
+ $File "$SRCDIR\public\tier1\KeyValues.h"
+ $File "$SRCDIR\public\tier0\l2cache.h"
+ $File "mapentities.h"
+ $File "$SRCDIR\public\materialsystem\materialsystem_config.h"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "..\hlmv\matsyswin.h"
+ $File "..\hlmv\mdlviewer.h"
+ $File "$SRCDIR\public\tier0\mem.h"
+ $File "$SRCDIR\public\tier0\memalloc.h"
+ $File "$SRCDIR\public\tier0\memdbgoff.h"
+ $File "$SRCDIR\public\tier0\memdbgon.h"
+ $File "$SRCDIR\public\tier1\mempool.h"
+ $File "$SRCDIR\public\mouthinfo.h"
+ $File "..\..\public\mxtk\mx.h"
+ $File "..\..\public\mxtk\mxBmp.h"
+ $File "..\..\public\mxtk\mxButton.h"
+ $File "..\..\public\mxtk\mxCheckBox.h"
+ $File "..\..\public\mxtk\mxChoice.h"
+ $File "..\..\public\mxtk\mxChooseColor.h"
+ $File "..\..\public\mxtk\mxEvent.h"
+ $File "..\..\public\mxtk\mxFileDialog.h"
+ $File "..\..\public\mxtk\mxGlWindow.h"
+ $File "..\..\public\mxtk\mxGroupBox.h"
+ $File "..\..\public\mxtk\mxImage.h"
+ $File "..\..\public\mxtk\mxInit.h"
+ $File "..\..\public\mxtk\mxLabel.h"
+ $File "..\..\public\mxtk\mxLineEdit.h"
+ $File "..\..\public\mxtk\mxLinkedList.h"
+ $File "..\..\public\mxtk\mxListBox.h"
+ $File "..\..\public\mxtk\mxlistview.h"
+ $File "..\..\public\mxtk\mxMatSysWindow.h"
+ $File "..\..\public\mxtk\mxMenu.h"
+ $File "..\..\public\mxtk\mxMenuBar.h"
+ $File "..\..\public\mxtk\mxMessageBox.h"
+ $File "..\..\public\mxtk\mxpath.h"
+ $File "..\..\public\mxtk\mxPcx.h"
+ $File "..\..\public\mxtk\mxPopupMenu.h"
+ $File "..\..\public\mxtk\mxProgressBar.h"
+ $File "..\..\public\mxtk\mxRadioButton.h"
+ $File "..\..\public\mxtk\mxScrollbar.h"
+ $File "..\..\public\mxtk\mxSlider.h"
+ $File "..\..\public\mxtk\mxstring.h"
+ $File "..\..\public\mxtk\mxTab.h"
+ $File "..\..\public\mxtk\mxTga.h"
+ $File "..\..\public\mxtk\mxToggleButton.h"
+ $File "..\..\public\mxtk\mxToolTip.h"
+ $File "..\..\public\mxtk\mxTreeView.h"
+ $File "..\..\public\mxtk\mxWidget.h"
+ $File "..\..\public\mxtk\mxWindow.h"
+ $File "$SRCDIR\public\networkvar.h"
+ $File "$SRCDIR\game\shared\npcevent.h"
+ $File "$SRCDIR\public\optimize.h"
+ $File "$SRCDIR\public\phonemeextractor\phonemeextractor.h"
+ $File "$SRCDIR\public\phyfile.h"
+ $File "..\common\physdll.h"
+ $File "..\hlmv\physmesh.h"
+ $File "$SRCDIR\public\tier0\platform.h"
+ $File "$SRCDIR\public\tier0\protected_things.h"
+ $File "$SRCDIR\public\vstdlib\random.h"
+ $File "..\sapi51\Include\sapi.h"
+ $File "..\sapi51\Include\sapiddk.h"
+ $File "..\common\scriplib.h"
+ $File "$SRCDIR\game\shared\sharedInterface.h"
+ $File "$SRCDIR\public\soundchars.h"
+ $File "$SRCDIR\public\soundflags.h"
+ $File "..\sapi51\Include\Spddkhlp.h"
+ $File "..\sapi51\Include\spdebug.h"
+ $File "..\sapi51\Include\sperror.h"
+ $File "..\sapi51\Include\sphelper.h"
+ $File "$SRCDIR\public\string_t.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "..\hlmv\studio_render.h"
+ $File "..\hlmv\StudioModel.h"
+ $File "$SRCDIR\public\texture_group_names.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ $File "$SRCDIR\public\tier1\utllinkedlist.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlmultilist.h"
+ $File "$SRCDIR\public\tier1\utlrbtree.h"
+ $File "$SRCDIR\public\tier1\utlsymbol.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\vcollide.h"
+ $File "$SRCDIR\public\vcollide_parse.h"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\mathlib\vector4d.h"
+ $File "$SRCDIR\public\vgui\VGUI.h"
+ $File "..\hlmv\ViewerSettings.h"
+ $File "$SRCDIR\public\mathlib\vmatrix.h"
+ $File "$SRCDIR\public\vphysics_interface.h"
+ $File "$SRCDIR\public\mathlib\vplane.h"
+ $File "$SRCDIR\public\tier0\vprof.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ $File "$SRCDIR\public\zip_uncompressed.h"
+ }
+
+ $Folder "Choreography"
+ {
+ $File "$SRCDIR\game\shared\choreoactor.h"
+ $File "$SRCDIR\game\shared\choreochannel.h"
+ $File "$SRCDIR\game\shared\choreoevent.h"
+ $File "$SRCDIR\game\shared\choreoscene.h"
+ $File "$SRCDIR\game\shared\ichoreoeventcallback.h"
+ }
+
+ $Folder "Audio Code"
+ {
+ $File "AudioWaveOutput.h"
+ $File "snd_audio_source.cpp"
+ $File "snd_audio_source.h"
+ $File "snd_wave_mixer.cpp"
+ $File "snd_wave_mixer.h"
+ $File "snd_wave_mixer_adpcm.cpp"
+ $File "snd_wave_mixer_adpcm.h"
+ $File "snd_wave_mixer_private.h"
+ $File "snd_wave_source.cpp"
+ $File "snd_wave_source.h"
+ $File "sound.cpp"
+ $File "sound.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib bitmap
+ $Lib choreoobjects
+ $Lib mathlib
+ $Lib $LIBCOMMON\mxtoolkitwin32
+ $Lib tier2
+ $Lib tier3
+ $Lib datamodel
+ $Lib dme_controls
+ $Lib dmserializers
+ $Lib matsys_controls
+ $Lib movieobjects
+ $Lib vgui_controls
+ $Lib $LIBCOMMON\lzma
+ }
+}
diff --git a/utils/hlfaceposer/ico00001.ico b/utils/hlfaceposer/ico00001.ico
new file mode 100644
index 0000000..d766cf9
--- /dev/null
+++ b/utils/hlfaceposer/ico00001.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00002.ico b/utils/hlfaceposer/ico00002.ico
new file mode 100644
index 0000000..6cda0f6
--- /dev/null
+++ b/utils/hlfaceposer/ico00002.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00003.ico b/utils/hlfaceposer/ico00003.ico
new file mode 100644
index 0000000..b61e1b1
--- /dev/null
+++ b/utils/hlfaceposer/ico00003.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00004.ico b/utils/hlfaceposer/ico00004.ico
new file mode 100644
index 0000000..ff2e77c
--- /dev/null
+++ b/utils/hlfaceposer/ico00004.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00005.ico b/utils/hlfaceposer/ico00005.ico
new file mode 100644
index 0000000..1fa01d2
--- /dev/null
+++ b/utils/hlfaceposer/ico00005.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00006.ico b/utils/hlfaceposer/ico00006.ico
new file mode 100644
index 0000000..8251647
--- /dev/null
+++ b/utils/hlfaceposer/ico00006.ico
Binary files differ
diff --git a/utils/hlfaceposer/ico00007.ico b/utils/hlfaceposer/ico00007.ico
new file mode 100644
index 0000000..35868d9
--- /dev/null
+++ b/utils/hlfaceposer/ico00007.ico
Binary files differ
diff --git a/utils/hlfaceposer/icon1.ico b/utils/hlfaceposer/icon1.ico
new file mode 100644
index 0000000..01d03a8
--- /dev/null
+++ b/utils/hlfaceposer/icon1.ico
Binary files differ
diff --git a/utils/hlfaceposer/icon2.ico b/utils/hlfaceposer/icon2.ico
new file mode 100644
index 0000000..41443c5
--- /dev/null
+++ b/utils/hlfaceposer/icon2.ico
Binary files differ
diff --git a/utils/hlfaceposer/ifaceposersound.h b/utils/hlfaceposer/ifaceposersound.h
new file mode 100644
index 0000000..59e9cb9
--- /dev/null
+++ b/utils/hlfaceposer/ifaceposersound.h
@@ -0,0 +1,55 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef IFACEPOSERSOUND_H
+#define IFACEPOSERSOUND_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class StudioModel;
+class CAudioSource;
+class CAudioMixer;
+class CAudioOuput;
+
+class IFacePoserSound
+{
+public:
+
+ virtual void Init( void ) = 0;
+ virtual void Shutdown( void ) = 0;
+ virtual void Update( float time ) = 0;
+ virtual void Flush( void ) = 0;
+
+ virtual CAudioSource *LoadSound( const char *wavfile ) = 0;
+
+ virtual void PlaySound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer ) = 0;
+ virtual void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ) = 0;
+ virtual void PlayPartialSound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample ) = 0;
+
+ virtual bool IsSoundPlaying( CAudioMixer *pMixer ) = 0;
+ virtual CAudioMixer *FindMixer( CAudioSource *source ) = 0;
+
+ virtual void StopAll( void ) = 0;
+ virtual void StopSound( CAudioMixer *mixer ) = 0;
+
+ virtual void RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr,
+ float starttime, float endtime, CAudioSource *pWave,
+ bool selected = false, int selectionstart = 0, int selectionend = 0 ) = 0;
+
+ virtual float GetAmountofTimeAhead( void ) = 0;
+ virtual int GetNumberofSamplesAhead( void ) = 0;
+
+ // virtual void InstallPhonemecallback( IPhonemeTag *pTagInterface ) = 0;
+
+ virtual CAudioOuput *GetAudioOutput( void ) = 0;
+
+ virtual void EnsureNoModelReferences( CAudioSource *source ) = 0;
+};
+
+extern IFacePoserSound *sound;
+
+#endif // IFACEPOSERSOUND_H
diff --git a/utils/hlfaceposer/ifaceposerworkspace.h b/utils/hlfaceposer/ifaceposerworkspace.h
new file mode 100644
index 0000000..83e3482
--- /dev/null
+++ b/utils/hlfaceposer/ifaceposerworkspace.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef IFACEPOSERWORKSPACE_H
+#define IFACEPOSERWORKSPACE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class IWorkspaceFiles
+{
+public:
+ enum
+ {
+ EXPRESSION = 0,
+ CHOREODATA,
+ MODELDATA,
+
+ NUM_FILE_TYPES
+ };
+
+ virtual void Init( char const *pchShortName ) = 0;
+
+ // Restore
+ virtual int GetNumStoredFiles( int type ) = 0;
+ virtual const char *GetStoredFile( int type, int number ) = 0;
+
+ // Save
+ virtual void StartStoringFiles( int type ) = 0;
+ virtual void FinishStoringFiles( int type ) = 0;
+ virtual void StoreFile( int type, const char *filename ) = 0;
+};
+
+extern IWorkspaceFiles *workspacefiles;
+
+#endif // IFACEPOSERWORKSPACE_H
diff --git a/utils/hlfaceposer/ifileloader.h b/utils/hlfaceposer/ifileloader.h
new file mode 100644
index 0000000..8f13b26
--- /dev/null
+++ b/utils/hlfaceposer/ifileloader.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef IFILELOADER_H
+#define IFILELOADER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+
+class CWaveFile;
+
+class IFileLoader
+{
+public:
+ virtual void AddWaveFilesToThread( CUtlVector< CWaveFile * >& wavefiles ) = 0;
+
+ virtual int ProcessCompleted() = 0;
+
+ virtual void Start() = 0;
+
+ virtual int GetPendingLoadCount() = 0;
+};
+
+extern IFileLoader *fileloader;
+
+#endif // IFILELOADER_H
diff --git a/utils/hlfaceposer/inputproperties.cpp b/utils/hlfaceposer/inputproperties.cpp
new file mode 100644
index 0000000..551d6f4
--- /dev/null
+++ b/utils/hlfaceposer/inputproperties.cpp
@@ -0,0 +1,80 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "resource.h"
+#include "InputProperties.h"
+#include "ChoreoView.h"
+#include "choreoactor.h"
+#include "mdlviewer.h"
+
+static CInputParams g_Params;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK InputPropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ SetDlgItemText( hwndDlg, IDC_INPUTSTRING, g_Params.m_szInputText );
+ SetDlgItemText( hwndDlg, IDC_STATIC_PROMPT, g_Params.m_szPrompt );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_INPUTSTRING ) );
+ SendMessage( GetDlgItem( hwndDlg, IDC_INPUTSTRING ), EM_SETSEL, 0, MAKELONG(0, 0xffff) );
+
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ g_Params.m_szInputText[ 0 ] = 0;
+ GetDlgItemText( hwndDlg, IDC_INPUTSTRING, g_Params.m_szInputText, sizeof( g_Params.m_szInputText ) );
+ EndDialog( hwndDlg, 1 );
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int InputProperties( CInputParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_INPUTDIALOG ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)InputPropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/inputproperties.h b/utils/hlfaceposer/inputproperties.h
new file mode 100644
index 0000000..250ccfa
--- /dev/null
+++ b/utils/hlfaceposer/inputproperties.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef INPUTPROPERTIES_H
+#define INPUTPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CInputParams : public CBaseDialogParams
+{
+ char m_szPrompt[ 256 ];
+
+ // i/o input text
+ char m_szInputText[ 1024 ];
+};
+
+// Display/create dialog
+int InputProperties( CInputParams *params );
+
+#endif // INPUTPROPERTIES_H
diff --git a/utils/hlfaceposer/mapentities.h b/utils/hlfaceposer/mapentities.h
new file mode 100644
index 0000000..7362701
--- /dev/null
+++ b/utils/hlfaceposer/mapentities.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MAPENTITIES_H
+#define MAPENTITIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class Vector;
+
+class IMapEntities
+{
+public:
+ virtual void CheckUpdateMap( char const *mapname ) = 0;
+ virtual bool LookupOrigin( char const *name, Vector& origin, QAngle& angles ) = 0;
+
+ virtual int Count() = 0;
+ virtual char const *GetName( int number ) = 0;
+
+};
+
+extern IMapEntities *mapentities;
+
+#endif // MAPENTITIES_H
diff --git a/utils/hlfaceposer/matsyswin.cpp b/utils/hlfaceposer/matsyswin.cpp
new file mode 100644
index 0000000..7675ddf
--- /dev/null
+++ b/utils/hlfaceposer/matsyswin.cpp
@@ -0,0 +1,806 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <mxtk/mx.h>
+#include <mxtk/mxMessageBox.h>
+#include <mxtk/mxTga.h>
+#include <mxtk/mxPcx.h>
+#include <mxtk/mxBmp.h>
+#include <mxtk/mxMatSysWindow.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "MatSysWin.h"
+#include "MDLViewer.h"
+#include "StudioModel.h"
+#include "ControlPanel.h"
+#include "ViewerSettings.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterialproxyfactory.h"
+#include "filesystem.h"
+#include <keyvalues.h>
+#include "materialsystem/imesh.h"
+#include "expressions.h"
+#include "hlfaceposer.h"
+#include "ifaceposersound.h"
+#include "materialsystem/IMaterialSystemHardwareConfig.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "istudiorender.h"
+#include "choreowidgetdrawhelper.h"
+#include "faceposer_models.h"
+#include "tier0/icommandline.h"
+#include "mathlib/vmatrix.h"
+#include "vstdlib/cvar.h"
+
+IFileSystem *filesystem = NULL;
+
+extern char g_appTitle[];
+
+// FIXME: move all this to mxMatSysWin
+
+class DummyMaterialProxyFactory : public IMaterialProxyFactory
+{
+public:
+ virtual IMaterialProxy *CreateProxy( const char *proxyName ) {return NULL;}
+ virtual void DeleteProxy( IMaterialProxy *pProxy ) {}
+};
+DummyMaterialProxyFactory g_DummyMaterialProxyFactory;
+
+
+static void ReleaseMaterialSystemObjects()
+{
+ StudioModel::ReleaseStudioModel();
+ models->ReleaseModels();
+}
+
+static void RestoreMaterialSystemObjects( int nChangeFlags )
+{
+ StudioModel::RestoreStudioModel();
+ models->RestoreModels();
+}
+
+void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig)
+{
+ pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS, true );
+}
+
+Vector g_vright( 50, 50, 0 ); // needs to be set to viewer's right in order for chrome to work
+
+IMaterial *g_materialBackground = NULL;
+IMaterial *g_materialWireframe = NULL;
+IMaterial *g_materialWireframeVertexColor = NULL;
+IMaterial *g_materialWireframeVertexColorNoCull = NULL;
+IMaterial *g_materialDebugCopyBaseTexture = NULL;
+IMaterial *g_materialFlatshaded = NULL;
+IMaterial *g_materialSmoothshaded = NULL;
+IMaterial *g_materialBones = NULL;
+IMaterial *g_materialLines = NULL;
+IMaterial *g_materialFloor = NULL;
+IMaterial *g_materialVertexColor = NULL;
+IMaterial *g_materialShadow = NULL;
+
+MatSysWindow *g_pMatSysWindow = 0;
+
+#define MATSYSWIN_NAME "3D View"
+
+MatSysWindow::MatSysWindow (mxWindow *parent, int x, int y, int w, int h, const char *label, int style)
+: IFacePoserToolWindow( "3D View", "3D View" ), mxMatSysWindow ( parent, x, y, w, h, label, style )
+{
+ g_pMaterialSystem->SetMaterialProxyFactory( &g_DummyMaterialProxyFactory );
+
+ SetAutoProcess( true );
+
+ setLabel( MATSYSWIN_NAME );
+
+ m_bSuppressSwap = false;
+
+ m_hWnd = (HWND)getHandle();
+
+ Con_Printf( "Setting material system video mode\n" );
+
+ MaterialSystem_Config_t config;
+ config = g_pMaterialSystem->GetCurrentConfigForVideoCard();
+ InitMaterialSystemConfig(&config);
+ g_pMaterialSystem->OverrideConfig( config, false );
+
+// config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0;
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
+
+ if (!g_pMaterialSystem->SetMode( ( void * )m_hWnd, config ) )
+ {
+ return;
+ }
+ g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
+ g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );
+
+ Con_Printf( "Loading debug materials\n" );
+
+ ITexture *pCubemapTexture = g_pMaterialSystem->FindTexture( "hlmv/cubemap", NULL, true );
+ pCubemapTexture->IncrementReferenceCount();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->BindLocalCubemap( pCubemapTexture );
+
+ g_materialBackground = g_pMaterialSystem->FindMaterial("particle/particleapp_background", TEXTURE_GROUP_OTHER, true);
+ g_materialWireframe = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true);
+ g_materialWireframeVertexColor = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
+
+ // test: create this from code - you need a vmt to make $nocull 1 happen, can't do it from the render context
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "Wireframe" );
+ pVMTKeyValues->SetInt("$ignorez", 1);
+ pVMTKeyValues->SetInt("$nocull", 1);
+ pVMTKeyValues->SetInt("$vertexcolor", 1);
+ pVMTKeyValues->SetInt("$decal", 1);
+ g_materialWireframeVertexColorNoCull = g_pMaterialSystem->CreateMaterial( "debug/wireframenocull", pVMTKeyValues );
+ }
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString("$basetexture", "vgui/white" );
+ g_materialDebugCopyBaseTexture = g_pMaterialSystem->CreateMaterial( "debug/copybasetexture", pVMTKeyValues );
+
+ }
+
+ g_materialFlatshaded = g_pMaterialSystem->FindMaterial("debug/debugdrawflatpolygons", TEXTURE_GROUP_OTHER, true);
+ g_materialSmoothshaded = g_pMaterialSystem->FindMaterial("debug/debugmrmfullbright2", TEXTURE_GROUP_OTHER, true);
+ g_materialBones = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true);
+ g_materialLines = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
+ g_materialFloor = g_pMaterialSystem->FindMaterial("hlmv/floor", TEXTURE_GROUP_OTHER, true);
+ g_materialVertexColor = g_pMaterialSystem->FindMaterial("debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true);
+ g_materialShadow = g_pMaterialSystem->FindMaterial("hlmv/shadow", TEXTURE_GROUP_OTHER, true);
+
+ if (!parent)
+ setVisible (true);
+ else
+ mx::setIdleWindow (this);
+
+ m_bSuppressResize = false;
+
+ m_stickyDepth = 0;
+ m_bIsSticky = false;
+ m_snapshotDepth = 0;
+}
+
+
+
+MatSysWindow::~MatSysWindow ()
+{
+ mx::setIdleWindow (0);
+}
+
+void MatSysWindow::redraw()
+{
+ BaseClass::redraw();
+return;
+ if ( IsLocked() )
+ {
+ RECT bounds;
+ GetClientRect( (HWND)getHandle(), &bounds );
+ bounds.bottom = bounds.top + GetCaptionHeight();
+ CChoreoWidgetDrawHelper helper( this, bounds );
+ HandleToolRedraw( helper );
+ }
+}
+
+#define MAX_FPS 250.0f
+#define MIN_TIMESTEP ( 1.0f / MAX_FPS )
+
+double realtime = 0.0f;
+
+void MatSysWindow::Frame( void )
+{
+ static bool recursion_guard = false;
+
+ static double prev = 0.0;
+ double curr = (double) mx::getTickCount () / 1000.0;
+ double dt = ( curr - prev );
+
+ if ( recursion_guard )
+ return;
+
+ recursion_guard = true;
+
+ // clamp to MAX_FPS
+ if ( dt >= 0.0 && dt < MIN_TIMESTEP )
+ {
+ Sleep( max( 0, (int)( ( MIN_TIMESTEP - dt ) * 1000.0f ) ) );
+
+ recursion_guard = false;
+ return;
+ }
+
+ if ( prev != 0.0 )
+ {
+ dt = min( 0.1, dt );
+
+ g_MDLViewer->Think( dt );
+
+ realtime += dt;
+ }
+
+ prev = curr;
+
+ DrawFrame();
+
+ recursion_guard = false;
+}
+
+void MatSysWindow::DrawFrame( void )
+{
+ if (!g_viewerSettings.pause)
+ {
+ redraw ();
+ }
+}
+
+int MatSysWindow::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ static float oldrx = 0, oldry = 0, oldtz = 50, oldtx = 0, oldty = 0;
+ static float oldlrx = 0, oldlry = 0;
+ static int oldx, oldy;
+
+ switch (event->event)
+ {
+ case mxEvent::Idle:
+ {
+ iret = 1;
+
+ Frame();
+ }
+ break;
+
+ case mxEvent::MouseDown:
+ {
+ StudioModel *pModel = models->GetActiveStudioModel();
+ if (!pModel)
+ break;
+ oldrx = pModel->m_angles[0];
+ oldry = pModel->m_angles[1];
+ oldtx = pModel->m_origin[0];
+ oldty = pModel->m_origin[1];
+ oldtz = pModel->m_origin[2];
+ oldx = (short)event->x;
+ oldy = (short)event->y;
+ oldlrx = g_viewerSettings.lightrot[0];
+ oldlry = g_viewerSettings.lightrot[1];
+ g_viewerSettings.pause = false;
+
+ float r = 1.0/3.0 * min( w(), h() );
+
+ float d = sqrt( ( float )( (event->x - w()/2) * (event->x - w()/2) + (event->y - h()/2) * (event->y - h()/2) ) );
+
+ if (d < r)
+ g_viewerSettings.rotating = false;
+ else
+ g_viewerSettings.rotating = true;
+
+ iret = 1;
+ }
+ break;
+
+ case mxEvent::MouseDrag:
+ {
+ StudioModel *pModel = models->GetActiveStudioModel();
+ if (!pModel)
+ break;
+
+ if (event->buttons & mxEvent::MouseLeftButton)
+ {
+ if (event->modifiers & mxEvent::KeyShift)
+ {
+ pModel->m_origin[1] = oldty - (float) ((short)event->x - oldx) * 0.1;
+ pModel->m_origin[2] = oldtz + (float) ((short)event->y - oldy) * 0.1;
+ }
+ else if (event->modifiers & mxEvent::KeyCtrl)
+ {
+ float ry = (float) (event->y - oldy);
+ float rx = (float) (event->x - oldx);
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement = QAngle( ry, rx, 0 );
+
+ matrix3x4_t tmp1, tmp2, tmp3;
+ AngleMatrix( g_viewerSettings.lightrot, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, g_viewerSettings.lightrot );
+ }
+ else
+ {
+ if (!g_viewerSettings.rotating)
+ {
+ float ry = (float) (event->y - oldy);
+ float rx = (float) (event->x - oldx);
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement;
+ matrix3x4_t tmp1, tmp2, tmp3;
+
+ movement = QAngle( 0, rx, 0 );
+ AngleMatrix( pModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp1, tmp2, tmp3 );
+ MatrixAngles( tmp3, pModel->m_angles );
+
+ movement = QAngle( ry, 0, 0 );
+ AngleMatrix( pModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, pModel->m_angles );
+ }
+ else
+ {
+ float ang1 = (180 / 3.1415) * atan2( oldx - w()/2.0, oldy - h()/2.0 );
+ float ang2 = (180 / 3.1415) * atan2( event->x - w()/2.0, event->y - h()/2.0 );
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement = QAngle( 0, 0, ang2 - ang1 );
+
+ matrix3x4_t tmp1, tmp2, tmp3;
+ AngleMatrix( pModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, pModel->m_angles );
+ }
+ }
+ }
+ else if (event->buttons & mxEvent::MouseRightButton)
+ {
+ pModel->m_origin[0] = oldtx + (float) ((short)event->y - oldy) * 0.1;
+ pModel->m_origin[0] = clamp( pModel->m_origin[0], 8.0f, 1024.0f );
+ }
+ redraw ();
+
+ iret = 1;
+ }
+ break;
+
+ case mxEvent::KeyDown:
+ {
+ iret = 1;
+ switch (event->key)
+ {
+ default:
+ iret = 0;
+ break;
+ case 116: // F5
+ {
+ g_MDLViewer->Refresh();
+ }
+ break;
+ case 32:
+ {
+ int iSeq = models->GetActiveStudioModel()->GetSequence();
+ if (iSeq == models->GetActiveStudioModel()->SetSequence (iSeq + 1))
+ {
+ g_pControlPanel->setSequence( 0 );
+ }
+ else
+ {
+ g_pControlPanel->setSequence( iSeq + 1 );
+ }
+ }
+ break;
+
+ case 27:
+ if (!getParent ()) // fullscreen mode ?
+ mx::quit ();
+ break;
+
+ case 'g':
+ g_viewerSettings.showGround = !g_viewerSettings.showGround;
+ break;
+
+ case 'h':
+ g_viewerSettings.showHitBoxes = !g_viewerSettings.showHitBoxes;
+ break;
+
+ case 'o':
+ g_viewerSettings.showBones = !g_viewerSettings.showBones;
+ break;
+
+ case 'b':
+ g_viewerSettings.showBackground = !g_viewerSettings.showBackground;
+ break;
+
+ case 'm':
+ g_viewerSettings.showMovement = !g_viewerSettings.showMovement;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ g_viewerSettings.renderMode = event->key - '1';
+ break;
+
+ case '-':
+ g_viewerSettings.speedScale -= 0.1f;
+ if (g_viewerSettings.speedScale < 0.0f)
+ g_viewerSettings.speedScale = 0.0f;
+ break;
+
+ case '+':
+ g_viewerSettings.speedScale += 0.1f;
+ if (g_viewerSettings.speedScale > 5.0f)
+ g_viewerSettings.speedScale = 5.0f;
+ break;
+ }
+ }
+ break;
+ } // switch (event->event)
+
+ return iret;
+}
+
+
+
+void
+drawFloor ()
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind(g_materialFloor);
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ {
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ float dist=-15000.0f;
+ float tMin=0, tMax=1;
+
+ meshBuilder.Position3f(-dist, dist, dist);
+ meshBuilder.TexCoord2f( 0, tMin,tMax );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, dist, dist);
+ meshBuilder.TexCoord2f( 0, tMax,tMax );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist,-dist, dist);
+ meshBuilder.TexCoord2f( 0, tMax,tMin );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f(-dist,-dist, dist);
+ meshBuilder.TexCoord2f( 0, tMin,tMin );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PopMatrix();
+}
+
+
+
+void
+setupRenderMode ()
+{
+}
+
+void MatSysWindow::SuppressBufferSwap( bool bSuppress )
+{
+ m_bSuppressSwap = bSuppress;
+}
+
+void MatSysWindow::draw ()
+{
+ int i;
+
+ g_pMaterialSystem->BeginFrame( 0 );
+ CUtlVector< StudioModel * > modellist;
+
+ modellist.AddToTail( models->GetActiveStudioModel() );
+
+ if ( models->CountVisibleModels() > 0 )
+ {
+ modellist.RemoveAll();
+ for ( i = 0; i < models->Count(); i++ )
+ {
+ if ( models->IsModelShownIn3DView( i ) )
+ {
+ modellist.AddToTail( models->GetStudioModel( i ) );
+ }
+ }
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->ClearBuffers(true, true);
+
+ int captiony = GetCaptionHeight();
+ int viewh = h2() - captiony;
+
+ g_pMaterialSystem->SetView( (HWND)getHandle() );
+
+ pRenderContext->Viewport( 0, captiony, w2(), viewh );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity( );
+ pRenderContext->PerspectiveX(20.0f, (float)w2() / (float)viewh, 1.0f, 20000.0f);
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+ // FIXME: why is this needed? Doesn't SetView() override this?
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ int modelcount = modellist.Count();
+ int countover2 = modelcount / 2;
+ int ydelta = g_pControlPanel->GetModelGap();
+ int yoffset = -countover2 * ydelta;
+ for ( i = 0 ; i < modelcount; i++ )
+ {
+ modellist[ i ]->IncrementFramecounter( );
+
+ Vector oldtrans = modellist[ i ]->m_origin;
+
+ modellist[ i ]->m_origin[ 1 ] = oldtrans[ 1 ] + yoffset;
+ yoffset += ydelta;
+
+ modellist[ i ]->GetStudioRender()->BeginFrame();
+ modellist[ i ]->DrawModel();
+ modellist[ i ]->GetStudioRender()->EndFrame();
+
+ modellist[ i ]->m_origin = oldtrans;
+ }
+
+ //
+ // draw ground
+ //
+ if (g_viewerSettings.showGround)
+ {
+ drawFloor ();
+ }
+
+ if (!m_bSuppressSwap)
+ {
+ g_pMaterialSystem->SwapBuffers();
+ }
+
+ g_pMaterialSystem->EndFrame();
+}
+
+void MatSysWindow::EnableStickySnapshotMode( )
+{
+ m_stickyDepth++;
+}
+
+void MatSysWindow::DisableStickySnapshotMode( )
+{
+ if (--m_stickyDepth == 0)
+ {
+ if (m_bIsSticky)
+ {
+ m_bIsSticky = false;
+
+ HWND wnd = (HWND)getHandle();
+
+ // Move back to original position
+ SetWindowPlacement( wnd, &m_wp );
+
+ SuppressResize( false );
+
+ SetCursor( m_hPrevCursor );
+ }
+ }
+}
+
+
+void MatSysWindow::PushSnapshotMode( int nSnapShotSize )
+{
+ if (m_snapshotDepth++ == 0)
+ {
+ if (m_stickyDepth)
+ {
+ if (m_bIsSticky)
+ return;
+
+ m_bIsSticky = true;
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );
+ }
+
+ SuppressResize( true );
+
+ RECT rcClient;
+ HWND wnd = (HWND)getHandle();
+
+ GetWindowPlacement( wnd, &m_wp );
+
+ GetClientRect( wnd, &rcClient );
+
+ MoveWindow( wnd, 0, 0, nSnapShotSize + 16, nSnapShotSize + 16, TRUE );
+ }
+}
+
+
+void MatSysWindow::PopSnapshotMode( )
+{
+ if (--m_snapshotDepth == 0)
+ {
+ if (m_stickyDepth == 0)
+ {
+ HWND wnd = (HWND)getHandle();
+
+ // Move back to original position
+ SetWindowPlacement( wnd, &m_wp );
+
+ SuppressResize( false );
+ }
+ }
+}
+
+
+void MatSysWindow::TakeSnapshotRect( const char *pFilename, int x, int y, int w, int h )
+{
+ int i;
+ HANDLE hf;
+ BITMAPFILEHEADER hdr;
+ BITMAPINFOHEADER bi;
+ DWORD dwTmp, imageSize;
+ byte *hp, b, *pBlue, *pRed;
+
+ w = ( w + 3 ) & ~3;
+
+ imageSize = w * h * 3;
+ // Create the file
+ hf = CreateFile( pFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if( hf == INVALID_HANDLE_VALUE )
+ {
+ return;
+ }
+
+ // file header
+ hdr.bfType = 0x4d42; // 'BM'
+ hdr.bfSize = (DWORD) ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + imageSize );
+ hdr.bfReserved1 = 0;
+ hdr.bfReserved2 = 0;
+ hdr.bfOffBits = (DWORD) ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) );
+
+ if( !WriteFile( hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL ) )
+ Error( "Couldn't write file header to snapshot.\n" );
+
+ // bitmap header
+ bi.biSize = sizeof(BITMAPINFOHEADER);
+ bi.biWidth = w;
+ bi.biHeight = h;
+ bi.biPlanes = 1;
+ bi.biBitCount = 24;
+ bi.biCompression = BI_RGB;
+ bi.biSizeImage = 0; //vid.rowbytes * vid.height;
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ if( !WriteFile( hf, (LPVOID) &bi, sizeof(BITMAPINFOHEADER), (LPDWORD) &dwTmp, NULL ) )
+ Error( "Couldn't write bitmap header to snapshot.\n" );
+
+ // bitmap bits
+ hp = (byte *) malloc(imageSize);
+
+ if (hp == NULL)
+ Error( "Couldn't allocate bitmap header to snapshot.\n" );
+
+ // Get Bits from the renderer
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->ReadPixels( x, y, w, h, hp, IMAGE_FORMAT_RGB888 );
+
+ // Invert vertically for BMP format
+ for (i = 0; i < h / 2; i++)
+ {
+ byte *top = hp + i * w * 3;
+ byte *bottom = hp + (h - i - 1) * w * 3;
+ for (int j = 0; j < w * 3; j++)
+ {
+ b = *top;
+ *top = *bottom;
+ *bottom = b;
+ top++;
+ bottom++;
+ }
+ }
+
+ // Reverse Red and Blue
+ pRed = hp;
+ pBlue = pRed + 2;
+ for(i = 0; i < w * h;i++)
+ {
+ b = *pRed;
+ *pRed = *pBlue;
+ *pBlue = b;
+ pBlue += 3;
+ pRed += 3;
+ }
+
+ if( !WriteFile( hf, (LPVOID)hp, imageSize, (LPDWORD) &dwTmp, NULL ) )
+ Error( "Couldn't write bitmap data snapshot.\n" );
+
+ free(hp);
+
+ // clean up
+ if( !CloseHandle( hf ) )
+ Error( "Couldn't close file for snapshot.\n" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool MatSysWindow::IsSuppressingResize( void )
+{
+ return m_bSuppressResize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : suppress -
+//-----------------------------------------------------------------------------
+void MatSysWindow::SuppressResize( bool suppress )
+{
+ m_bSuppressResize = suppress;
+}
+
+void
+MatSysWindow::TakeScreenShot (const char *filename)
+{
+ redraw ();
+ int w = w2 ();
+ int h = h2 ();
+
+ mxImage *image = new mxImage ();
+ if (image->create (w, h, 24))
+ {
+#if 0
+ glReadBuffer (GL_FRONT);
+ glReadPixels (0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, image->data);
+#else
+ HDC hdc = GetDC ((HWND) getHandle ());
+ byte *data = (byte *) image->data;
+ int i = 0;
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ COLORREF cref = GetPixel (hdc, x, y);
+ data[i++] = (byte) ((cref >> 0)& 0xff);
+ data[i++] = (byte) ((cref >> 8) & 0xff);
+ data[i++] = (byte) ((cref >> 16) & 0xff);
+ }
+ }
+ ReleaseDC ((HWND) getHandle (), hdc);
+#endif
+ if (!mxTgaWrite (filename, image))
+ mxMessageBox (this, "Error writing screenshot.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+
+ delete image;
+ }
+}
diff --git a/utils/hlfaceposer/matsyswin.h b/utils/hlfaceposer/matsyswin.h
new file mode 100644
index 0000000..3985168
--- /dev/null
+++ b/utils/hlfaceposer/matsyswin.h
@@ -0,0 +1,210 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MATSYSWIN_H
+#define MATSYSWIN_H
+#pragma once
+
+
+#include <mxtk/mxMatSysWindow.h>
+#include "materialsystem/imaterialsystem.h"
+#include "faceposertoolwindow.h"
+#include "interface.h"
+
+
+class MatSysWindow : public mxMatSysWindow, public IFacePoserToolWindow
+{
+ typedef mxMatSysWindow BaseClass;
+public:
+
+ // CREATORS
+ MatSysWindow( mxWindow *parent, int x, int y, int w, int h, const char *label, int style );
+ ~MatSysWindow( );
+
+ // MANIPULATORS
+ virtual int handleEvent( mxEvent *event );
+ virtual void draw( );
+
+ virtual void redraw();
+
+ void EnableStickySnapshotMode( void );
+ void DisableStickySnapshotMode( void );
+ void PushSnapshotMode( int nSnapShotSize );
+ void PopSnapshotMode( void );
+
+ void TakeSnapshotRect( const char *pFilename, int x, int y, int w, int h );
+ bool IsSuppressingResize( void );
+ void SuppressResize( bool suppress );
+
+ void TakeScreenShot(const char *filename);
+
+ void Frame( void );
+ void DrawFrame( void );
+
+ void SuppressBufferSwap( bool bSuppress );
+
+ void *m_hWnd;
+
+private:
+ bool m_bSuppressResize;
+ bool m_bSuppressSwap;
+
+ // stack and sticky window mode
+ int m_stickyDepth;
+ bool m_bIsSticky;
+ int m_snapshotDepth;
+ WINDOWPLACEMENT m_wp;
+ HCURSOR m_hPrevCursor;
+};
+
+extern MatSysWindow *g_pMatSysWindow;
+
+extern IMaterialSystem *g_pMaterialSystem;
+extern IMaterial *g_materialBackground;
+extern IMaterial *g_materialWireframe;
+extern IMaterial *g_materialWireframe;
+extern IMaterial *g_materialWireframeVertexColor;
+extern IMaterial *g_materialWireframeVertexColorNoCull;
+extern IMaterial *g_materialDebugCopyBaseTexture;
+extern IMaterial *g_materialFlatshaded;
+extern IMaterial *g_materialSmoothshaded;
+extern IMaterial *g_materialBones;
+extern IMaterial *g_materialLines;
+extern IMaterial *g_materialFloor;
+
+
+#if 0
+
+typedef struct
+{
+ int width;
+ int height;
+ int bpp;
+ int flags;
+ int frequency;
+} screen_res_t;
+
+
+
+typedef struct
+{
+ int width;
+ int height;
+ int bpp;
+} devinfo_t;
+
+
+
+class MaterialSystemApp
+{
+public:
+
+ MaterialSystemApp();
+ ~MaterialSystemApp();
+
+ void Term();
+
+ // Post a message to shutdown the app.
+ void AppShutdown();
+
+ int WinMain(void *hInstance, void *hPrevInstance, char *szCmdLine, int iCmdShow);
+ long WndProc(void *hwnd, long iMsg, long wParam, long lParam);
+
+ int FindNumParameter(const char *s, int defaultVal=-1);
+ bool FindParameter(const char *s);
+ const char* FindParameterArg(const char *s);
+
+ void SetTitleText(PRINTF_FORMAT_STRING const char *fmt, ...);
+
+
+private:
+
+ bool InitMaterialSystem();
+ void Clear();
+
+ bool CreateMainWindow(int width, int height, int bpp, bool fullscreen);
+
+ void RenderScene();
+
+ void MouseCapture();
+ void MouseRelease();
+
+ void GetParameters();
+
+
+public:
+ IMaterialSystem *m_pMaterialSystem;
+ void *m_hMaterialSystemInst;
+
+ devinfo_t m_DevInfo;
+
+ void *m_hInstance;
+ int m_iCmdShow;
+ void *m_hWnd;
+ void *m_hDC;
+ bool m_bActive;
+ bool m_bFullScreen;
+ int m_width;
+ int m_height;
+ int m_centerx; // for mouse offset calculations
+ int m_centery;
+ int m_bpp;
+ BOOL m_bChangeBPP;
+ BOOL m_bAllowSoft;
+ BOOL m_bPaused;
+ int m_glnWidth;
+ int m_glnHeight;
+ float m_gldAspect;
+ float m_NearClip;
+ float m_FarClip;
+ float m_fov;
+
+ screen_res_t *m_pResolutions;
+ int m_iResCount;
+
+ int m_iVidMode;
+};
+
+
+// ---------------------------------------------------------------------------------------- //
+// Global functions
+// ---------------------------------------------------------------------------------------- //
+
+// Show an error dialog and quit.
+bool Sys_Error(PRINTF_FORMAT_STRING const char *pMsg, ...);
+
+// Print to the trace window.
+void con_Printf(PRINTF_FORMAT_STRING const char *pMsg, ...);
+
+// Returns true if the key is down.
+bool IsKeyDown(char key);
+
+
+
+extern MaterialSystemApp g_MaterialSystemApp;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned int g_Time;
+
+#ifdef __cplusplus
+};
+#endif
+
+
+#endif
+
+#endif // GLAPP_H
diff --git a/utils/hlfaceposer/mdlviewer.cpp b/utils/hlfaceposer/mdlviewer.cpp
new file mode 100644
index 0000000..7c4d2ba
--- /dev/null
+++ b/utils/hlfaceposer/mdlviewer.cpp
@@ -0,0 +1,2752 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+#include "cbase.h"
+#include <direct.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include <mxtk/mxTga.h>
+#include <mxtk/mxEvent.h>
+#include "mdlviewer.h"
+#include "ViewerSettings.h"
+#include "MatSysWin.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include "StudioModel.h"
+#include "mxExpressionTray.h"
+#include "mxStatusWindow.h"
+#include "ChoreoView.h"
+#include "ifaceposersound.h"
+#include "ifaceposerworkspace.h"
+#include "expclass.h"
+#include "PhonemeEditor.h"
+#include "filesystem.h"
+#include "ExpressionTool.h"
+#include "ControlPanel.h"
+#include "choreowidgetdrawhelper.h"
+#include "choreoviewcolors.h"
+#include "tabwindow.h"
+#include "faceposer_models.h"
+#include "choiceproperties.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "tier1/strtools.h"
+#include "InputProperties.h"
+#include "GestureTool.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "inputsystem/iinputsystem.h"
+#include "RampTool.h"
+#include "SceneRampTool.h"
+#include "tier0/icommandline.h"
+#include "phonemeextractor/PhonemeExtractor.h"
+#include "animationbrowser.h"
+#include "CloseCaptionTool.h"
+#include "wavebrowser.h"
+#include "vcdbrowser.h"
+#include "ifilesystemopendialog.h"
+#include <vgui/ILocalize.h>
+#include <vgui/IVGui.h>
+#include "appframework/appframework.h"
+#include "icvar.h"
+#include "vstdlib/cvar.h"
+#include "istudiorender.h"
+#include "materialsystem/imaterialsystem.h"
+#include "vphysics_interface.h"
+#include "Datacache/imdlcache.h"
+#include "datacache/idatacache.h"
+#include "filesystem_init.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "tier1/strtools.h"
+#include "appframework/tier3app.h"
+#include "faceposer_vgui.h"
+#include "vguiwnd.h"
+#include "vgui_controls/Frame.h"
+#include "vgui/ISurface.h"
+#include "p4lib/ip4.h"
+#include "tier2/p4helpers.h"
+#include "ProgressDialog.h"
+#include "scriplib.h"
+
+#define WINDOW_TAB_OFFSET 24
+
+MDLViewer *g_MDLViewer = 0;
+char g_appTitle[] = "Half-Life Face Poser";
+static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" };
+
+using namespace vgui;
+
+//-----------------------------------------------------------------------------
+// Singleton interfaces
+//-----------------------------------------------------------------------------
+IPhysicsSurfaceProps *physprop;
+IPhysicsCollision *physcollision;
+IStudioDataCache *g_pStudioDataCache;
+vgui::ILocalize *g_pLocalize = NULL;
+ISoundEmitterSystemBase *soundemitter = NULL;
+CreateInterfaceFn g_Factory;
+IFileSystem *g_pFileSystem = NULL;
+
+bool g_bInError = false;
+
+static char gamedir[MAX_PATH]; // full path to gamedir U:\main\game\ep2
+static char gamedirsimple[MAX_PATH]; // just short name: ep2
+
+// Filesystem dialog module wrappers.
+CSysModule *g_pFSDialogModule = 0;
+CreateInterfaceFn g_FSDialogFactory = 0;
+
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/Button.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/ComboBox.h"
+#include "tier1/fmtstr.h"
+
+class CFacePoserVguiFrame : public Frame
+{
+ DECLARE_CLASS_SIMPLE( CFacePoserVguiFrame, Frame );
+
+public:
+ CFacePoserVguiFrame( Panel *parent, const char *panelName ) :
+ BaseClass( parent, panelName )
+ {
+ SetTitle( panelName, true );
+
+ SetTitleBarVisible( false );
+
+ SetSizeable( false );
+ SetMoveable( false );
+
+ SetPaintBackgroundEnabled( true );
+ SetCloseButtonVisible( false );
+ m_pEntry = new TextEntry( this, "textentry" );
+ m_pEntry->AddActionSignalTarget( this );
+ m_pButton = new Button( this, "button", "Button1", this );
+ m_pButton->SetCommand( new KeyValues( "OnButtonPressed" ) );
+ m_pLabel = new Label( this, "label", "..." );
+
+ m_pCombo = new ComboBox( this, "combo", 5, true );
+ for ( int i = 0; i < 10; ++i )
+ {
+ m_pCombo->AddItem( CFmtStr( "item%02d", i + 1 ), NULL );
+ }
+ }
+
+ MESSAGE_FUNC( OnButtonPressed, "OnButtonPressed" )
+ {
+ Msg( "OnButtonPressed\n" );
+ }
+
+ MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", str )
+ {
+ char sz[ 256 ];
+ m_pEntry->GetText( sz, sizeof( sz ) );
+ m_pLabel->SetText( sz );
+
+ m_pCombo->GetText( sz, sizeof( sz ) );
+ Msg( "Combo %s\n", sz );
+ }
+
+ virtual void PerformLayout()
+ {
+ BaseClass::PerformLayout();
+
+ int w, h;
+ GetSize( w, h );
+
+ int y = 30;
+ int skip = 20;
+ m_pEntry->SetBounds( 5, y, w, skip - 2 );
+ y += skip;
+ m_pButton->SetBounds( 5, y, w, skip - 2 );
+ y += skip;
+ m_pCombo->SetBounds( 5, y, w, skip - 2 );
+ y += skip;
+ m_pLabel->SetBounds( 5, y, w, skip - 2 );
+ y += skip;
+
+ }
+
+private:
+
+ TextEntry *m_pEntry;
+ Button *m_pButton;
+ Label *m_pLabel;
+ ComboBox *m_pCombo;
+};
+
+class TestWindow : public CVGuiPanelWnd, public IFacePoserToolWindow
+{
+ typedef CVGuiPanelWnd BaseClass;
+
+public:
+
+ TestWindow( mxWindow *parent, int x, int y, int w, int h) :
+ BaseClass(parent, x, y, w, h ),
+ IFacePoserToolWindow( "FacePoser Frame", "FacePoser Frame" )
+ {
+ CFacePoserVguiFrame *f = new CFacePoserVguiFrame( NULL, "FacePoser Frame" );
+
+ SetParentWindow( this );
+ SetMainPanel( f );
+ f->SetVisible( true );
+ f->SetPaintBackgroundEnabled( true );
+
+ FacePoser_MakeToolWindow( this, true );
+ }
+
+ virtual int handleEvent( mxEvent *event )
+ {
+ if ( HandleToolEvent( event ) )
+ return 1;
+ return BaseClass::handleEvent( event );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// FIXME: Remove this crap (from cmdlib.cpp)
+// We can't include cmdlib owing to appframework incompatibilities
+//-----------------------------------------------------------------------------
+void Q_mkdir( const char *path )
+{
+#if defined( _WIN32 ) || defined( WIN32 )
+ if (_mkdir (path) != -1)
+ return;
+#else
+ if (mkdir (path, 0777) != -1)
+ return;
+#endif
+ if (errno != EEXIST)
+ {
+ Error ("mkdir %s: %s",path, strerror(errno));
+ }
+}
+
+void CreatePath( const char *relative )
+{
+ char fullpath[ 512 ];
+ Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", GetGameDirectory(), relative );
+
+ char *path = fullpath;
+
+ char *ofs, c;
+
+ if (path[1] == ':')
+ {
+ path += 2;
+ }
+
+ for (ofs = const_cast<char*>(path+1); *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ {
+ // create the directory, but not if it's actually a filename with a dot in it!!!
+ *ofs = 0;
+ if ( !Q_stristr( path, "." ) )
+ {
+ Q_mkdir (path);
+ }
+ *ofs = c;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LoadFile
+//-----------------------------------------------------------------------------
+int LoadFile (const char *filename, void **bufferptr)
+{
+ FileHandle_t f = filesystem->Open( filename, "rb" );
+ int length = filesystem->Size( f );
+ void *buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ if ( filesystem->Read (buffer, length, f) != (int)length )
+ {
+ Error ("File read failure");
+ }
+ filesystem->Close (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+char *ExpandPath(char *path)
+{
+ static char full[1024];
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
+ return path;
+
+ V_sprintf_safe( full, "%s%s", gamedir, path );
+ return full;
+}
+
+
+//-----------------------------------------------------------------------------
+// This is here because scriplib.cpp is included in this project but cmdlib.cpp
+// is not, but scriplib.cpp uses some stuff from cmdlib.cpp, same with
+// LoadFile and ExpandPath above. The only thing that currently uses this
+// is $include in scriptlib, if this function returns 0, $include will
+// behave the way it did before this change
+//-----------------------------------------------------------------------------
+int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath )
+{
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: Move into appsystem framework
+//-----------------------------------------------------------------------------
+void LoadFileSystemDialogModule()
+{
+ Assert( !g_pFSDialogModule );
+
+ // Load the module with the file system open dialog.
+ const char *pDLLName = "FileSystemOpenDialog.dll";
+ g_pFSDialogModule = Sys_LoadModule( pDLLName );
+ if ( g_pFSDialogModule )
+ {
+ g_FSDialogFactory = Sys_GetFactory( g_pFSDialogModule );
+ }
+
+ if ( !g_pFSDialogModule || !g_FSDialogFactory )
+ {
+ if ( g_pFSDialogModule )
+ {
+ Sys_UnloadModule( g_pFSDialogModule );
+ g_pFSDialogModule = NULL;
+ }
+ }
+}
+
+void UnloadFileSystemDialogModule()
+{
+ if ( g_pFSDialogModule )
+ {
+ Sys_UnloadModule( g_pFSDialogModule );
+ g_pFSDialogModule = 0;
+ }
+}
+
+
+
+void
+MDLViewer::initRecentFiles ()
+{
+ for (int i = 0; i < 8; i++)
+ {
+ if (strlen (recentFiles[i]))
+ {
+ mb->modify (IDC_FILE_RECENTFILES1 + i, IDC_FILE_RECENTFILES1 + i, recentFiles[i]);
+ }
+ else
+ {
+ mb->modify (IDC_FILE_RECENTFILES1 + i, IDC_FILE_RECENTFILES1 + i, "(empty)");
+ mb->setEnabled (IDC_FILE_RECENTFILES1 + i, false);
+ }
+ }
+}
+
+
+#define RECENTFILESPATH "/hlfaceposer.rf"
+void
+MDLViewer::loadRecentFiles ()
+{
+ char path[256];
+ strcpy (path, mx::getApplicationPath ());
+ strcat (path, RECENTFILESPATH);
+ FILE *file = fopen (path, "rb");
+ if (file)
+ {
+ fread (recentFiles, sizeof recentFiles, 1, file);
+ fclose (file);
+ }
+}
+
+
+
+void
+MDLViewer::saveRecentFiles ()
+{
+ char path[256];
+
+ strcpy (path, mx::getApplicationPath ());
+ strcat (path, RECENTFILESPATH);
+
+ FILE *file = fopen (path, "wb");
+ if (file)
+ {
+ fwrite (recentFiles, sizeof recentFiles, 1, file);
+ fclose (file);
+ }
+}
+
+bool MDLViewer::AreSoundScriptsDirty()
+{
+ // Save any changed sound script files
+ int c = soundemitter->GetNumSoundScripts();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( soundemitter->IsSoundScriptDirty( i ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MDLViewer::CanClose()
+{
+ Con_Printf( "Checking for vcd changes...\n" );
+
+ if ( m_bVCDSaved )
+ {
+ int retval = mxMessageBox( NULL, "Rebuild scenes.image?", g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return false;
+ }
+
+ m_bVCDSaved = false;
+ if ( retval == 0 ) // YES
+ {
+ OnRebuildScenesImage();
+ }
+ }
+
+ Con_Printf( "Checking for sound script changes...\n" );
+
+ // Save any changed sound script files
+ int c = soundemitter->GetNumSoundScripts();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !soundemitter->IsSoundScriptDirty( i ) )
+ continue;
+
+ char const *scriptname = soundemitter->GetSoundScriptName( i );
+ if ( !scriptname )
+ continue;
+
+ if ( !filesystem->FileExists( scriptname ) ||
+ !filesystem->IsFileWritable( scriptname ) )
+ {
+ continue;
+ }
+
+ int retval = mxMessageBox( NULL, va( "Save changes to sound script '%s'?", scriptname ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return false;
+ }
+
+ if ( retval == 0 )
+ {
+ soundemitter->SaveChangesToSoundScript( i );
+ }
+ }
+
+ SaveWindowPositions();
+
+ models->SaveModelList();
+ models->CloseAllModels();
+
+ return true;
+}
+
+bool MDLViewer::Closing( void )
+{
+ return true;
+}
+
+#define IDC_GRIDSETTINGS_FPS 1001
+#define IDC_GRIDSETTINGS_SNAP 1002
+
+class CFlatButton : public mxButton
+{
+public:
+ CFlatButton( mxWindow *parent, int id )
+ : mxButton( parent, 0, 0, 0, 0, "", id )
+ {
+ HWND wnd = (HWND)getHandle();
+ DWORD exstyle = GetWindowLong( wnd, GWL_EXSTYLE );
+ exstyle |= WS_EX_CLIENTEDGE;
+ SetWindowLong( wnd, GWL_EXSTYLE, exstyle );
+
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ style &= ~WS_BORDER;
+ SetWindowLong( wnd, GWL_STYLE, style );
+
+ }
+};
+
+class CMDLViewerGridSettings : public mxWindow
+{
+public:
+ typedef mxWindow BaseClass;
+
+ CMDLViewerGridSettings( mxWindow *parent, int x, int y, int w, int h ) :
+ mxWindow( parent, x, y, w, h )
+ {
+ FacePoser_AddWindowStyle( this, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
+ m_btnFPS = new CFlatButton( this, IDC_GRIDSETTINGS_FPS );
+ m_btnGridSnap = new CFlatButton( this, IDC_GRIDSETTINGS_SNAP );
+
+ }
+
+ void Init( void )
+ {
+ if ( g_pChoreoView )
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ char sz[ 256 ];
+ Q_snprintf( sz, sizeof( sz ), "%i fps", scene->GetSceneFPS() );
+ m_btnFPS->setLabel( sz );
+
+ Q_snprintf( sz, sizeof( sz ), "snap: %s", scene->IsUsingFrameSnap() ? "on" : "off" );
+ m_btnGridSnap->setLabel( sz );
+
+ m_btnFPS->setVisible( true );
+ m_btnGridSnap->setVisible( true );
+ return;
+ }
+ }
+
+ m_btnFPS->setVisible( false );
+ m_btnGridSnap->setVisible( false );
+ }
+
+ virtual int handleEvent( mxEvent *event )
+ {
+ int iret = 0;
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Size:
+ {
+ int leftedge = w2() * 0.45f;
+ m_btnFPS->setBounds( 0, 0, leftedge, h2() );
+ m_btnGridSnap->setBounds( leftedge, 0, w2() - leftedge, h2() );
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_GRIDSETTINGS_FPS:
+ {
+ if ( g_pChoreoView )
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ int currentFPS = scene->GetSceneFPS();
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+
+ strcpy( params.m_szDialogTitle, "Change FPS" );
+
+ Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ),
+ "%i", currentFPS );
+
+ strcpy( params.m_szPrompt, "Current FPS:" );
+
+ if ( InputProperties( &params ) )
+ {
+ int newFPS = atoi( params.m_szInputText );
+
+ if ( ( newFPS > 0 ) && ( newFPS != currentFPS ) )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Change Scene FPS" );
+ scene->SetSceneFPS( newFPS );
+ g_pChoreoView->PushRedo( "Change Scene FPS" );
+ Init();
+
+ Con_Printf( "FPS changed to %i\n", newFPS );
+ }
+ }
+
+ }
+ }
+ }
+ break;
+ case IDC_GRIDSETTINGS_SNAP:
+ {
+ if ( g_pChoreoView )
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( "Change Snap Frame" );
+
+ scene->SetUsingFrameSnap( !scene->IsUsingFrameSnap() );
+
+ g_pChoreoView->PushRedo( "Change Snap Frame" );
+
+ Init();
+
+ Con_Printf( "Time frame snapping: %s\n",
+ scene->IsUsingFrameSnap() ? "on" : "off" );
+ }
+ }
+
+
+ }
+ break;
+ }
+ }
+ }
+ return iret;
+ }
+
+ bool PaintBackground( void )
+ {
+ CChoreoWidgetDrawHelper drawHelper( this );
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+ drawHelper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rc );
+ return false;
+ }
+
+private:
+
+ CFlatButton *m_btnFPS;
+ CFlatButton *m_btnGridSnap;
+};
+
+
+#define IDC_MODELTAB_LOAD 1000
+#define IDC_MODELTAB_CLOSE 1001
+#define IDC_MODELTAB_CLOSEALL 1002
+#define IDC_MODELTAB_CENTERONFACE 1003
+#define IDC_MODELTAB_ASSOCIATEACTOR 1004
+#define IDC_MODELTAB_TOGGLE3DVIEW 1005
+#define IDC_MODELTAB_SHOWALL 1006
+#define IDC_MODELTAB_HIDEALL 1007
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CMDLViewerModelTab : public CTabWindow
+{
+public:
+ typedef CTabWindow BaseClass;
+
+ CMDLViewerModelTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
+ CTabWindow( parent, x, y, w, h, id, style )
+ {
+ SetInverted( true );
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my )
+ {
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ char const *current = "";
+ char const *filename = "";
+ int idx = getSelectedIndex();
+ if ( idx >= 0 )
+ {
+ current = models->GetModelName( idx );
+ filename = models->GetModelFileName( idx );
+ }
+
+ if ( models->Count() < MAX_FP_MODELS )
+ {
+ pop->add( "Load Model...", IDC_MODELTAB_LOAD );
+ }
+ if ( idx >= 0 )
+ {
+ pop->add( va( "Close '%s'", current ), IDC_MODELTAB_CLOSE );
+ }
+ if ( models->Count() > 0 )
+ {
+ pop->add( "Close All", IDC_MODELTAB_CLOSEALL );
+ }
+ if ( idx >= 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Center %s's face", current ), IDC_MODELTAB_CENTERONFACE );
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ // See if there is already an actor with this model associated
+ int c = scene->GetNumActors();
+ bool hasassoc = false;
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ Assert( a );
+
+ if ( stricmp( a->GetFacePoserModelName(), filename ) )
+ continue;
+ hasassoc = true;
+ break;
+ }
+
+ if ( hasassoc )
+ {
+ pop->add( va( "Change associated actor for %s", current ), IDC_MODELTAB_ASSOCIATEACTOR );
+ }
+ else
+ {
+ pop->add( va( "Associate actor to %s", current ), IDC_MODELTAB_ASSOCIATEACTOR );
+ }
+ }
+
+ pop->addSeparator();
+
+ bool visible = models->IsModelShownIn3DView( idx );
+ if ( visible )
+ {
+ pop->add( va( "Remove %s from 3D View", current ), IDC_MODELTAB_TOGGLE3DVIEW );
+ }
+ else
+ {
+ pop->add( va( "Show %s in 3D View", current ), IDC_MODELTAB_TOGGLE3DVIEW );
+ }
+ }
+ if ( models->Count() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( "Show All", IDC_MODELTAB_SHOWALL );
+ pop->add( "Hide All", IDC_MODELTAB_HIDEALL );
+ }
+
+ // Convert click position
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ // Convert coordinate space
+ pop->popup( this, pt.x, pt.y );
+ }
+
+ virtual int handleEvent( mxEvent *event )
+ {
+ int iret = 0;
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_MODELTAB_SHOWALL:
+ case IDC_MODELTAB_HIDEALL:
+ {
+ bool show = ( event->action == IDC_MODELTAB_SHOWALL ) ? true : false;
+ int c = models->Count();
+ for ( int i = 0; i < c ; i++ )
+ {
+ models->ShowModelIn3DView( i, show );
+ }
+ }
+ break;
+ case IDC_MODELTAB_LOAD:
+ {
+ if ( ! CommandLine()->FindParm( "-NoSteamDialog" ) )
+ {
+ g_MDLViewer->LoadModel_Steam();
+ }
+ else
+ {
+ char modelfile[ 512 ];
+ if ( FacePoser_ShowOpenFileNameDialog( modelfile, sizeof( modelfile ), "models", "*.mdl" ) )
+ {
+ g_MDLViewer->LoadModelFile( modelfile );
+ }
+ }
+ }
+ break;
+ case IDC_MODELTAB_CLOSE:
+ {
+ int idx = getSelectedIndex();
+ if ( idx >= 0 )
+ {
+ models->FreeModel( idx );
+ }
+ }
+ break;
+ case IDC_MODELTAB_CLOSEALL:
+ {
+ models->CloseAllModels();
+ }
+ break;
+ case IDC_MODELTAB_CENTERONFACE:
+ {
+ g_pControlPanel->CenterOnFace();
+ }
+ break;
+ case IDC_MODELTAB_TOGGLE3DVIEW:
+ {
+ int idx = getSelectedIndex();
+ if ( idx >= 0 )
+ {
+ bool visible = models->IsModelShownIn3DView( idx );
+ models->ShowModelIn3DView( idx, !visible );
+ }
+ }
+ break;
+ case IDC_MODELTAB_ASSOCIATEACTOR:
+ {
+ int idx = getSelectedIndex();
+ if ( idx >= 0 )
+ {
+ char const *modelname = models->GetModelFileName( idx );
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ CChoiceParams params;
+ strcpy( params.m_szDialogTitle, "Associate Actor" );
+
+ params.m_bPositionDialog = false;
+ params.m_nLeft = 0;
+ params.m_nTop = 0;
+ strcpy( params.m_szPrompt, "Choose actor:" );
+
+ params.m_Choices.RemoveAll();
+
+ params.m_nSelected = -1;
+ int oldsel = -1;
+
+ int c = scene->GetNumActors();
+ ChoiceText text;
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ Assert( a );
+
+
+ strcpy( text.choice, a->GetName() );
+
+ if ( !stricmp( a->GetFacePoserModelName(), modelname ) )
+ {
+ params.m_nSelected = i;
+ oldsel = -1;
+ }
+
+ params.m_Choices.AddToTail( text );
+ }
+
+ if ( ChoiceProperties( &params ) &&
+ params.m_nSelected != oldsel )
+ {
+
+ // Chose something new...
+ CChoreoActor *a = scene->GetActor( params.m_nSelected );
+
+ g_pChoreoView->AssociateModelToActor( a, idx );
+ }
+ }
+ }
+
+ }
+ }
+ }
+ break;
+ }
+ if ( iret )
+ return iret;
+ return BaseClass::handleEvent( event );
+ }
+
+
+ void HandleModelSelect( void )
+ {
+ int idx = getSelectedIndex();
+ if ( idx < 0 )
+ return;
+
+ // FIXME: Do any necessary window resetting here!!!
+ g_pControlPanel->ChangeModel( models->GetModelFileName( idx ) );
+ }
+
+ void Init( void )
+ {
+ removeAll();
+
+ int c = models->Count();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ char const *name = models->GetModelName( i );
+
+ // Strip it down to the base name
+ char cleanname[ 256 ];
+ Q_FileBase( name, cleanname, sizeof( cleanname ) );
+
+ add( cleanname );
+ }
+ }
+};
+
+#define IDC_TOOL_TOGGLEVISIBILITY 1000
+#define IDC_TOOL_TOGGLELOCK 1001
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CMDLViewerWindowTab : public CTabWindow
+{
+public:
+ typedef CTabWindow BaseClass;
+
+ CMDLViewerWindowTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
+ CTabWindow( parent, x, y, w, h, id, style )
+ {
+ SetInverted( true );
+
+ m_nLastSelected = -1;
+ m_flLastSelectedTime = -1;
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my )
+ {
+ IFacePoserToolWindow *tool = GetSelectedTool();
+ if ( !tool )
+ return;
+
+ mxWindow *toolw = tool->GetMxWindow();
+ if ( !toolw )
+ return;
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ bool isVisible = toolw->isVisible();
+ bool isLocked = tool->IsLocked();
+
+ pop->add( isVisible ? "Hide" : "Show", IDC_TOOL_TOGGLEVISIBILITY );
+ pop->add( isLocked ? "Unlock" : "Lock", IDC_TOOL_TOGGLELOCK );
+
+ // Convert click position
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+
+ /*
+ ClientToScreen( (HWND)getHandle(), &pt );
+ ScreenToClient( (HWND)g_MDLViewer->getHandle(), &pt );
+ */
+
+ // Convert coordinate space
+ pop->popup( this, pt.x, pt.y );
+ }
+
+ virtual int handleEvent( mxEvent *event )
+ {
+ int iret = 0;
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_TOOL_TOGGLEVISIBILITY:
+ {
+ IFacePoserToolWindow *tool = GetSelectedTool();
+ if ( tool )
+ {
+ mxWindow *toolw = tool->GetMxWindow();
+ if ( toolw )
+ {
+ toolw->setVisible( !toolw->isVisible() );
+ g_MDLViewer->UpdateWindowMenu();
+ }
+ }
+ }
+ break;
+ case IDC_TOOL_TOGGLELOCK:
+ {
+ IFacePoserToolWindow *tool = GetSelectedTool();
+ if ( tool )
+ {
+ tool->ToggleLockedState();
+ }
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if ( iret )
+ return iret;
+ return BaseClass::handleEvent( event );
+ }
+
+ void Init( void )
+ {
+ int c = IFacePoserToolWindow::GetToolCount();
+ int i;
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ add( tool->GetDisplayNameRoot() );
+ }
+ }
+
+#define WINDOW_DOUBLECLICK_TIME 0.4
+
+ void HandleWindowSelect( void )
+ {
+ extern double realtime;
+ IFacePoserToolWindow *tool = GetSelectedTool();
+ if ( !tool )
+ return;
+
+ bool doubleclicked = false;
+
+ double curtime = realtime;
+ int clickedItem = getSelectedIndex();
+
+ if ( clickedItem == m_nLastSelected )
+ {
+ if ( curtime < m_flLastSelectedTime + WINDOW_DOUBLECLICK_TIME )
+ {
+ doubleclicked = true;
+ }
+ }
+
+ m_flLastSelectedTime = curtime;
+ m_nLastSelected = clickedItem;
+
+ mxWindow *toolw = tool->GetMxWindow();
+ if ( !toolw )
+ return;
+
+ if ( doubleclicked )
+ {
+ toolw->setVisible( !toolw->isVisible() );
+ m_flLastSelectedTime = -1;
+ }
+
+ if ( !toolw->isVisible() )
+ {
+ return;
+ }
+
+ // Move window to front
+ HWND wnd = (HWND)tool->GetMxWindow()->getHandle();
+ SetFocus( wnd );
+ SetWindowPos( wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+ }
+
+private:
+
+ IFacePoserToolWindow *GetSelectedTool()
+ {
+ int idx = getSelectedIndex();
+ int c = IFacePoserToolWindow::GetToolCount();
+
+ if ( idx < 0 || idx >= c )
+ return NULL;
+
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( idx );
+ return tool;
+ }
+
+ // HACKY double click handler
+ int m_nLastSelected;
+ double m_flLastSelectedTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: The workspace is the parent of all of the tool windows
+//-----------------------------------------------------------------------------
+class CMDLViewerWorkspace : public mxWindow
+{
+public:
+ CMDLViewerWorkspace( mxWindow *parent, int x, int y, int w, int h, const char *label = 0, int style = 0)
+ : mxWindow( parent, x, y, w, h, label, style )
+ {
+ FacePoser_AddWindowStyle( this, WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool PaintBackground( void )
+ {
+ CChoreoWidgetDrawHelper drawHelper( this );
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+ drawHelper.DrawFilledRect( GetSysColor( COLOR_APPWORKSPACE ), rc );
+ return false;
+ }
+};
+
+void MDLViewer::LoadPosition( void )
+{
+ bool visible;
+ bool locked;
+ bool zoomed;
+ int x, y, w, h;
+
+ FacePoser_LoadWindowPositions( "MDLViewer", visible, x, y, w, h, locked, zoomed );
+
+ if ( w == 0 || h == 0 )
+ {
+ zoomed = true;
+ visible = true;
+ }
+
+ setBounds( x, y, w, h );
+ if ( zoomed )
+ {
+ ShowWindow( (HWND)getHandle(), SW_SHOWMAXIMIZED );
+ }
+ else
+ {
+ setVisible( visible );
+ }
+}
+
+void MDLViewer::SavePosition( void )
+{
+ bool visible;
+ int xpos, ypos, width, height;
+
+ visible = isVisible();
+ xpos = x();
+ ypos = y();
+ width = w();
+ height = h();
+
+ // xpos and ypos are screen space
+ POINT pt;
+ pt.x = xpos;
+ pt.y = ypos;
+
+ // Convert from screen space to relative to client area of parent window so
+ // the setBounds == MoveWindow call will offset to the same location
+ if ( getParent() )
+ {
+ ScreenToClient( (HWND)getParent()->getHandle(), &pt );
+ xpos = (short)pt.x;
+ ypos = (short)pt.y;
+ }
+
+ bool zoomed = IsZoomed( (HWND)getHandle() ) ? true : false;
+
+ bool iconic = IsIconic( (HWND)getHandle() ) ? true : false;
+
+ // Don't reset values if it's minimized during shutdown
+ if ( iconic )
+ return;
+
+ FacePoser_SaveWindowPositions( "MDLViewer", visible, xpos, ypos, width, height, false, zoomed );
+}
+
+MDLViewer::MDLViewer () :
+ mxWindow (0, 0, 0, 0, 0, g_appTitle, mxWindow::Normal),
+ menuCloseCaptionLanguages(0),
+ m_bOldSoundScriptsDirty( -1 ),
+ m_bVCDSaved( false )
+{
+ int i;
+
+ g_MDLViewer = this;
+
+ FacePoser_MakeToolWindow( this, false );
+
+ workspace = new CMDLViewerWorkspace( this, 0, 0, 500, 500, "" );
+ windowtab = new CMDLViewerWindowTab( this, 0, 500, 500, 20, IDC_WINDOW_TAB );
+ modeltab = new CMDLViewerModelTab( this, 500, 500, 200, 20, IDC_MODEL_TAB );
+ gridsettings = new CMDLViewerGridSettings( this, 0, 500, 500, 20 );
+ modeltab->SetRightJustify( true );
+
+ g_pStatusWindow = new mxStatusWindow( workspace, 0, 0, 1024, 150, "" );
+ g_pStatusWindow->setVisible( true );
+
+ InitViewerSettings( "faceposer" );
+ g_viewerSettings.speechapiindex = SPEECH_API_LIPSINC;
+ g_viewerSettings.m_iEditAttachment = -1;
+
+ LoadViewerRootSettings( );
+
+ LoadPosition();
+ // ShowWindow( (HWND)getHandle(), SW_SHOWMAXIMIZED );
+
+ g_pStatusWindow->setBounds( 0, h2() - 150, w2(), 150 );
+
+ Con_Printf( "MDLViewer started\n" );
+
+ Con_Printf( "Creating menu bar\n" );
+
+ // create menu stuff
+ mb = new mxMenuBar (this);
+ menuFile = new mxMenu ();
+ menuOptions = new mxMenu ();
+ menuWindow = new mxMenu ();
+ menuHelp = new mxMenu ();
+ menuEdit = new mxMenu ();
+ menuExpressions = new mxMenu();
+ menuChoreography = new mxMenu();
+
+ mb->addMenu ("File", menuFile);
+ //mb->addMenu( "Edit", menuEdit );
+ mb->addMenu ("Options", menuOptions);
+ mb->addMenu ( "Expression", menuExpressions );
+ mb->addMenu ( "Choreography", menuChoreography );
+ mb->addMenu ("Window", menuWindow);
+ mb->addMenu ("Help", menuHelp);
+
+ mxMenu *menuRecentFiles = new mxMenu ();
+ menuRecentFiles->add ("(empty)", IDC_FILE_RECENTFILES1);
+ menuRecentFiles->add ("(empty)", IDC_FILE_RECENTFILES2);
+ menuRecentFiles->add ("(empty)", IDC_FILE_RECENTFILES3);
+ menuRecentFiles->add ("(empty)", IDC_FILE_RECENTFILES4);
+
+ menuFile->add ("Load Model...", IDC_FILE_LOADMODEL);
+ menuFile->add( "Refresh\tF5", IDC_FILE_REFRESH );
+
+ menuFile->addSeparator();
+ menuFile->add ("Save Sound Changes...", IDC_FILE_SAVESOUNDSCRIPTCHANGES );
+ menuFile->add( "Rebuild scenes.image...", IDC_FILE_REBUILDSCENESIMAGE );
+
+ menuFile->addSeparator();
+
+ menuFile->add ("Load Background Texture...", IDC_FILE_LOADBACKGROUNDTEX);
+ menuFile->add ("Load Ground Texture...", IDC_FILE_LOADGROUNDTEX);
+ menuFile->addSeparator ();
+ menuFile->add ("Unload Ground Texture", IDC_FILE_UNLOADGROUNDTEX);
+ menuFile->addSeparator ();
+ menuFile->addMenu ("Recent Files", menuRecentFiles);
+ menuFile->addSeparator ();
+ menuFile->add ("Exit", IDC_FILE_EXIT);
+
+ menuFile->setEnabled(IDC_FILE_LOADBACKGROUNDTEX, false);
+ menuFile->setEnabled(IDC_FILE_LOADGROUNDTEX, false);
+ menuFile->setEnabled(IDC_FILE_UNLOADGROUNDTEX, false);
+ menuFile->setEnabled(IDC_FILE_SAVESOUNDSCRIPTCHANGES, false);
+
+ menuOptions->add ("Background Color...", IDC_OPTIONS_COLORBACKGROUND);
+ menuOptions->add ("Ground Color...", IDC_OPTIONS_COLORGROUND);
+ menuOptions->add ("Light Color...", IDC_OPTIONS_COLORLIGHT);
+
+ {
+ menuCloseCaptionLanguages = new mxMenu();
+
+ for ( int i = 0; i < CC_NUM_LANGUAGES; i++ )
+ {
+ int id = IDC_OPTIONS_LANGUAGESTART + i;
+ menuCloseCaptionLanguages->add( CSentence::NameForLanguage( i ), id );
+ }
+
+ menuOptions->addSeparator();
+ menuOptions->addMenu( "CC Language", menuCloseCaptionLanguages );
+ }
+
+ menuOptions->addSeparator ();
+ menuOptions->add ("Center View", IDC_OPTIONS_CENTERVIEW);
+ menuOptions->add ("Center on Face", IDC_OPTIONS_CENTERONFACE );
+#ifdef WIN32
+ menuOptions->addSeparator ();
+ menuOptions->add ("Make Screenshot...", IDC_OPTIONS_MAKESCREENSHOT);
+ //menuOptions->add ("Dump Model Info", IDC_OPTIONS_DUMP);
+ menuOptions->addSeparator ();
+ menuOptions->add ("Clear model sounds.", IDC_OPTIONS_CLEARMODELSOUNDS );
+
+#endif
+
+ menuExpressions->add( "New...", IDC_EXPRESSIONS_NEW );
+ menuExpressions->addSeparator ();
+ menuExpressions->add( "Load...", IDC_EXPRESSIONS_LOAD );
+ menuExpressions->add( "Save", IDC_EXPRESSIONS_SAVE );
+ menuExpressions->addSeparator ();
+ menuExpressions->add( "Export to VFE", IDC_EXPRESSIONS_EXPORT );
+ menuExpressions->addSeparator ();
+ menuExpressions->add( "Close class", IDC_EXPRESSIONS_CLOSE );
+ menuExpressions->add( "Close all classes", IDC_EXPRESSIONS_CLOSEALL );
+ menuExpressions->addSeparator();
+ menuExpressions->add( "Recreate all bitmaps", IDC_EXPRESSIONS_REDOBITMAPS );
+
+ menuChoreography->add( "New...", IDC_CHOREOSCENE_NEW );
+ menuChoreography->addSeparator();
+ menuChoreography->add( "Load...", IDC_CHOREOSCENE_LOAD );
+ menuChoreography->add( "Save", IDC_CHOREOSCENE_SAVE );
+ menuChoreography->add( "Save As...", IDC_CHOREOSCENE_SAVEAS );
+ menuChoreography->addSeparator();
+ menuChoreography->add( "Close", IDC_CHOREOSCENE_CLOSE );
+ menuChoreography->addSeparator();
+ menuChoreography->add( "Add Actor...", IDC_CHOREOSCENE_ADDACTOR );
+ menuChoreography->addSeparator();
+ menuChoreography->add( "Load Next", IDC_CHOREOSCENE_LOADNEXT );
+
+#ifdef WIN32
+ menuHelp->add ("Goto Homepage...", IDC_HELP_GOTOHOMEPAGE);
+ menuHelp->addSeparator ();
+#endif
+ menuHelp->add ("About...", IDC_HELP_ABOUT);
+
+ // create the Material System window
+ Con_Printf( "Creating 3D View\n" );
+ g_pMatSysWindow = new MatSysWindow (workspace, 0, 0, 100, 100, "", mxWindow::Normal);
+
+ Con_Printf( "Creating Close Caption tool" );
+ g_pCloseCaptionTool = new CloseCaptionTool( workspace );
+
+ Con_Printf( "Creating control panel\n" );
+ g_pControlPanel = new ControlPanel (workspace);
+
+ Con_Printf( "Creating phoneme editor\n" );
+ g_pPhonemeEditor = new PhonemeEditor( workspace );
+
+ Con_Printf( "Creating expression tool\n" );
+ g_pExpressionTool = new ExpressionTool( workspace );
+
+ Con_Printf( "Creating gesture tool\n" );
+ g_pGestureTool = new GestureTool( workspace );
+
+ Con_Printf( "Creating ramp tool\n" );
+ g_pRampTool = new RampTool( workspace );
+
+ Con_Printf( "Creating scene ramp tool\n" );
+ g_pSceneRampTool = new SceneRampTool( workspace );
+
+ Con_Printf( "Creating expression tray\n" );
+ g_pExpressionTrayTool = new mxExpressionTray( workspace, IDC_EXPRESSIONTRAY );
+
+ Con_Printf( "Creating animation browser\n" );
+ g_pAnimationBrowserTool = new AnimationBrowser( workspace, IDC_ANIMATIONBROWSER );
+
+ Con_Printf( "Creating flex slider window\n" );
+ g_pFlexPanel = new FlexPanel( workspace );
+
+ Con_Printf( "Creating wave browser\n" );
+ g_pWaveBrowser = new CWaveBrowser( workspace );
+
+ Con_Printf( "Creating VCD browser\n" );
+ g_pVCDBrowser = new CVCDBrowser( workspace );
+
+ Con_Printf( "Creating choreography view\n" );
+ g_pChoreoView = new CChoreoView( workspace, 200, 200, 400, 300, 0 );
+ // Choreo scene file drives main window title name
+ g_pChoreoView->SetUseForMainWindowTitle( true );
+#if 0
+ new TestWindow( workspace, 100, 100, 256, 256 );
+#endif
+
+ Con_Printf( "IFacePoserToolWindow::Init\n" );
+
+ IFacePoserToolWindow::InitTools();
+
+ Con_Printf( "windowtab->Init\n" );
+
+ windowtab->Init();
+
+ Con_Printf( "loadRecentFiles\n" );
+
+ loadRecentFiles ();
+ initRecentFiles ();
+
+ Con_Printf( "RestoreThumbnailSize\n" );
+
+ g_pExpressionTrayTool->RestoreThumbnailSize();
+ g_pAnimationBrowserTool->RestoreThumbnailSize();
+
+ Con_Printf( "Add Tool Windows\n" );
+
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ menuWindow->add( tool->GetToolName(), IDC_WINDOW_FIRSTTOOL + i );
+ }
+
+ menuWindow->addSeparator();
+ menuWindow->add( "Cascade", IDC_WINDOW_CASCADE );
+ menuWindow->addSeparator();
+ menuWindow->add( "Tile", IDC_WINDOW_TILE );
+ menuWindow->add( "Tile Horizontally", IDC_WINDOW_TILE_HORIZ );
+ menuWindow->add( "Tile Vertically", IDC_WINDOW_TILE_VERT );
+ menuWindow->addSeparator();
+ menuWindow->add( "Hide All", IDC_WINDOW_HIDEALL );
+ menuWindow->add( "Show All", IDC_WINDOW_SHOWALL );
+
+ Con_Printf( "UpdateWindowMenu\n" );
+
+ UpdateWindowMenu();
+ // Check the default item
+ UpdateLanguageMenu( g_viewerSettings.cclanguageid );
+
+ m_nCurrentFrame = 0;
+
+ Con_Printf( "gridsettings->Init()\n" );
+
+ gridsettings->Init();
+
+ Con_Printf( "LoadWindowPositions\n" );
+
+ LoadWindowPositions();
+
+ Con_Printf( "Model viewer created\n" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MDLViewer::UpdateWindowMenu( void )
+{
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( int i = 0; i < c ; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ menuWindow->setChecked( IDC_WINDOW_FIRSTTOOL + i, tool->GetMxWindow()->isVisible() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : currentLanguageId -
+//-----------------------------------------------------------------------------
+void MDLViewer::UpdateLanguageMenu( int currentLanguageId )
+{
+ if ( !menuCloseCaptionLanguages )
+ return;
+
+ for ( int i = 0; i < CC_NUM_LANGUAGES; i++ )
+ {
+ int id = IDC_OPTIONS_LANGUAGESTART + i;
+ menuCloseCaptionLanguages->setChecked( id, i == currentLanguageId ? true : false );
+ }
+}
+
+void MDLViewer::OnDelete()
+{
+ saveRecentFiles ();
+ SaveViewerRootSettings( );
+
+#ifdef WIN32
+ DeleteFile ("hlmv.cfg");
+ DeleteFile ("midump.txt");
+#endif
+
+ IFacePoserToolWindow::ShutdownTools();
+
+ g_MDLViewer = NULL;
+}
+
+MDLViewer::~MDLViewer ()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MDLViewer::InitModelTab( void )
+{
+ modeltab->Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void MDLViewer::InitGridSettings( void )
+{
+ gridsettings->Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int MDLViewer::GetActiveModelTab( void )
+{
+ return modeltab->getSelectedIndex();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : modelindex -
+//-----------------------------------------------------------------------------
+void MDLViewer::SetActiveModelTab( int modelindex )
+{
+ modeltab->select( modelindex );
+ modeltab->HandleModelSelect();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reloads the currently loaded model file.
+//-----------------------------------------------------------------------------
+void MDLViewer::Refresh( void )
+{
+ Con_ColorPrintf( RGB( 0, 125, 255 ), "Refreshing...\n" );
+
+ bool reinit_soundemitter = true;
+
+ // Save any changed sound script files
+ int c = soundemitter->GetNumSoundScripts();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !soundemitter->IsSoundScriptDirty( i ) )
+ continue;
+
+ char const *scriptname = soundemitter->GetSoundScriptName( i );
+ if ( !scriptname )
+ continue;
+
+ if ( !filesystem->FileExists( scriptname ) ||
+ !filesystem->IsFileWritable( scriptname ) )
+ {
+ continue;
+ }
+
+ int retval = mxMessageBox( NULL, va( "Save changes to sound script '%s'?", scriptname ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval != 0 )
+ {
+ reinit_soundemitter = false;
+ continue;
+ }
+
+ if ( retval == 0 )
+ {
+ soundemitter->SaveChangesToSoundScript( i );
+ Con_ColorPrintf( RGB( 50, 255, 100 ), " saving changes to script file '%s'\n", scriptname );
+ }
+ }
+
+ // kill the soundemitter system
+ if ( reinit_soundemitter )
+ {
+ soundemitter->Shutdown();
+ }
+
+
+ Con_ColorPrintf( RGB( 50, 255, 100 ), " reloading textures\n" );
+ g_pMaterialSystem->ReloadTextures();
+
+ models->ReleaseModels();
+
+ Con_ColorPrintf( RGB( 50, 255, 100 ), " reloading models\n" );
+ models->RestoreModels();
+
+ // restart the soundemitter system
+ if ( reinit_soundemitter )
+ {
+ Con_ColorPrintf( RGB( 50, 255, 100 ), " reloading sound emitter system\n" );
+ soundemitter->Init();
+ }
+ else
+ {
+ Con_ColorPrintf( RGB( 250, 50, 50 ), " NOT reloading sound emitter system\n" );
+ }
+
+ Con_ColorPrintf( RGB( 0, 125, 255 ), "done.\n" );
+}
+
+void MDLViewer::OnFileLoaded( char const *pszFile )
+{
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ if (!Q_stricmp( recentFiles[i], pszFile ))
+ break;
+ }
+
+ // swap existing recent file
+ if (i < 8)
+ {
+ char tmp[256];
+ strcpy (tmp, recentFiles[0]);
+ strcpy (recentFiles[0], recentFiles[i]);
+ strcpy (recentFiles[i], tmp);
+ }
+
+ // insert recent file
+ else
+ {
+ for (i = 7; i > 0; i--)
+ strcpy (recentFiles[i], recentFiles[i - 1]);
+
+ strcpy( recentFiles[0], pszFile );
+ }
+
+ initRecentFiles ();
+
+ if ( g_pVCDBrowser )
+ {
+ g_pVCDBrowser->SetCurrent( pszFile );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads the file and updates the MRU list.
+// Input : pszFile - File to load.
+//-----------------------------------------------------------------------------
+void MDLViewer::LoadModelFile( const char *pszFile )
+{
+ models->LoadModel( pszFile );
+
+ OnFileLoaded( pszFile );
+
+ g_pControlPanel->CenterOnFace();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *wnd -
+// x -
+// y -
+// Output : static bool
+//-----------------------------------------------------------------------------
+static bool WindowContainsPoint( mxWindow *wnd, int x, int y )
+{
+ POINT pt;
+ pt.x = (short)x;
+ pt.y = (short)y;
+
+ HWND window = (HWND)wnd->getHandle();
+ if ( !window )
+ return false;
+
+ ScreenToClient( window, &pt );
+
+ if ( pt.x < 0 )
+ return false;
+ if ( pt.y < 0 )
+ return false;
+ if ( pt.x > wnd->w() )
+ return false;
+ if ( pt.y > wnd->h() )
+ return false;
+
+ return true;
+}
+
+
+void MDLViewer::LoadModel_Steam()
+{
+ if ( !g_FSDialogFactory )
+ return;
+
+ IFileSystemOpenDialog *pDlg;
+ pDlg = (IFileSystemOpenDialog*)g_FSDialogFactory( FILESYSTEMOPENDIALOG_VERSION, NULL );
+ if ( !pDlg )
+ {
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "Can't create %s interface.", FILESYSTEMOPENDIALOG_VERSION );
+ ::MessageBox( NULL, str, "Error", MB_OK );
+ return;
+ }
+ pDlg->Init( g_Factory, NULL );
+ pDlg->AddFileMask( "*.jpg" );
+ pDlg->AddFileMask( "*.mdl" );
+ pDlg->SetInitialDir( "models", "game" );
+ pDlg->SetFilterMdlAndJpgFiles( true );
+
+ if (pDlg->DoModal() == IDOK)
+ {
+ char filename[MAX_PATH];
+ pDlg->GetFilename( filename, sizeof( filename ) );
+ LoadModelFile( filename );
+ }
+
+ pDlg->Release();
+}
+
+
+
+int MDLViewer::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ switch (event->event)
+ {
+ case mxEvent::Size:
+ {
+ int width = w2();
+ int height = h2();
+
+ windowtab->SetRowHeight( WINDOW_TAB_OFFSET - 2 );
+ modeltab->SetRowHeight( WINDOW_TAB_OFFSET - 2 );
+
+ int gridsettingswide = 100;
+ int gridstart = width - gridsettingswide - 5;
+
+ int modelwide = gridstart / 3;
+ int windowwide = gridstart - modelwide;
+
+ int rowheight = max( windowtab->GetBestHeight( windowwide ), modeltab->GetBestHeight( modelwide ) );
+
+ workspace->setBounds( 0, 0, width, height - rowheight );
+
+ gridsettings->setBounds( gridstart, height - rowheight + 1, gridsettingswide, WINDOW_TAB_OFFSET - 2 );
+
+ windowtab->setBounds( 0, height - rowheight, windowwide, rowheight );
+ modeltab->setBounds( windowwide, height - rowheight, modelwide, rowheight );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch (event->action)
+ {
+ case IDC_WINDOW_TAB:
+ {
+ windowtab->HandleWindowSelect();
+ }
+ break;
+ case IDC_MODEL_TAB:
+ {
+ modeltab->HandleModelSelect();
+ }
+ break;
+
+ case IDC_FILE_LOADMODEL:
+ {
+ if ( ! CommandLine()->FindParm( "-NoSteamDialog" ) )
+ {
+ g_MDLViewer->LoadModel_Steam();
+ }
+ else
+ {
+ char modelfile[ 512 ];
+ if ( FacePoser_ShowOpenFileNameDialog( modelfile, sizeof( modelfile ), "models", "*.mdl" ) )
+ {
+ LoadModelFile( modelfile );
+ }
+ }
+ }
+ break;
+
+ case IDC_FILE_REFRESH:
+ {
+ Refresh();
+ break;
+ }
+
+ case IDC_FILE_SAVESOUNDSCRIPTCHANGES:
+ {
+ OnSaveSoundScriptChanges();
+ }
+ break;
+ case IDC_FILE_REBUILDSCENESIMAGE:
+ {
+ OnRebuildScenesImage();
+ }
+ break;
+
+ case IDC_FILE_LOADBACKGROUNDTEX:
+ case IDC_FILE_LOADGROUNDTEX:
+ {
+ const char *ptr = mxGetOpenFileName (this, 0, "*.*");
+ if (ptr)
+ {
+ if (0 /* g_pMatSysWindow->loadTexture (ptr, event->action - IDC_FILE_LOADBACKGROUNDTEX) */)
+ {
+ if (event->action == IDC_FILE_LOADBACKGROUNDTEX)
+ g_pControlPanel->setShowBackground (true);
+ else
+ g_pControlPanel->setShowGround (true);
+
+ }
+ else
+ mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ }
+ }
+ break;
+
+ case IDC_FILE_UNLOADGROUNDTEX:
+ {
+ // g_pMatSysWindow->loadTexture (0, 1);
+ g_pControlPanel->setShowGround (false);
+ }
+ break;
+
+ case IDC_FILE_RECENTFILES1:
+ case IDC_FILE_RECENTFILES2:
+ case IDC_FILE_RECENTFILES3:
+ case IDC_FILE_RECENTFILES4:
+ case IDC_FILE_RECENTFILES5:
+ case IDC_FILE_RECENTFILES6:
+ case IDC_FILE_RECENTFILES7:
+ case IDC_FILE_RECENTFILES8:
+ {
+ int i = event->action - IDC_FILE_RECENTFILES1;
+
+ if ( recentFiles[ i ] && recentFiles[ i ][ 0 ] )
+ {
+ char ext[ 4 ];
+ Q_ExtractFileExtension( recentFiles[ i ], ext, sizeof( ext ) );
+ bool valid = false;
+ if ( !Q_stricmp( ext, "mdl" ) )
+ {
+ // Check extension
+ LoadModelFile( recentFiles[ i ] );
+ valid = true;
+ }
+ else if ( !Q_stricmp( ext, "vcd" ) )
+ {
+ g_pChoreoView->LoadSceneFromFile( recentFiles[ i ] );
+ valid = true;
+ }
+
+ if ( valid )
+ {
+ char tmp[256];
+ strcpy (tmp, recentFiles[0]);
+ strcpy (recentFiles[0], recentFiles[i]);
+ strcpy (recentFiles[i], tmp);
+
+ initRecentFiles ();
+ }
+ }
+
+ redraw ();
+ }
+ break;
+
+ case IDC_FILE_EXIT:
+ {
+ redraw ();
+ mx::quit ();
+ }
+ break;
+
+ case IDC_OPTIONS_COLORBACKGROUND:
+ case IDC_OPTIONS_COLORGROUND:
+ case IDC_OPTIONS_COLORLIGHT:
+ {
+ float *cols[3] = { g_viewerSettings.bgColor, g_viewerSettings.gColor, g_viewerSettings.lColor };
+ float *col = cols[event->action - IDC_OPTIONS_COLORBACKGROUND];
+ int r = (int) (col[0] * 255.0f);
+ int g = (int) (col[1] * 255.0f);
+ int b = (int) (col[2] * 255.0f);
+ if (mxChooseColor (this, &r, &g, &b))
+ {
+ col[0] = (float) r / 255.0f;
+ col[1] = (float) g / 255.0f;
+ col[2] = (float) b / 255.0f;
+ }
+ }
+ break;
+
+ case IDC_OPTIONS_CENTERVIEW:
+ g_pControlPanel->centerView ();
+ break;
+
+ case IDC_OPTIONS_CENTERONFACE:
+ g_pControlPanel->CenterOnFace();
+ break;
+
+ case IDC_OPTIONS_CLEARMODELSOUNDS:
+ {
+ sound->StopAll();
+ Con_ColorPrintf( RGB( 0, 100, 255 ), "Resetting model sound channels\n" );
+ }
+ break;
+
+ case IDC_OPTIONS_MAKESCREENSHOT:
+ {
+ char *ptr = (char *) mxGetSaveFileName (this, "", "*.tga");
+ if (ptr)
+ {
+ char fn[ 512 ];
+ Q_strncpy( fn, ptr, sizeof( fn ) );
+ Q_SetExtension( fn, ".tga", sizeof( fn ) );
+ g_pMatSysWindow->TakeScreenShot( fn );
+ }
+ }
+ break;
+
+ case IDC_OPTIONS_DUMP:
+ g_pControlPanel->dumpModelInfo ();
+ break;
+
+#ifdef WIN32
+ case IDC_HELP_GOTOHOMEPAGE:
+ ShellExecute (0, "open", "http://developer.valvesoftware.com/wiki/Category:Choreography", 0, 0, SW_SHOW);
+ break;
+#endif
+
+ case IDC_HELP_ABOUT:
+ mxMessageBox (this,
+ "v1.0 Copyright � 1996-2007, Valve Corporation. All rights reserved.\r\nBuild Date: " __DATE__ "",
+ "Valve Face Poser",
+ MX_MB_OK | MX_MB_INFORMATION);
+ break;
+
+ case IDC_EXPRESSIONS_REDOBITMAPS:
+ {
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ g_pProgressDialog->Start( "Rebuild Bitmaps", "", true );
+
+ g_pMatSysWindow->EnableStickySnapshotMode( );
+ for ( int i = 0; i < active->GetNumExpressions() ; i++ )
+ {
+ CExpression *exp = active->GetExpression( i );
+ if ( !exp )
+ continue;
+
+ g_pProgressDialog->UpdateText( exp->name );
+ g_pProgressDialog->Update( (float)i / (float)active->GetNumExpressions() );
+ if ( g_pProgressDialog->IsCancelled() )
+ {
+ Msg( "Cancelled\n" );
+ break;
+ }
+
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+
+ if ( ! ( i % 5 ) )
+ {
+ g_pExpressionTrayTool->redraw();
+ }
+ }
+ g_pMatSysWindow->DisableStickySnapshotMode( );
+
+ g_pProgressDialog->Finish();
+
+ active->SelectExpression( 0 );
+ }
+ }
+ break;
+ case IDC_EXPRESSIONS_NEW:
+ {
+ char classfile[ 512 ];
+ if ( FacePoser_ShowSaveFileNameDialog( classfile, sizeof( classfile ), "expressions", "*.txt" ) )
+ {
+ Q_DefaultExtension( classfile, ".txt", sizeof( classfile ) );
+ expressions->CreateNewClass( classfile );
+ }
+ }
+ break;
+ case IDC_EXPRESSIONS_LOAD:
+ {
+ char classfile[ 512 ];
+ if ( FacePoser_ShowOpenFileNameDialog( classfile, sizeof( classfile ), "expressions", "*.txt" ) )
+ {
+ expressions->LoadClass( classfile );
+ }
+ }
+ break;
+
+ case IDC_EXPRESSIONS_SAVE:
+ {
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ active->Save();
+ active->Export();
+ }
+ }
+ break;
+ case IDC_EXPRESSIONS_EXPORT:
+ {
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ active->Export();
+ }
+ }
+ break;
+ case IDC_EXPRESSIONS_CLOSE:
+ g_pControlPanel->Close();
+ break;
+ case IDC_EXPRESSIONS_CLOSEALL:
+ g_pControlPanel->Closeall();
+ break;
+ case IDC_CHOREOSCENE_NEW:
+ g_pChoreoView->New();
+ break;
+ case IDC_CHOREOSCENE_LOAD:
+ g_pChoreoView->Load();
+ break;
+ case IDC_CHOREOSCENE_LOADNEXT:
+ g_pChoreoView->LoadNext();
+ break;
+ case IDC_CHOREOSCENE_SAVE:
+ g_pChoreoView->Save();
+ break;
+ case IDC_CHOREOSCENE_SAVEAS:
+ g_pChoreoView->SaveAs();
+ break;
+ case IDC_CHOREOSCENE_CLOSE:
+ g_pChoreoView->Close();
+ break;
+ case IDC_CHOREOSCENE_ADDACTOR:
+ g_pChoreoView->NewActor();
+ break;
+ case IDC_WINDOW_TILE:
+ {
+ OnTile();
+ }
+ break;
+ case IDC_WINDOW_TILE_HORIZ:
+ {
+ OnTileHorizontally();
+ }
+ break;
+ case IDC_WINDOW_TILE_VERT:
+ {
+ OnTileVertically();
+ }
+ break;
+ case IDC_WINDOW_CASCADE:
+ {
+ OnCascade();
+ }
+ break;
+ case IDC_WINDOW_HIDEALL:
+ {
+ OnHideAll();
+ }
+ break;
+ case IDC_WINDOW_SHOWALL:
+ {
+ OnShowAll();
+ }
+ break;
+ default:
+ {
+ iret = 0;
+ int tool_number = event->action - IDC_WINDOW_FIRSTTOOL;
+ int max_tools = IDC_WINDOW_LASTTOOL - IDC_WINDOW_FIRSTTOOL;
+
+ if ( tool_number >= 0 &&
+ tool_number <= max_tools &&
+ tool_number < IFacePoserToolWindow::GetToolCount() )
+ {
+ iret = 1;
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( tool_number );
+ if ( tool )
+ {
+ mxWindow *toolw = tool->GetMxWindow();
+
+ bool wasvisible = toolw->isVisible();
+ toolw->setVisible( !wasvisible );
+
+ g_MDLViewer->UpdateWindowMenu();
+
+ }
+ }
+
+ int lang_number = event->action - IDC_OPTIONS_LANGUAGESTART;
+ if ( lang_number >= 0 &&
+ lang_number < CC_NUM_LANGUAGES )
+ {
+ iret = 1;
+ SetCloseCaptionLanguageId( lang_number );
+ }
+ }
+ break;
+ } //switch (event->action)
+ } // mxEvent::Action
+ break;
+ case KeyDown:
+ {
+ //g_pMatSysWindow->handleEvent(event);
+ // Send it to the active tool
+ IFacePoserToolWindow *active = IFacePoserToolWindow::GetActiveTool();
+ if ( active )
+ {
+ mxWindow *w = active->GetMxWindow();
+ if ( w )
+ {
+ w->handleEvent( event );
+ }
+ }
+ else
+ {
+ g_pMatSysWindow->handleEvent(event);
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::Activate:
+ {
+ if (event->action)
+ {
+ mx::setIdleWindow( g_pMatSysWindow );
+ // Force reload of localization data
+ SetCloseCaptionLanguageId( GetCloseCaptionLanguageId(), true );
+ }
+ else
+ {
+ mx::setIdleWindow( 0 );
+ }
+ iret = 1;
+ }
+ break;
+ } // event->event
+
+ return iret;
+}
+
+void MDLViewer::SaveWindowPositions( void )
+{
+ // Save the model viewer position
+ SavePosition();
+
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *w = IFacePoserToolWindow::GetTool( i );
+ w->SavePosition();
+ }
+}
+
+void MDLViewer::LoadWindowPositions( void )
+{
+ // NOTE: Don't do this here, we do the mdlviewer position earlier in startup
+ // LoadPosition();
+
+ int w = this->w();
+ int h = this->h();
+
+ g_viewerSettings.width = w;
+ g_viewerSettings.height = h;
+
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *w = IFacePoserToolWindow::GetTool( i );
+ w->LoadPosition();
+ }
+}
+
+void
+MDLViewer::redraw ()
+{
+}
+
+int MDLViewer::GetCurrentFrame( void )
+{
+ return m_nCurrentFrame;
+}
+
+void MDLViewer::Think( float dt )
+{
+ ++m_nCurrentFrame;
+
+ // Iterate across tools
+ IFacePoserToolWindow::ToolThink( dt );
+
+ sound->Update( dt );
+
+ bool soundscriptsdirty = AreSoundScriptsDirty();
+ if ( soundscriptsdirty != m_bOldSoundScriptsDirty )
+ {
+ // Update the menu item when this changes
+ menuFile->setEnabled(IDC_FILE_SAVESOUNDSCRIPTCHANGES, soundscriptsdirty );
+ }
+
+ m_bOldSoundScriptsDirty = soundscriptsdirty;
+}
+
+static int CountVisibleTools( void )
+{
+ int i;
+ int c = IFacePoserToolWindow::GetToolCount();
+ int viscount = 0;
+
+ for ( i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ mxWindow *w = tool->GetMxWindow();
+ if ( !w->isVisible() )
+ continue;
+
+ viscount++;
+ }
+
+ return viscount;
+}
+
+void MDLViewer::OnCascade()
+{
+ int i;
+ int c = IFacePoserToolWindow::GetToolCount();
+ int viscount = CountVisibleTools();
+
+ int x = 0, y = 0;
+
+ int offset = 20;
+
+ int wide = workspace->w2() - viscount * offset;
+ int tall = ( workspace->h2() - viscount * offset ) / 2;
+
+ for ( i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ mxWindow *w = tool->GetMxWindow();
+ if ( !w->isVisible() )
+ continue;
+
+ w->setBounds( x, y, wide, tall );
+ x += offset;
+ y += offset;
+ }
+}
+
+void MDLViewer::OnTile()
+{
+ int c = CountVisibleTools();
+
+ int rows = (int)sqrt( ( float )c );
+ rows = clamp( rows, 1, rows );
+
+ int cols = 1;
+ while ( rows * cols < c )
+ {
+ cols++;
+ }
+
+ DoTile( rows, cols );
+}
+
+void MDLViewer::OnTileHorizontally()
+{
+ int c = CountVisibleTools();
+
+ DoTile( c, 1 );
+}
+
+void MDLViewer::OnTileVertically()
+{
+ int c = CountVisibleTools();
+
+ DoTile( 1, c );
+}
+
+void MDLViewer::OnHideAll()
+{
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ mxWindow *w = tool->GetMxWindow();
+
+ w->setVisible( false );
+ }
+
+ UpdateWindowMenu();
+}
+
+void MDLViewer::OnShowAll()
+{
+ int c = IFacePoserToolWindow::GetToolCount();
+ for ( int i = 0; i < c; i++ )
+ {
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( i );
+ mxWindow *w = tool->GetMxWindow();
+
+ w->setVisible( true );
+ }
+
+ UpdateWindowMenu();
+}
+
+void MDLViewer::DoTile( int x, int y )
+{
+ int c = IFacePoserToolWindow::GetToolCount();
+
+ if ( x < 1 )
+ x = 1;
+ if ( y < 1 )
+ y = 1;
+
+ int wide = workspace->w2() / y;
+ int tall = workspace->h2() / x;
+
+ int obj = 0;
+
+ for ( int row = 0 ; row < x ; row++ )
+ {
+ for ( int col = 0; col < y; col++ )
+ {
+ bool found = false;
+ while ( 1 )
+ {
+ if ( obj >= c )
+ break;
+
+ IFacePoserToolWindow *tool = IFacePoserToolWindow::GetTool( obj++ );
+ mxWindow *w = tool->GetMxWindow();
+ if ( w->isVisible() )
+ {
+ w->setBounds( col * wide, row * tall, wide, tall );
+
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found )
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Not used by faceposer
+// Output : int
+//-----------------------------------------------------------------------------
+int MDLViewer::GetCurrentHitboxSet(void)
+{
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool MDLViewer::PaintBackground( void )
+{
+ CChoreoWidgetDrawHelper drawHelper( this );
+
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ drawHelper.DrawFilledRect( COLOR_CHOREO_BACKGROUND, rc );
+ return false;
+}
+
+void MDLViewer::OnRebuildScenesImage()
+{
+ g_pProgressDialog->Start( "Rebuilding scenes.image", "", false );
+
+ CUtlBuffer targetBuffer;
+
+ bool bLittleEndian = true;
+
+ const char *pFilename = bLittleEndian ? "scenes/scenes.image" : "scenes/scenes.360.image";
+
+ CP4AutoEditAddFile checkout( CFmtStr( "%s%s", gamedir, pFilename ) );
+
+ bool bSuccess = g_pSceneImage->CreateSceneImageFile( targetBuffer, gamedir, bLittleEndian, false, this );
+ if ( bSuccess )
+ {
+ scriptlib->WriteBufferToFile( pFilename, targetBuffer, WRITE_TO_DISK_ALWAYS );
+ }
+
+ g_pProgressDialog->Finish();
+ m_bVCDSaved = false;
+}
+
+void MDLViewer::UpdateStatus( char const *pchSceneName, bool bQuiet, int nIndex, int nCount )
+{
+ g_pProgressDialog->UpdateText( pchSceneName );
+ g_pProgressDialog->Update( (float)nIndex / (float)nCount );
+}
+
+void MDLViewer::OnVCDSaved()
+{
+ m_bVCDSaved = true;
+}
+
+SpewRetval_t HLFacePoserSpewFunc( SpewType_t spewType, char const *pMsg )
+{
+ g_bInError = true;
+
+ switch (spewType)
+ {
+ case SPEW_ERROR:
+ ::MessageBox(NULL, pMsg, "FATAL ERROR", MB_OK);
+ g_bInError = false;
+ return SPEW_ABORT;
+
+ case SPEW_LOG:
+ g_bInError = false;
+ return SPEW_CONTINUE;
+
+ case SPEW_WARNING:
+ Con_ErrorPrintf( pMsg );
+ g_bInError = false;
+ return SPEW_CONTINUE;
+
+ default:
+ Con_Printf(pMsg);
+ g_bInError = false;
+#ifdef _DEBUG
+ return spewType == SPEW_ASSERT ? SPEW_DEBUGGER : SPEW_CONTINUE;
+#else
+ return SPEW_CONTINUE;
+#endif
+ }
+}
+
+void MDLViewer::OnSaveSoundScriptChanges()
+{
+ if ( !AreSoundScriptsDirty() )
+ {
+ return;
+ }
+
+// Save any changed sound script files
+ int c = soundemitter->GetNumSoundScripts();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( !soundemitter->IsSoundScriptDirty( i ) )
+ continue;
+
+ char const *scriptname = soundemitter->GetSoundScriptName( i );
+ if ( !scriptname )
+ continue;
+
+ if ( !filesystem->FileExists( scriptname ) )
+ {
+ continue;
+ }
+
+ if ( !filesystem->IsFileWritable( scriptname ) )
+ {
+ mxMessageBox( NULL, va( "Can't save changes to sound script '%s', file is READ-ONLY?", scriptname ), g_appTitle, MX_MB_OK );
+ continue;
+ }
+
+ int retval = mxMessageBox( NULL, va( "Save changes to sound script '%s'?", scriptname ), g_appTitle, MX_MB_YESNOCANCEL );
+ if ( retval == 2 )
+ {
+ return;
+ }
+
+ if ( retval == 0 )
+ {
+ soundemitter->SaveChangesToSoundScript( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CHLFacePoserApp : public CTier3SteamApp
+{
+ typedef CTier3SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+
+private:
+ // Sets up the search paths
+ bool SetupSearchPaths();
+};
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CHLFacePoserApp::Create()
+{
+ // Save some memory so engine/hammer isn't so painful
+ CommandLine()->AppendParm( "-disallowhwmorph", NULL );
+
+ SpewOutputFunc( HLFacePoserSpewFunc );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+ { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
+ { "vphysics.dll", VPHYSICS_INTERFACE_VERSION },
+ { "datacache.dll", DATACACHE_INTERFACE_VERSION },
+ { "datacache.dll", MDLCACHE_INTERFACE_VERSION },
+ { "datacache.dll", STUDIO_DATA_CACHE_INTERFACE_VERSION },
+ { "vguimatsurface.dll", VGUI_SURFACE_INTERFACE_VERSION },
+ { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
+ { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ // Add the P4 module separately so that if it is absent (say in the SDK) then the other system will initialize properly
+ AppModule_t p4Module = LoadModule( "p4lib.dll" );
+ if ( p4Module != APP_MODULE_INVALID )
+ {
+ AddSystem( p4Module, P4_INTERFACE_VERSION );
+ }
+
+ g_Factory = GetFactory();
+
+ IMaterialSystem* pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+ if ( !pMaterialSystem )
+ {
+ Warning( "Material System interface could not be found!\n" );
+ return false;
+ }
+
+ const char *pShaderDLL = CommandLine()->ParmValue("-shaderdll");
+ if(!pShaderDLL)
+ {
+ pShaderDLL = "shaderapidx9.dll";
+ }
+ pMaterialSystem->SetShaderAPI( pShaderDLL );
+
+ return true;
+}
+
+
+void CHLFacePoserApp::Destroy()
+{
+}
+
+
+const char *GetGameDirectory()
+{
+ // TODO: get rid of this and ONLY use the filesystem, so hlfaceposer works nicely for
+ // mods that get the base game resources from the Steam filesystem.
+ return gamedir;
+}
+
+char const *GetGameDirectorySimple()
+{
+ return gamedirsimple;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CHLFacePoserApp::SetupSearchPaths()
+{
+ // Add paths...
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ // Set gamedir.
+ Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), GetGameInfoPath() );
+
+ Q_FileBase( gamedir, gamedirsimple, sizeof( gamedirsimple ) );
+
+ Q_AppendSlash( gamedir, sizeof( gamedir ) );
+
+ workspacefiles->Init( GetGameDirectorySimple() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+bool CHLFacePoserApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ g_pFileSystem = filesystem = g_pFullFileSystem;
+ g_pStudioDataCache = (IStudioDataCache*)FindSystem( STUDIO_DATA_CACHE_INTERFACE_VERSION );
+ physcollision = (IPhysicsCollision *)FindSystem( VPHYSICS_COLLISION_INTERFACE_VERSION );
+ physprop = (IPhysicsSurfaceProps *)FindSystem( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION );
+ g_pLocalize = (vgui::ILocalize *)FindSystem(VGUI_LOCALIZE_INTERFACE_VERSION );
+ soundemitter = (ISoundEmitterSystemBase*)FindSystem(SOUNDEMITTERSYSTEM_INTERFACE_VERSION);
+
+ if ( !soundemitter || !g_pLocalize || !filesystem || !physprop || !physcollision ||
+ !g_pMaterialSystem || !g_pStudioRender || !g_pMDLCache || !g_pDataCache )
+ {
+ Error("Unable to load required library interface!\n");
+ }
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ filesystem->SetWarningFunc( Warning );
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ // Get the adapter from the command line....
+ const char *pAdapterString;
+ int nAdapter = 0;
+ if (CommandLine()->CheckParm( "-adapter", &pAdapterString ))
+ {
+ nAdapter = atoi( pAdapterString );
+ }
+
+ int adapterFlags = MATERIAL_INIT_ALLOCATE_FULLSCREEN_TEXTURE;
+ if ( CommandLine()->CheckParm( "-ref" ) )
+ {
+ adapterFlags |= MATERIAL_INIT_REFERENCE_RASTERIZER;
+ }
+
+ g_pMaterialSystem->SetAdapter( nAdapter, adapterFlags );
+
+ LoadFileSystemDialogModule();
+
+ return true;
+}
+
+void CHLFacePoserApp::PostShutdown()
+{
+ UnloadFileSystemDialogModule();
+
+ g_pFileSystem = filesystem = NULL;
+ g_pStudioDataCache = NULL;
+ physcollision = NULL;
+ physprop = NULL;
+
+ BaseClass::PostShutdown();
+
+ g_Factory = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CHLFacePoserApp::Main()
+{
+ // Do Perforce Stuff
+ g_p4factory->SetDummyMode( false );
+ if ( CommandLine()->FindParm( "-nop4" ) || !p4 )
+ {
+ g_p4factory->SetDummyMode( true );
+ }
+
+ g_p4factory->SetOpenFileChangeList( "FacePoser Auto Checkout" );
+
+ soundemitter->ModInit();
+ g_pMaterialSystem->ModInit();
+
+ g_pDataCache->SetSize( 64 * 1024 * 1024 );
+
+ // Always start with english
+ g_pLocalize->AddFile( "resource/closecaption_english.txt", "GAME", true );
+
+ sound->Init();
+
+ IFacePoserToolWindow::EnableToolRedraw( false );
+
+ g_MDLViewer = new MDLViewer ();
+ g_MDLViewer->setMenuBar (g_MDLViewer->getMenuBar ());
+
+ FaceposerVGui()->Init( (HWND)g_MDLViewer->getHandle() );
+
+ // Force reload of close captioning data file!!!
+ SetCloseCaptionLanguageId( g_viewerSettings.cclanguageid, true );
+
+ g_pStudioModel->Init();
+
+ int i;
+ bool modelloaded = false;
+ for ( i = 1; i < CommandLine()->ParmCount(); i++ )
+ {
+ if ( Q_stristr (CommandLine()->GetParm( i ), ".mdl") )
+ {
+ modelloaded = true;
+ g_MDLViewer->LoadModelFile( CommandLine()->GetParm( i ) );
+ break;
+ }
+ }
+
+ models->LoadModelList();
+ g_pPhonemeEditor->ValidateSpeechAPIIndex();
+
+ if ( models->Count() == 0 )
+ {
+ g_pFlexPanel->initFlexes( );
+ }
+
+ // Load expressions from last time
+ int files = workspacefiles->GetNumStoredFiles( IWorkspaceFiles::EXPRESSION );
+ for ( i = 0; i < files; i++ )
+ {
+ expressions->LoadClass( workspacefiles->GetStoredFile( IWorkspaceFiles::EXPRESSION, i ) );
+ }
+
+ IFacePoserToolWindow::EnableToolRedraw( true );
+
+ int nRetVal = mx::run ();
+
+ if (g_pStudioModel)
+ {
+ g_pStudioModel->Shutdown();
+ g_pStudioModel = NULL;
+ }
+
+ g_pMaterialSystem->ModShutdown();
+ soundemitter->ModShutdown();
+ g_pMaterialSystem->ModShutdown();
+
+ FaceposerVGui()->Shutdown();
+
+ return nRetVal;
+}
+
+static bool CHLFacePoserApp_SuggestGameInfoDirFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
+{
+ if ( pbBubbleDirectories )
+ *pbBubbleDirectories = true;
+
+ for ( int i = 1; i < CommandLine()->ParmCount(); i++ )
+ {
+ if ( Q_stristr( CommandLine()->GetParm( i ), ".mdl" ) )
+ {
+ Q_MakeAbsolutePath( pchPathBuffer, nBufferLength, CommandLine()->GetParm( i ) );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int main (int argc, char *argv[])
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ CoInitialize(NULL);
+
+ // make sure, we start in the right directory
+ char szName[256];
+ strcpy (szName, mx::getApplicationPath() );
+ mx::init (argc, argv);
+
+ char workingdir[ 256 ];
+ workingdir[0] = 0;
+ Q_getwd( workingdir, sizeof( workingdir ) );
+
+ // Set game info directory suggestion callback
+ SetSuggestGameInfoDirFn( CHLFacePoserApp_SuggestGameInfoDirFn );
+
+ CHLFacePoserApp hlFacePoserApp;
+ CSteamApplication steamApplication( &hlFacePoserApp );
+ int nRetVal = steamApplication.Run();
+
+ CoUninitialize();
+
+ return nRetVal;
+}
+
diff --git a/utils/hlfaceposer/mdlviewer.h b/utils/hlfaceposer/mdlviewer.h
new file mode 100644
index 0000000..69ba681
--- /dev/null
+++ b/utils/hlfaceposer/mdlviewer.h
@@ -0,0 +1,196 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MDLVIEWER_H
+#define MDLVIEWER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "sceneimage.h"
+
+#define IDC_FILE_LOADMODEL 1001
+#define IDC_FILE_LOADBACKGROUNDTEX 1002
+#define IDC_FILE_LOADGROUNDTEX 1003
+#define IDC_FILE_UNLOADGROUNDTEX 1004
+#define IDC_FILE_RECENTFILES1 1008
+#define IDC_FILE_RECENTFILES2 1009
+#define IDC_FILE_RECENTFILES3 1010
+#define IDC_FILE_RECENTFILES4 1011
+#define IDC_FILE_RECENTFILES5 1012
+#define IDC_FILE_RECENTFILES6 1013
+#define IDC_FILE_RECENTFILES7 1014
+#define IDC_FILE_RECENTFILES8 1015
+#define IDC_FILE_EXIT 1016
+#define IDC_FILE_REFRESH 1017
+#define IDC_FILE_SAVESOUNDSCRIPTCHANGES 1018
+#define IDC_FILE_REBUILDSCENESIMAGE 1019
+
+#define IDC_EXPRESSIONS_SAVE 1020
+#define IDC_EXPRESSIONS_LOAD 1021
+#define IDC_EXPRESSIONS_SAVEAS 1022
+
+#define IDC_EXPRESSIONS_EXPORT 1023
+
+#define IDC_EXPRESSIONS_CLOSE 1024
+#define IDC_EXPRESSIONS_CLOSEALL 1025
+
+#define IDC_EXPRESSIONS_NEW 1026
+#define IDC_EXPRESSIONS_REDOBITMAPS 1027
+
+
+#define IDC_CHOREOSCENE_NEW 1030
+#define IDC_CHOREOSCENE_LOAD 1031
+#define IDC_CHOREOSCENE_SAVE 1032
+#define IDC_CHOREOSCENE_SAVEAS 1033
+#define IDC_CHOREOSCENE_CLOSE 1034
+#define IDC_CHOREOSCENE_ADDACTOR 1035
+#define IDC_FILE_LOADMODEL_STEAM 1036
+#define IDC_CHOREOSCENE_LOADNEXT 1038
+
+#define IDC_OPTIONS_COLORBACKGROUND 1101
+#define IDC_OPTIONS_COLORGROUND 1102
+#define IDC_OPTIONS_COLORLIGHT 1103
+#define IDC_OPTIONS_CENTERVIEW 1104
+#define IDC_OPTIONS_MAKESCREENSHOT 1105
+#define IDC_OPTIONS_DUMP 1106
+#define IDC_OPTIONS_CENTERONFACE 1107
+#define IDC_OPTIONS_CLEARMODELSOUNDS 1108
+
+#define IDC_OPTIONS_LANGUAGESTART 1150
+
+#define IDC_WINDOW_FIRSTTOOL 1200
+#define IDC_WINDOW_LASTTOOL 1231
+#define IDC_WINDOW_TILE_HORIZ 1232
+#define IDC_WINDOW_TILE_VERT 1233
+#define IDC_WINDOW_CASCADE 1234
+#define IDC_WINDOW_HIDEALL 1235
+#define IDC_WINDOW_SHOWALL 1236
+#define IDC_WINDOW_TILE 1237
+
+#define IDC_WINDOW_TAB 1238
+#define IDC_MODEL_TAB 1239
+#define IDC_GRIDSETTINGS 1240
+
+#define IDC_HELP_GOTOHOMEPAGE 1301
+#define IDC_HELP_ABOUT 1302
+
+class mxMenuBar;
+class mxMenu;
+class MatSysWindow;
+class ControlPanel;
+class FlexPanel;
+class mxStatusWindow;
+class CChoreoView;
+class CMDLViewerWorkspace;
+class CMDLViewerWindowTab;
+class CMDLViewerModelTab;
+class CMDLViewerGridSettings;
+
+enum { Action, Size, Timer, Idle, Show, Hide,
+ MouseUp, MouseDown, MouseMove, MouseDrag,
+ KeyUp, KeyDown
+ };
+
+class MDLViewer : public mxWindow, public ISceneCompileStatus
+{
+ mxMenuBar *mb;
+ mxMenu *menuFile;
+ mxMenu *menuOptions;
+ mxMenu *menuCloseCaptionLanguages;
+ mxMenu *menuWindow;
+ mxMenu *menuHelp;
+ mxMenu *menuEdit;
+ mxMenu *menuExpressions;
+ mxMenu *menuChoreography;
+
+ CMDLViewerWorkspace *workspace;
+ CMDLViewerWindowTab *windowtab;
+ CMDLViewerModelTab *modeltab;
+ CMDLViewerGridSettings *gridsettings;
+
+ void loadRecentFiles ();
+ void saveRecentFiles ();
+ void initRecentFiles ();
+
+ int m_nCurrentFrame;
+
+public:
+ // CREATORS
+ MDLViewer ();
+ ~MDLViewer ();
+
+ virtual void OnDelete();
+ virtual bool CanClose();
+
+ virtual void UpdateStatus( char const *pchSceneName, bool bQuiet, int nIndex, int nCount );
+
+ void OnFileLoaded( char const *pszFile );
+
+ // MANIPULATORS
+ virtual int handleEvent (mxEvent *event);
+ void redraw ();
+ virtual bool PaintBackground( void );
+
+ void UpdateWindowMenu( void );
+ void UpdateLanguageMenu( int currentLanguageId );
+
+ void InitModelTab( void );
+ void InitGridSettings( void );
+
+ int GetActiveModelTab( void );
+ void SetActiveModelTab( int modelindex );
+
+ void Refresh( void );
+ void LoadModelFile( const char *pszFile );
+ int GetCurrentHitboxSet(void);
+
+ virtual bool Closing( void );
+
+ void LoadWindowPositions( void );
+ void SaveWindowPositions( void );
+
+ void OnSaveSoundScriptChanges();
+ void OnRebuildScenesImage();
+
+ void OnCascade();
+ void OnTile();
+ void OnTileHorizontally();
+ void OnTileVertically();
+
+ void OnHideAll();
+ void OnShowAll();
+
+ void Think( float dt );
+
+ int GetCurrentFrame( void );
+
+ // ACCESSORS
+ mxMenuBar *getMenuBar () const { return mb; }
+
+ void LoadModel_Steam();
+
+ void OnVCDSaved();
+
+private:
+ void DoTile( int x, int y );
+
+ void LoadPosition( void );
+ void SavePosition( void );
+
+ bool AreSoundScriptsDirty();
+
+ bool m_bOldSoundScriptsDirty;
+ bool m_bVCDSaved;
+};
+
+
+const char *GetGameDirectory( );
+void CreatePath( const char *pPath );
+extern MDLViewer *g_MDLViewer;
+extern char g_appTitle[];
+
+#endif // MDLVIEWER_H
diff --git a/utils/hlfaceposer/mxbitmapbutton.cpp b/utils/hlfaceposer/mxbitmapbutton.cpp
new file mode 100644
index 0000000..4f088b7
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmapbutton.cpp
@@ -0,0 +1,96 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "mxBitmapButton.h"
+#include "hlfaceposer.h"
+
+
+mxBitmapButton::mxBitmapButton( mxWindow *parent, int x, int y, int w, int h, int id /*= 0*/, const char *bitmap /* = 0 */ )
+: mxWindow( parent, x, y, w, h, "" )
+{
+ setId( id );
+
+ m_bmImage.valid = false;
+
+ SetImage( bitmap );
+
+ HWND wnd = (HWND)getHandle();
+
+ DWORD style = GetWindowLong( wnd, GWL_STYLE );
+ style |= WS_CLIPSIBLINGS;
+ SetWindowLong( wnd, GWL_STYLE, style );
+}
+
+mxBitmapButton::~mxBitmapButton( void )
+{
+ DeleteImage();
+}
+
+void mxBitmapButton::redraw()
+{
+ HWND wnd = (HWND)getHandle();
+ if ( !wnd )
+ return;
+
+ if ( !m_bmImage.valid )
+ return;
+
+ RECT rc;
+ GetClientRect( wnd, &rc );
+
+ HDC dc = GetDC( wnd );
+
+ DrawBitmapToDC( dc, 0, 0, w(), h(), m_bmImage );
+
+ ReleaseDC( wnd, dc );
+
+ ValidateRect( wnd, &rc );
+}
+
+int mxBitmapButton::handleEvent( mxEvent * event )
+{
+ int iret = 0;
+
+ switch (event->event)
+ {
+ case mxEvent::MouseUp:
+ // Send message to parent
+ HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
+ if ( parent )
+ {
+ LPARAM lp;
+ WPARAM wp;
+
+ wp = MAKEWPARAM( getId(), BN_CLICKED );
+ lp = (long)getHandle();
+
+ SendMessage( parent, WM_COMMAND, wp, lp );
+ iret = 1;
+ }
+ break;
+ }
+
+ return iret;
+}
+
+void mxBitmapButton::SetImage( const char *bitmapname )
+{
+ if ( m_bmImage.valid )
+ {
+ DeleteImage();
+ }
+
+ LoadBitmapFromFile( bitmapname, m_bmImage );
+}
+
+void mxBitmapButton::DeleteImage( void )
+{
+ if ( m_bmImage.valid )
+ {
+ DeleteObject( m_bmImage.image );
+ m_bmImage.valid = false;
+ }
+}
diff --git a/utils/hlfaceposer/mxbitmapbutton.h b/utils/hlfaceposer/mxbitmapbutton.h
new file mode 100644
index 0000000..60e1d9a
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmapbutton.h
@@ -0,0 +1,34 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MXBITMAPBUTTON_H
+#define MXBITMAPBUTTON_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include <mxtk/mx.h>
+#include "mxBitmapTools.h"
+
+class mxBitmapButton : public mxWindow
+{
+public:
+ mxBitmapButton( mxWindow *parent, int x, int y, int w, int h, int id = 0, const char *bitmap = 0 );
+ ~mxBitmapButton( void );
+
+ virtual void redraw();
+ virtual int handleEvent( mxEvent * event );
+
+ void SetImage( const char *bitmapname );
+
+private:
+ void DeleteImage( void );
+
+ mxbitmapdata_t m_bmImage;
+};
+#endif // MXBITMAPBUTTON_H
diff --git a/utils/hlfaceposer/mxbitmaptools.cpp b/utils/hlfaceposer/mxbitmaptools.cpp
new file mode 100644
index 0000000..87b23c9
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmaptools.cpp
@@ -0,0 +1,154 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "mxbitmaptools.h"
+#include <mxtk/mx.h>
+#include "hlfaceposer.h"
+#include "filesystem.h"
+
+bool LoadBitmapFromFile( const char *relative, mxbitmapdata_t& bitmap )
+{
+
+ bitmap.valid = false;
+ bitmap.image = NULL;
+ bitmap.width = -1;
+ bitmap.height = -1;
+
+ // Draw
+ HDC dc = GetDC( NULL );
+ if ( !dc )
+ {
+ return false;
+ }
+
+ int width, height;
+
+ width = 100;
+ height = 100;
+
+ HBITMAP bmNewImage = (HBITMAP)0;
+
+ HBITMAP bm, oldbm;
+ bm = CreateCompatibleBitmap( dc, width, height );
+ if ( bm )
+ {
+ oldbm = (HBITMAP)SelectObject( dc, bm );
+
+ HDC memdc = CreateCompatibleDC( dc );
+ if ( memdc )
+ {
+ char filename[ 512 ];
+ filesystem->RelativePathToFullPath( relative, "MOD", filename, sizeof( filename ) );
+
+ bmNewImage = (HBITMAP)LoadImage(
+ (HINSTANCE) GetModuleHandle(0), filename,
+ IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE );
+
+ if ( !bmNewImage )
+ {
+ filesystem->RelativePathToFullPath( relative, "GAME", filename, sizeof( filename ) );
+
+ bmNewImage = (HBITMAP)LoadImage(
+ (HINSTANCE) GetModuleHandle(0), filename,
+ IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE );
+ }
+
+ if ( bmNewImage )
+ {
+ HBITMAP oldmembm = (HBITMAP)SelectObject( memdc, bmNewImage );
+
+ BITMAPINFO bmi;
+ memset( &bmi, 0, sizeof( bmi ) );
+ bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
+
+ if ( GetDIBits( memdc, bmNewImage, 0, 0, NULL, &bmi, DIB_RGB_COLORS ) )
+ {
+ bitmap.width = bmi.bmiHeader.biWidth;
+ bitmap.height = bmi.bmiHeader.biHeight;
+ }
+
+ SelectObject( memdc, oldmembm );
+ }
+
+ DeleteDC( memdc );
+ }
+
+ SelectObject( dc, oldbm );
+ DeleteObject( bm );
+ }
+
+ ReleaseDC( NULL, dc );
+
+ if ( bmNewImage &&
+ bitmap.width != -1 &&
+ bitmap.height != -1 )
+ {
+ bitmap.image = bmNewImage;
+ bitmap.valid = true;
+ }
+
+ return bitmap.valid;
+}
+
+void DrawBitmapToWindow( mxWindow *wnd, int x, int y, int w, int h, mxbitmapdata_t& bitmap )
+{
+ if ( !bitmap.valid )
+ return;
+
+ // Draw
+ HDC dc = GetDC( (HWND) wnd->getHandle() );
+ if ( !dc )
+ return;
+
+ HBITMAP bm, oldbm;
+ bm = CreateCompatibleBitmap( dc, w, h );
+ oldbm = (HBITMAP)SelectObject( dc, bm );
+
+ HDC memdc = CreateCompatibleDC( dc );
+ HBITMAP oldmembm = (HBITMAP)SelectObject( memdc, bitmap.image );
+
+ int oldmode = SetStretchBltMode( dc, COLORONCOLOR );
+
+ StretchBlt( dc, x, y, w, h, memdc, 0, 0, bitmap.width, bitmap.height, SRCCOPY );
+
+ SetStretchBltMode( dc, oldmode );
+
+ SelectObject( memdc, oldmembm );
+ DeleteDC( memdc );
+
+ SelectObject( dc, oldbm );
+ DeleteObject( bm );
+ ReleaseDC( (HWND) wnd->getHandle(), dc );
+
+ RECT rc;
+ rc.left = x;
+ rc.right = x + w;
+ rc.top = y;
+ rc.bottom = y + h;
+
+ ValidateRect( (HWND)wnd->getHandle(), &rc );
+}
+
+void DrawBitmapToDC( void *hdc, int x, int y, int w, int h, mxbitmapdata_t& bitmap )
+{
+ if ( !bitmap.valid )
+ return;
+
+ HDC dc = (HDC)hdc;
+
+ HDC memdc = CreateCompatibleDC( dc );
+ HBITMAP oldmembm = (HBITMAP)SelectObject( memdc, bitmap.image );
+
+ int oldmode = SetStretchBltMode( dc, COLORONCOLOR );
+
+ StretchBlt( dc, x, y, w, h, memdc, 0, 0, bitmap.width, bitmap.height, SRCCOPY );
+
+ SetStretchBltMode( dc, oldmode );
+
+ SelectObject( memdc, oldmembm );
+ DeleteDC( memdc );
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/mxbitmaptools.h b/utils/hlfaceposer/mxbitmaptools.h
new file mode 100644
index 0000000..dad3268
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmaptools.h
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( MXBITMAPTOOLS_H )
+#define MXBITMAPTOOLS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+struct mxbitmapdata_t
+{
+ mxbitmapdata_t()
+ {
+ valid = false;
+ image = 0;
+ width = 0;
+ height = 0;
+ }
+
+ bool valid;
+ void *image;
+ int width;
+ int height;
+};
+
+class mxWindow;
+
+bool LoadBitmapFromFile( const char *relative, mxbitmapdata_t& bitmap );
+void DrawBitmapToWindow( mxWindow *wnd, int x, int y, int w, int h, mxbitmapdata_t& bitmap );
+void DrawBitmapToDC( void *hdc, int x, int y, int w, int h, mxbitmapdata_t& bitmap );
+
+#endif // MXBITMAPTOOLS_H \ No newline at end of file
diff --git a/utils/hlfaceposer/mxbitmapwindow.cpp b/utils/hlfaceposer/mxbitmapwindow.cpp
new file mode 100644
index 0000000..d79d9c0
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmapwindow.cpp
@@ -0,0 +1,84 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include <mxtk/mxWindow.h>
+#include "mxBitmapWindow.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include "tier0/dbg.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+// x -
+// y -
+// w -
+// h -
+// 0 -
+// 0 -
+//-----------------------------------------------------------------------------
+mxBitmapWindow::mxBitmapWindow(mxWindow *parent, int x, int y, int w, int h, int style /*= 0*/, const char *bitmap /*=0*/ )
+: mxWindow( parent, x, y, w, h, "", style )
+{
+ m_Bitmap.valid = false;
+
+ if ( bitmap && bitmap[ 0 ] )
+ {
+ Load( bitmap );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+mxBitmapWindow::~mxBitmapWindow( void )
+{
+ if ( m_Bitmap.valid )
+ {
+ DeleteObject( m_Bitmap.image );
+ m_Bitmap.valid = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *bitmap -
+// Output : virtual void
+//-----------------------------------------------------------------------------
+void mxBitmapWindow::setImage( const char *bitmap )
+{
+ if ( m_Bitmap.valid )
+ {
+ DeleteObject( m_Bitmap.image );
+ m_Bitmap.valid = NULL;
+ }
+ if ( bitmap && bitmap[ 0 ] )
+ {
+ Load( bitmap );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *bitmap -
+// Output : mxImage
+//-----------------------------------------------------------------------------
+bool mxBitmapWindow::Load( const char *bitmap )
+{
+ Assert( !m_Bitmap.valid );
+ return LoadBitmapFromFile( bitmap, m_Bitmap );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : void mxBitmapWindow::redraw
+//-----------------------------------------------------------------------------
+void mxBitmapWindow::redraw ()
+{
+ DrawBitmapToWindow( this, 0, 0, w(), h(), m_Bitmap );
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/mxbitmapwindow.h b/utils/hlfaceposer/mxbitmapwindow.h
new file mode 100644
index 0000000..8bb6d5c
--- /dev/null
+++ b/utils/hlfaceposer/mxbitmapwindow.h
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( MXBITMAPWINDOW_H )
+#define MXBITMAPWINDOW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxBitmapTools.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class mxBitmapWindow : public mxWindow
+{
+public:
+ mxBitmapWindow( mxWindow *parent, int x, int y, int w, int h, int style = 0, const char *bitmap = 0 );
+ virtual ~mxBitmapWindow ( void );
+
+ virtual void setImage( const char *bitmap );
+
+ virtual void redraw ();
+
+ virtual bool Load( const char *bitmap );
+private:
+
+ mxbitmapdata_t m_Bitmap;
+
+};
+
+#endif // MXBITMAPWINDOW_H \ No newline at end of file
diff --git a/utils/hlfaceposer/mxexpressionslider.cpp b/utils/hlfaceposer/mxexpressionslider.cpp
new file mode 100644
index 0000000..0e82fc9
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressionslider.cpp
@@ -0,0 +1,723 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include <windows.h>
+#include "mxExpressionSlider.h"
+#include "expressiontool.h"
+#include "mathlib/mathlib.h"
+#include "hlfaceposer.h"
+#include "choreowidgetdrawhelper.h"
+
+mxExpressionSlider::mxExpressionSlider (mxWindow *parent, int x, int y, int w, int h, int id )
+ : mxWindow( parent, x, y, w, h )
+{
+ setId( id );
+ setType( MX_SLIDER );
+
+ FacePoser_AddWindowStyle( this, WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
+
+ m_flMin[ 0 ] = 0.0;
+ m_flMax[ 0 ] = 1.0f;
+ m_nTicks[ 0 ] = 20;
+ m_flCurrent[ 0 ] = 0.0f;
+
+ m_flMin[ 1 ] = 0.0;
+ m_flMax[ 1 ] = 1.0f;
+ m_nTicks[ 1 ] = 20;
+ m_flCurrent[ 1 ] = 0.5f;
+
+ m_flSetting[ 0 ] = 0.0;
+ m_flSetting[ 1 ] = 0.0;
+
+ m_bIsEdited[ 0 ] = false;
+ m_bIsEdited[ 1 ] = false;
+
+ m_bDraggingThumb = false;
+ m_nCurrentBar = 0;
+
+ m_bPaired = false;
+
+ m_nTitleWidth = 120;
+ m_bDrawTitle = true;
+
+ m_pInfluence = new mxCheckBox( this, 2, 4, 12, 12, "", IDC_INFLUENCE );
+}
+
+mxExpressionSlider::~mxExpressionSlider( void )
+{
+}
+
+void mxExpressionSlider::SetTitleWidth( int width )
+{
+ m_nTitleWidth = width;
+ redraw();
+}
+
+void mxExpressionSlider::SetDrawTitle( bool drawTitle )
+{
+ m_bDrawTitle = drawTitle;
+ redraw();
+}
+
+
+void mxExpressionSlider::SetMode( bool paired )
+{
+ if ( m_bPaired != paired )
+ {
+ m_bPaired = paired;
+ redraw();
+ }
+}
+
+void mxExpressionSlider::BoundValue( void )
+{
+ for ( int i = 0; i < NUMBARS; i++ )
+ {
+ if ( m_flCurrent[ i ] > m_flMax[ i ] )
+ {
+ m_flCurrent[ i ] = m_flMax[ i ];
+ }
+ if ( m_flCurrent[ i ] < m_flMin[ i ] )
+ {
+ m_flCurrent[ i ] = m_flMin[ i ];
+ }
+ }
+}
+
+
+void mxExpressionSlider::setValue( int barnum, float value )
+{
+ if (m_flSetting[ barnum ] == value && m_bIsEdited[ barnum ] == false)
+ return;
+
+ m_flSetting[ barnum ] = value;
+ m_bIsEdited[ barnum ] = false;
+
+ if (m_bPaired)
+ {
+ if (m_flSetting[ 0 ] < m_flSetting[ 1 ])
+ {
+ m_flCurrent[ 0 ] = m_flSetting[ 1 ];
+ m_flCurrent[ 1 ] = 1.0 - (m_flSetting[ 0 ] / m_flSetting[ 1 ]) * 0.5;
+ }
+ else if (m_flSetting[ 0 ] > m_flSetting[ 1 ])
+ {
+ m_flCurrent[ 0 ] = m_flSetting[ 0 ];
+ m_flCurrent[ 1 ] = (m_flSetting[ 1 ] / m_flSetting[ 0 ]) * 0.5;
+ }
+ else
+ {
+ m_flCurrent[ 0 ] = m_flSetting[ 0 ];
+ m_flCurrent[ 1 ] = 0.5;
+ }
+ }
+ else
+ {
+ m_flCurrent[ barnum ] = value;
+ }
+
+ BoundValue();
+ // FIXME: delay this until all sliders are set
+ if (!m_bPaired || barnum == 1)
+ redraw();
+}
+
+void mxExpressionSlider::setRange( int barnum, float min, float max, int ticks /*= 100*/ )
+{
+ m_flMin[ barnum ] = min;
+ m_flMax[ barnum ] = max;
+
+ Assert( m_flMax[ barnum ] > m_flMin[ barnum ] );
+
+ m_nTicks[ barnum ] = ticks;
+
+ BoundValue();
+
+ redraw();
+}
+
+void mxExpressionSlider::setInfluence( float value )
+{
+ bool bWasChecked = m_pInfluence->isChecked( );
+ bool bNowChecked = value > 0.0f ? true : false;
+ if (bNowChecked != bWasChecked)
+ {
+ m_pInfluence->setChecked( bNowChecked );
+ redraw();
+ }
+}
+
+float mxExpressionSlider::getRawValue( int barnum ) const
+{
+ return m_flCurrent[ barnum ];
+}
+
+float mxExpressionSlider::getValue( int barnum ) const
+{
+ float scale = 1.0;
+ if (m_bPaired)
+ {
+ // if it's paired, this is assuming that m_flCurrent[0] holds the max value,
+ // and m_flCurrent[1] is a weighting from 0 to 1, with 0.5 being even
+ if (barnum == 0 && m_flCurrent[ 1 ] > 0.5)
+ {
+ scale = (1.0 - m_flCurrent[ 1 ]) / 0.5;
+ }
+ else if (barnum == 1 && m_flCurrent[ 1 ] < 0.5)
+ {
+ scale = (m_flCurrent[ 1 ] / 0.5);
+ }
+ }
+
+ return m_flCurrent[ 0 ] * scale;
+}
+
+float mxExpressionSlider::getMinValue( int barnum ) const
+{
+ return m_flMin[ barnum ];
+}
+
+float mxExpressionSlider::getMaxValue( int barnum ) const
+{
+ return m_flMax[ barnum ];
+}
+
+float mxExpressionSlider::getInfluence( ) const
+{
+ return m_pInfluence->isChecked() ? 1.0f : 0.0f;
+}
+
+void mxExpressionSlider::setEdited( int barnum, bool isEdited )
+{
+ if (m_bIsEdited[ barnum ] == isEdited)
+ return;
+
+ m_bIsEdited[ barnum ] = isEdited;
+ redraw();
+}
+
+bool mxExpressionSlider::isEdited( int barnum ) const
+{
+ return (m_bIsEdited[ barnum ]);
+}
+
+void mxExpressionSlider::GetSliderRect( RECT& rc )
+{
+ HWND wnd = (HWND)getHandle();
+ if ( !wnd )
+ return;
+
+ GetClientRect( wnd, &rc );
+
+ if ( m_bDrawTitle )
+ {
+ rc.left += m_nTitleWidth;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &rc -
+//-----------------------------------------------------------------------------
+void mxExpressionSlider::GetBarRect( RECT &rcBar )
+{
+ RECT rc;
+ GetSliderRect( rc );
+
+ rcBar = rc;
+
+ InflateRect( &rcBar, -10, 0 );
+ rcBar.top += 5;
+ rcBar.bottom = rcBar.top + 2;
+
+ int midy = ( rcBar.top + rcBar.bottom ) / 2;
+ rcBar.top = midy - 1;
+ rcBar.bottom = midy + 1;
+}
+
+
+void mxExpressionSlider::GetThumbRect( int barnum, RECT &rcThumb )
+{
+ RECT rc;
+ GetSliderRect( rc );
+
+ RECT rcBar = rc;
+ GetBarRect( rcBar );
+
+ float frac = 0.0f;
+
+ if ( m_flMax[ barnum ] - m_flMin[ barnum ] > 0 )
+ {
+ frac = (m_flCurrent[ barnum ] - m_flMin[ barnum ]) / ( m_flMax[ barnum ] - m_flMin[ barnum ] );
+ }
+
+ int tickmark = (int)( frac * m_nTicks[ barnum ] + 0.5 );
+ tickmark = min( m_nTicks[ barnum ], tickmark );
+ tickmark = max( 0, tickmark );
+
+ int thumbwidth = 20;
+ int thumbheight = 14;
+ int xoffset = -thumbwidth / 2;
+ int yoffset = -thumbheight / 2 + 2;
+
+ int leftedge = rcBar.left + (int)( (float)( rcBar.right - rcBar.left ) * (float)(tickmark) / (float)m_nTicks[ barnum ] );
+
+ rcThumb.left = leftedge + xoffset;
+ rcThumb.right = rcThumb.left + thumbwidth;
+ rcThumb.top = rcBar.top + yoffset;
+ rcThumb.bottom = rcThumb.top + thumbheight;
+}
+
+void mxExpressionSlider::DrawBar( HDC& dc )
+{
+ RECT rcBar;
+
+ GetBarRect( rcBar );
+
+ HPEN oldPen;
+
+ HPEN shadow;
+ HBRUSH face;
+ HPEN hilight;
+
+ shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DSHADOW ) );
+ hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
+ face = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
+
+ oldPen = (HPEN)SelectObject( dc, hilight );
+
+ MoveToEx( dc, rcBar.left, rcBar.bottom, NULL );
+ LineTo( dc, rcBar.left, rcBar.top );
+ LineTo( dc, rcBar.right, rcBar.top );
+
+ SelectObject( dc, shadow );
+
+ LineTo( dc, rcBar.right, rcBar.bottom );
+ LineTo( dc, rcBar.left, rcBar.bottom );
+
+ rcBar.left += 1;
+ //rcBar.right -= 1;
+ rcBar.top += 1;
+ rcBar.bottom -= 1;
+
+ FillRect( dc, &rcBar, face );
+
+ SelectObject( dc, oldPen );
+
+ DeleteObject( face );
+ DeleteObject( shadow );
+ DeleteObject( hilight );
+}
+
+void mxExpressionSlider::DrawThumb( int barnum, HDC& dc )
+{
+ RECT rcThumb;
+
+ GetThumbRect( barnum, rcThumb );
+
+ // Draw it
+
+
+ HPEN oldPen;
+
+ HPEN shadow;
+ HBRUSH face;
+ HPEN hilight;
+
+ shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DDKSHADOW ) );
+ hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
+
+ switch ( barnum )
+ {
+ default:
+ case MAGNITUDE_BAR:
+ {
+ float frac;
+ if (m_flCurrent[ barnum ] < 0)
+ {
+ frac = m_flCurrent[ barnum ] / m_flMin[ barnum ];
+ }
+ else
+ {
+ frac = m_flCurrent[ barnum ] / m_flMax[ barnum ];
+ }
+ frac = min( 1.0f, frac );
+ frac = max( 0.0f, frac );
+
+ COLORREF clr = GetSysColor( COLOR_3DFACE );
+ int r, g, b;
+ r = GetRValue( clr );
+ g = GetRValue( clr );
+ b = GetRValue( clr );
+
+ // boost colors
+ r = (int)( (1-frac) * b );
+ b = min( 255, (int)(r + ( 255 - r ) * frac ) );
+ g = (int)( (1-frac) * g );
+
+ face = CreateSolidBrush( RGB( r, g, b ) );
+ }
+ break;
+ case BALANCE_BAR:
+ {
+ float frac = m_flCurrent[ barnum ];
+ frac = min( 1.0f, frac );
+ frac = max( 0.0f, frac );
+
+ COLORREF clr = GetSysColor( COLOR_3DFACE );
+ int r, g, b;
+ r = GetRValue( clr );
+ g = GetRValue( clr );
+ b = GetRValue( clr );
+
+ // boost colors toward red if we are not at 0.5
+ float boost = 2.0 * ( fabs( frac - 0.5f ) );
+
+ r = r + ( 255 - r ) * boost;
+ g = ( 1 - boost ) * g;
+ b = ( 1 - boost ) * b;
+
+ face = CreateSolidBrush( RGB( r, g, b ) );
+ }
+ break;
+ }
+
+ //rcThumb.left += 1;
+ //rcThumb.right -= 1;
+ //rcThumb.top += 1;
+ //rcThumb.bottom -= 1;
+
+ //FillRect( dc, &rcThumb, face );
+ POINT region[3];
+ int cPoints = 3;
+
+ InflateRect( &rcThumb, -2, 0 );
+
+// int height = rcThumb.bottom - rcThumb.top;
+// int offset = height / 2 + 1;
+ int offset = 2;
+
+ switch ( barnum )
+ {
+ case MAGNITUDE_BAR:
+ default:
+ {
+ region[ 0 ].x = rcThumb.left;
+ region[ 0 ].y = rcThumb.top;
+
+ region[ 1 ].x = rcThumb.right;
+ region[ 1 ].y = rcThumb.top;
+
+ region[ 2 ].x = ( rcThumb.left + rcThumb.right ) / 2;
+ region[ 2 ].y = rcThumb.bottom - offset;
+ }
+ break;
+ case BALANCE_BAR:
+ {
+ region[ 0 ].x = ( rcThumb.left + rcThumb.right ) / 2;
+ region[ 0 ].y = rcThumb.top + offset;
+
+ region[ 1 ].x = rcThumb.left;
+ region[ 1 ].y = rcThumb.bottom;
+
+ region[ 2 ].x = rcThumb.right;
+ region[ 2 ].y = rcThumb.bottom;
+
+ }
+ break;
+ }
+
+ HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
+
+ int oldPF = SetPolyFillMode( dc, ALTERNATE );
+ FillRgn( dc, rgn, face );
+ SetPolyFillMode( dc, oldPF );
+
+ DeleteObject( rgn );
+
+ oldPen = (HPEN)SelectObject( dc, hilight );
+
+ MoveToEx( dc, region[0].x, region[0].y, NULL );
+ LineTo( dc, region[1].x, region[1].y );
+ SelectObject( dc, shadow );
+ LineTo( dc, region[2].x, region[2].y );
+ SelectObject( dc, hilight );
+ LineTo( dc, region[0].x, region[0].y );
+
+ SelectObject( dc, oldPen );
+
+ DeleteObject( face );
+ DeleteObject( shadow );
+ DeleteObject( hilight );
+}
+
+void mxExpressionSlider::DrawTitle( HDC &dc )
+{
+ if ( !m_bDrawTitle )
+ return;
+
+ HWND wnd = (HWND)getHandle();
+ if ( !wnd )
+ return;
+
+ RECT rc;
+ GetClientRect( wnd, &rc );
+ rc.right = m_nTitleWidth;
+ rc.left += 16;
+
+ InflateRect( &rc, -5, -2 );
+
+ char sz[ 128 ];
+ sprintf( sz, "%s", getLabel() );
+
+ HFONT fnt, oldfont;
+
+ fnt = CreateFont(
+ -12 // H
+ , 0 // W
+ , 0 // Escapement
+ , 0 // Orient
+ , FW_NORMAL // Wt. (BOLD)
+ , 0 // Ital.
+ , 0 // Underl.
+ , 0 // SO
+ , ANSI_CHARSET // Charset
+ , OUT_TT_PRECIS // Out prec.
+ , CLIP_DEFAULT_PRECIS // Clip prec.
+ , PROOF_QUALITY // Qual.
+ , VARIABLE_PITCH | FF_DONTCARE // Pitch and Fam.
+ , "Arial" );
+
+ COLORREF oldColor;
+
+ if (!isEdited( 0 ))
+ {
+ oldColor = SetTextColor( dc, GetSysColor( COLOR_BTNTEXT ) );
+ }
+ else
+ {
+ oldColor = SetTextColor( dc, RGB( 255, 0, 0 ) );
+ }
+ int oldMode = SetBkMode( dc, TRANSPARENT );
+ oldfont = (HFONT)SelectObject( dc, fnt );
+
+ DrawText( dc, sz, -1, &rc, DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_WORD_ELLIPSIS );
+
+ SelectObject( dc, oldfont );
+ DeleteObject( fnt );
+ SetBkMode( dc, oldMode );
+ SetTextColor( dc, oldColor );
+}
+
+void mxExpressionSlider::redraw()
+{
+ HWND wnd = (HWND)getHandle();
+ if ( !wnd )
+ return;
+
+ HDC finalDC = GetDC( wnd );
+ if ( !finalDC )
+ return;
+
+ RECT rc;
+ GetClientRect( wnd, &rc );
+
+ int w = rc.right - rc.left;
+ int h = rc.bottom - rc.top;
+
+ HDC dc = CreateCompatibleDC( finalDC );
+ HBITMAP oldbm, bm;
+
+ bm = CreateCompatibleBitmap( finalDC, w, h );
+
+ oldbm = (HBITMAP)SelectObject( dc, bm );
+
+ HBRUSH br = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
+
+ FillRect( dc, &rc, br );
+
+ DeleteObject( br );
+
+ DrawTitle( dc );
+
+ DrawBar( dc );
+
+ // Draw slider
+ for ( int i = ( m_bPaired ? 1 : 0 ); i >= 0 ; i-- )
+ {
+ DrawThumb( i, dc );
+ }
+
+ BitBlt( finalDC, 0, 0, w, h, dc, 0, 0, SRCCOPY );
+
+ SelectObject( dc, oldbm );
+
+ DeleteObject( bm );
+
+ DeleteDC( dc );
+
+ ReleaseDC( wnd, finalDC );
+ ValidateRect( wnd, &rc );
+}
+
+void mxExpressionSlider::MoveThumb( int barnum, int xpos, bool finish )
+{
+ RECT rcBar;
+
+ GetBarRect( rcBar );
+
+ if ( xpos < rcBar.left )
+ {
+ m_flCurrent[ barnum ] = m_flMin[ barnum ];
+ }
+ else if ( xpos > rcBar.right )
+ {
+ m_flCurrent[ barnum ] = m_flMax[ barnum ];
+ }
+ else
+ {
+ float frac = (float)( xpos - rcBar.left ) / (float)( rcBar.right - rcBar.left );
+ // snap slider to nearest "tick" so that it get drawn in the correct place
+ int curtick = (int)( frac * m_nTicks[ 0 ] + 0.5);
+ m_flCurrent[ barnum ] = m_flMin[ barnum ] + ((float)curtick / (float)m_nTicks[ 0 ]) * (m_flMax[ barnum ] - m_flMin[ barnum ]);
+ }
+
+ // update equivalent setting
+ m_flSetting[ 0 ] = getValue( 0 );
+ m_flSetting[ 1 ] = getValue( 1 );
+
+ m_bIsEdited[ 0 ] = true;
+ m_bIsEdited[ 1 ] = true;
+
+ // Send message to parent
+ HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
+ if ( parent )
+ {
+ LPARAM lp;
+ WPARAM wp;
+
+ wp = MAKEWPARAM( finish ? SB_ENDSCROLL : SB_THUMBPOSITION, barnum );
+ lp = (long)getHandle();
+
+ SendMessage( parent, WM_HSCROLL, wp, lp );
+ }
+
+ BoundValue();
+ redraw();
+}
+
+bool mxExpressionSlider::PaintBackground( void )
+{
+ return false;
+}
+
+int mxExpressionSlider::handleEvent( mxEvent *event )
+{
+ int iret = 0;
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_INFLUENCE:
+ {
+ SetFocus( (HWND)getHandle() );
+
+ setEdited( 0, false );
+ setEdited( 1, false );
+
+ // Send message to parent
+ HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
+ if ( parent )
+ {
+ LPARAM lp;
+ WPARAM wp;
+
+ wp = MAKEWPARAM( SB_ENDSCROLL, m_nCurrentBar );
+ lp = (long)getHandle();
+
+ SendMessage( parent, WM_HSCROLL, wp, lp );
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ SetFocus( (HWND)getHandle() );
+
+ if ( !m_bDraggingThumb )
+ {
+ RECT rcThumb;
+ POINT pt;
+
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+
+ m_nCurrentBar = ( event->buttons & mxEvent::MouseRightButton ) ? BALANCE_BAR : MAGNITUDE_BAR;
+ GetThumbRect( m_nCurrentBar, rcThumb );
+
+ if ( PtInRect( &rcThumb, pt ) )
+ {
+ m_bDraggingThumb = true;
+ }
+
+ // Snap position if they didn't click on the thumb itself
+#if 0
+ else
+ {
+ m_bDraggingThumb = true;
+ MoveThumb( m_nCurrentBar, (short)event->x, false );
+ }
+#endif
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ if ( m_bDraggingThumb )
+ {
+ m_pInfluence->setChecked( true );
+ MoveThumb( m_nCurrentBar, (short)event->x, false );
+ iret = 1;
+ }
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ if ( m_bDraggingThumb )
+ {
+ m_pInfluence->setChecked( true );
+ m_bDraggingThumb = false;
+ MoveThumb( m_nCurrentBar, (short)event->x, true );
+ m_nCurrentBar = 0;
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ if ( event->key == VK_RETURN ||
+ event->key == 'S' )
+ {
+ // See if there is an event in the flex animation window
+ g_pExpressionTool->OnSetSingleKeyFromFlex( getLabel() );
+ }
+ }
+ break;
+ }
+
+ return iret;
+}
diff --git a/utils/hlfaceposer/mxexpressionslider.h b/utils/hlfaceposer/mxexpressionslider.h
new file mode 100644
index 0000000..6ea7ef1
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressionslider.h
@@ -0,0 +1,91 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MXEXPRESSIONSLIDER_H
+#define MXEXPRESSIONSLIDER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mxWindow.h>
+#include <mxtk/mxCheckBox.h>
+
+#define IDC_INFLUENCE 1000
+
+class mxExpressionSlider : public mxWindow
+{
+public:
+ enum
+ {
+ MAGNITUDE_BAR = 0,
+ BALANCE_BAR = 1,
+
+ NUMBARS = 2
+ };
+
+ mxExpressionSlider (mxWindow *parent, int x, int y, int w, int h, int id );
+ ~mxExpressionSlider( void );
+
+ void SetTitleWidth( int width );
+ void SetDrawTitle( bool drawTitle );
+
+ void SetMode( bool paired );
+
+ void setValue ( int barnum, float value);
+ void setRange ( int barnum, float min, float max, int ticks = 100);
+ void setInfluence ( float value );
+ void setEdited( bool isEdited );
+
+ // ACCESSORS
+ float getRawValue( int barnum ) const;
+
+ float getValue( int barnum ) const;
+ float getMinValue( int barnum ) const;
+ float getMaxValue( int barnum ) const;
+ float getInfluence( ) const;
+
+ void setEdited( int barnum, bool isEdited );
+ bool isEdited( int barnum ) const;
+
+ virtual void redraw();
+ virtual bool PaintBackground( void );
+
+ virtual int handleEvent (mxEvent *event);
+
+private:
+ void BoundValue( void );
+
+ void GetSliderRect( RECT& rc );
+ void GetBarRect( RECT &rc );
+ void GetThumbRect( int barnum, RECT &rc );
+
+ void DrawBar( HDC& dc );
+ void DrawThumb( int barnum, HDC& dc );
+ void DrawTitle( HDC &dc );
+
+ void MoveThumb( int barnum, int xpos, bool finish );
+
+ int m_nTitleWidth;
+ bool m_bDrawTitle;
+
+ float m_flMin[ NUMBARS ], m_flMax[ NUMBARS ];
+ int m_nTicks[ NUMBARS ];
+ float m_flCurrent[ NUMBARS ]; // slider location (amount/balance)
+
+ float m_flSetting[ NUMBARS ]; // paired flex input values (right/left)
+ bool m_bIsEdited[ NUMBARS ]; // paired flex input values (right/left)
+
+ bool m_bDraggingThumb;
+ int m_nCurrentBar;
+
+ bool m_bPaired;
+
+ mxCheckBox *m_pInfluence;
+};
+
+
+#endif // MXEXPRESSIONSLIDER_H
diff --git a/utils/hlfaceposer/mxexpressiontab.cpp b/utils/hlfaceposer/mxexpressiontab.cpp
new file mode 100644
index 0000000..21efec0
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressiontab.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "hlfaceposer.h"
+#include "mxExpressionTab.h"
+#include "mdlviewer.h"
+#include "expressions.h"
+
+mxExpressionTab *g_pExpressionClass = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose: Right click context menu
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void mxExpressionTab::ShowRightClickMenu( int mx, int my )
+{
+ if ( !g_MDLViewer )
+ return;
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ pop->add( "New...", IDC_EXPRESSIONS_NEW );
+ pop->addSeparator ();
+ pop->add( "Load...", IDC_EXPRESSIONS_LOAD );
+ pop->add( "Save", IDC_EXPRESSIONS_SAVE );
+ pop->addSeparator ();
+ pop->add( "Export to VFE", IDC_EXPRESSIONS_EXPORT );
+ pop->addSeparator ();
+ if ( m_nSelected != -1 )
+ {
+ pop->add( "Close class", IDC_EXPRESSIONS_CLOSE );
+ }
+ pop->add( "Close all classes", IDC_EXPRESSIONS_CLOSEALL );
+ pop->addSeparator();
+ pop->add( "Recreate all bitmaps", IDC_EXPRESSIONS_REDOBITMAPS );
+
+ // Convert click position
+ POINT pt;
+ pt.x = mx;
+ pt.y = my;
+ ClientToScreen( (HWND)getHandle(), &pt );
+ ScreenToClient( (HWND)g_MDLViewer->getHandle(), &pt );
+
+ // Convert coordinate space
+ pop->popup( g_MDLViewer, pt.x, pt.y );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int mxExpressionTab::getSelectedIndex () const
+{
+ // Convert based on override index
+ return m_nSelected;
+}
diff --git a/utils/hlfaceposer/mxexpressiontab.h b/utils/hlfaceposer/mxexpressiontab.h
new file mode 100644
index 0000000..2eea9a3
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressiontab.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef MXEXPRESSIONTAB_H
+#define MXEXPRESSIONTAB_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tabwindow.h"
+
+class mxExpressionTab : public CTabWindow
+{
+public:
+ mxExpressionTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 )
+ : CTabWindow( parent, x, y, w, h, id, style )
+ {
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my );
+ virtual int getSelectedIndex () const;
+};
+
+extern mxExpressionTab *g_pExpressionClass;
+
+#endif // MXEXPRESSIONTAB_H
diff --git a/utils/hlfaceposer/mxexpressiontray.cpp b/utils/hlfaceposer/mxexpressiontray.cpp
new file mode 100644
index 0000000..57bdd6d
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressiontray.cpp
@@ -0,0 +1,1212 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <windows.h>
+#include <stdio.h>
+#include <mxtk/mxWindow.h>
+#include <mxtk/mxScrollBar.h>
+#include "mxexpressiontray.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "ControlPanel.h"
+#include "FlexPanel.h"
+#include <mxtk/mxPopupMenu.h>
+#include "ChoreoView.h"
+#include "StudioModel.h"
+#include "ExpressionProperties.h"
+#include "InputProperties.h"
+#include "ViewerSettings.h"
+#include "mxExpressionTab.h"
+#include "choreowidgetdrawhelper.h"
+#include "ExpressionTool.h"
+#include "faceposer_models.h"
+#include "tier0/icommandline.h"
+#include "filesystem.h"
+
+#define MAX_THUMBNAILSIZE 256
+#define MIN_THUMBNAILSIZE 64
+#define THUMBNAIL_SIZE_STEP 4
+#define DEFAULT_THUMBNAIL_SIZE 128
+#define TOP_GAP 45
+
+mxExpressionTray *g_pExpressionTrayTool = 0;
+
+mxExpressionTray::mxExpressionTray( mxWindow *parent, int id /*=0*/ )
+: IFacePoserToolWindow( "ExpressionTrayTool", "Expressions" ), mxWindow( parent, 0, 0, 0, 0, "ExpressionTrayTool", id )
+{
+ setId( id );
+
+ m_nTopOffset = 0;
+ slScrollbar = new mxScrollbar( this, 0, 0, 18, 100, IDC_TRAYSCROLL, mxScrollbar::Vertical );
+
+ m_nLastNumExpressions = -1;
+
+ m_nGranularity = 10;
+
+ m_nPrevCell = -1;
+ m_nCurCell = -1;
+
+ m_nClickedCell = -1;
+
+ m_nButtonSquare = 16;
+
+ m_nGap = 4;
+ m_nDescriptionHeight = 34;
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ m_pButtons = NULL;
+
+ m_nPreviousExpressionCount = -1;
+
+ m_bDragging = false;
+ m_nDragCell = -1;
+
+ CreateButtons();
+
+ g_pExpressionClass = new mxExpressionTab( this, 5, 5, 500, 20, IDC_EXPRESSIONCLASS );
+
+ m_pABButton = new mxButton( this, 520, 8, 50, 18, "A/B", IDC_AB );
+ m_pThumbnailIncreaseButton = new mxButton( this, 0, 0, 18, 18, "+", IDC_THUMBNAIL_INCREASE );
+ m_pThumbnailDecreaseButton = new mxButton( this, 0, 0, 18, 18, "-", IDC_THUMBNAIL_DECREASE );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : mxExpressionTray::~mxExpressionTray
+//-----------------------------------------------------------------------------
+mxExpressionTray::~mxExpressionTray ( void )
+{
+ DeleteAllButtons();
+ g_pExpressionTrayTool = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : cellsize -
+//-----------------------------------------------------------------------------
+void mxExpressionTray::SetCellSize( int cellsize )
+{
+ m_nSnapshotWidth = cellsize;
+ m_nSnapshotHeight = cellsize + m_nDescriptionHeight;
+
+ DeleteAllButtons();
+ CreateButtons();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::Deselect( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ for ( int i = 0 ; i < active->GetNumExpressions(); i++ )
+ {
+ CExpression *exp = active->GetExpression( i );
+ if ( exp )
+ {
+ exp->SetSelected( false );
+ }
+ }
+ }
+
+ m_nCurCell = m_nPrevCell = -1;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : exp -
+//-----------------------------------------------------------------------------
+void mxExpressionTray::Select( int exp, bool deselect /*=true*/ )
+{
+ int oldcell = m_nCurCell;
+
+ if ( deselect )
+ {
+ Deselect();
+ }
+
+ m_nPrevCell = oldcell;
+ m_nCurCell = exp;
+
+ if ( m_nCurCell >= 0 )
+ {
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ CExpression *exp = active->GetExpression( m_nCurCell );
+ if ( exp )
+ {
+ exp->SetSelected( true );
+ }
+ }
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *btn -
+//-----------------------------------------------------------------------------
+void mxExpressionTray::AddButton( const char *name, const char *tooltip, const char *bitmap, ETMEMBERFUNC pfnCallback,
+ bool active, int x, int y, int w, int h )
+{
+ mxETButton *btn = new mxETButton;
+ strcpy( btn->m_szName, name );
+ strcpy( btn->m_szToolTip, tooltip );
+ btn->m_bActive = active;
+ btn->m_rc.left = x;
+ btn->m_rc.top = y;
+ btn->m_rc.right = x + w;
+ btn->m_rc.bottom = y + h;
+
+ btn->m_pImage = new mxbitmapdata_t;
+ Assert( btn->m_pImage );
+ btn->m_pImage->valid = false;
+ LoadBitmapFromFile( bitmap, *btn->m_pImage );
+
+ btn->m_fnCallback = pfnCallback;
+
+ btn->next = m_pButtons;
+ m_pButtons = btn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::CreateButtons( void )
+{
+ int x = m_nSnapshotWidth - 2 * ( m_nButtonSquare + 4 );
+ int y = 4;
+
+ AddButton( "undo", "Undo", "gfx/hlfaceposer/undo.bmp", &mxExpressionTray::ET_Undo, true, x, y, m_nButtonSquare, m_nButtonSquare );
+
+ x += ( m_nButtonSquare + 4 );
+
+ AddButton( "redo", "Redo", "gfx/hlfaceposer/redo.bmp", &mxExpressionTray::ET_Redo, true, x, y, m_nButtonSquare, m_nButtonSquare );
+}
+
+void mxExpressionTray::ActivateButton( const char *name, bool active )
+{
+ mxETButton *btn = FindButton( name );
+ if ( !name )
+ return;
+
+ btn->m_bActive = active;
+}
+
+mxExpressionTray::mxETButton *mxExpressionTray::FindButton( const char *name )
+{
+ mxETButton *p = m_pButtons;
+ while ( p )
+ {
+ if ( !stricmp( p->m_szName, name ) )
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::DeleteAllButtons( void )
+{
+ mxETButton *p = m_pButtons, *n;
+ while ( p )
+ {
+ n = p->next;
+ delete p->m_pImage;
+ delete p;
+ p = n;
+ }
+ m_pButtons = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+// y -
+// Output : mxExpressionTray::mxETButton
+//-----------------------------------------------------------------------------
+mxExpressionTray::mxETButton *mxExpressionTray::GetItemUnderCursor( int x, int y )
+{
+ // Convert to cell space
+ int cell = GetCellUnderPosition( x, y );
+ if ( cell == -1 )
+ {
+ return NULL;
+ }
+
+ // Cell is off screen?
+ int cx, cy, cw, ch;
+ if ( !ComputeRect( cell, cx, cy, cw, ch ) )
+ {
+ return NULL;
+ }
+
+
+ mxETButton *p = m_pButtons;
+ while ( p )
+ {
+ if ( p->m_bActive &&
+ x >= cx &&
+ x <= cx + cw &&
+ y >= cy &&
+ y <= cy + ch )
+ {
+ // In-side cell
+ int cellx = x - cx;
+ int celly = y - cy;
+
+ if ( cellx >= p->m_rc.left &&
+ cellx <= p->m_rc.right &&
+ celly >= p->m_rc.top &&
+ celly <= p->m_rc.bottom )
+ {
+ return p;
+ }
+ }
+ p = p->next;
+ }
+
+ return NULL;
+}
+
+void mxExpressionTray::DrawButton( CChoreoWidgetDrawHelper& helper, int cell, mxETButton *btn )
+{
+ if ( !btn || !btn->m_pImage || !btn->m_pImage->valid )
+ return;
+
+ if ( !btn->m_bActive )
+ return;
+
+ int x, y, w, h;
+ if ( !ComputeRect( cell, x, y, w, h ) )
+ return;
+
+ x += btn->m_rc.left;
+ y += btn->m_rc.top;
+ w = btn->m_rc.right - btn->m_rc.left;
+ h = btn->m_rc.bottom - btn->m_rc.top;
+
+ HDC dc = helper.GrabDC();
+
+ DrawBitmapToDC( dc, x, y, w, h, *btn->m_pImage );
+ helper.DrawOutlinedRect( RGB( 170, 170, 170 ), PS_SOLID, 1, x, y, x + w, y + h );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int mxExpressionTray::ComputePixelsNeeded( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return 100;
+
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int rowsneeded = ( ( active->GetNumExpressions() + colsperrow - 1 ) / colsperrow );
+ return rowsneeded * ( m_nSnapshotHeight + m_nGap ) + m_nGap + TOP_GAP + GetCaptionHeight();
+}
+
+bool mxExpressionTray::ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch )
+{
+ // Remove scroll bar
+ int w = this->w2() - 16;
+
+ int colsperrow;
+
+ colsperrow = ( w - m_nGap ) / ( m_nSnapshotWidth + m_nGap );
+ // At least one
+ colsperrow = max( 1, colsperrow );
+
+ int row, col;
+
+ row = cell / colsperrow;
+ col = cell % colsperrow;
+
+ // don't allow partial columns
+
+ rcx = m_nGap + col * ( m_nSnapshotWidth + m_nGap );
+ rcy = GetCaptionHeight() + TOP_GAP + ( -m_nTopOffset * m_nGranularity ) + m_nGap + row * ( m_nSnapshotHeight + m_nGap );
+
+ // Starts off screen
+ if ( rcx < 0 )
+ return false;
+
+ // Ends off screen
+ if ( rcx + m_nSnapshotWidth + m_nGap > this->w2() )
+ return false;
+
+ // Allow partial in y direction
+ if ( rcy > this->h2() )
+ return false;
+
+ if ( rcy + m_nSnapshotHeight + m_nGap < 0 )
+ return false;
+
+ // Some portion is onscreen
+ rcw = m_nSnapshotWidth;
+ rch = m_nSnapshotHeight;
+ return true;
+}
+
+void mxExpressionTray::DrawExpressionFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr )
+{
+ helper.DrawOutlinedRect( clr, PS_SOLID, 4, x, y, x + w, y + h );
+}
+
+void mxExpressionTray::DrawExpressionDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, const char *expressionname, const char *description )
+{
+ int textheight = 15;
+
+ RECT textRect;
+ textRect.left = x + 5;
+ textRect.top = y + h - 2 * textheight - 12;
+ textRect.right = x + w - 10;
+ textRect.bottom = y + h - 12;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 0, 0, 0 ), textRect, "%s", expressionname );
+
+// DrawText( hdc, expressionname, strlen( expressionname ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
+
+ OffsetRect( &textRect, 0, textheight );
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 63, 63, 63 ), textRect, "%s", description );
+
+// DrawText( hdc, description, strlen( description ), &textRect, DT_NOPREFIX | DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_WORD_ELLIPSIS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dc -
+// current -
+// rcx -
+// rcy -
+// rcw -
+// rch -
+//-----------------------------------------------------------------------------
+void mxExpressionTray::DrawDirtyFlag( CChoreoWidgetDrawHelper& helper, CExpression *current, int rcx, int rcy, int rcw, int rch )
+{
+ // Not dirty
+ if ( !current || ( !current->CanUndo() && !current->GetDirty() ) )
+ return;
+
+ int fontsize = 14;
+
+ RECT textRect;
+ textRect.left = rcx + 5;
+ textRect.right = rcx + rcw;
+ textRect.top = rcy + 5;
+ textRect.bottom = textRect.top + fontsize + 2;
+
+ helper.DrawColoredText( "Arial", fontsize, FW_NORMAL, RGB( 100, 240, 255 ), textRect, "*" );
+}
+
+bool mxExpressionTray::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+void mxExpressionTray::DrawThumbNail( CExpClass *active, CExpression *current, CChoreoWidgetDrawHelper& helper, int rcx, int rcy, int rcw, int rch, int c, int selected, bool updateselection )
+{
+ if ( !current )
+ return;
+
+ HDC dc = helper.GrabDC();
+
+ helper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rcx, rcy, rcw + rcx, rch + rcy );
+
+ if ( current->m_Bitmap[ models->GetActiveModelIndex() ].valid )
+ {
+ DrawBitmapToDC( dc, rcx, rcy, rcw, rch - m_nDescriptionHeight, current->m_Bitmap[ models->GetActiveModelIndex() ] );
+ helper.DrawOutlinedRect( RGB( 127, 127, 127 ), PS_SOLID, 1, rcx, rcy, rcx + rcw, rcy + rch - m_nDescriptionHeight );
+ }
+
+ DrawDirtyFlag( helper, current, rcx, rcy, rcw, rch );
+
+ DrawExpressionDescription( helper, rcx, rcy, rcw, rch, current->name, current->description );
+
+ if ( c == selected )
+ {
+ DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 255, 100, 63 ) );
+
+ if ( updateselection )
+ {
+ m_nPrevCell = -1;
+ m_nCurCell = c;
+ }
+
+ if ( current->CanUndo() || current->CanRedo() )
+ {
+ if ( current->CanUndo() )
+ {
+ DrawButton( helper, c, FindButton( "undo" ) );
+ }
+
+ if ( current->CanRedo() )
+ {
+ DrawButton( helper, c, FindButton( "redo" ) );
+ }
+
+ RECT rc;
+ rc.left = rcx + rcw - 2 * ( m_nButtonSquare + 4 );
+ rc.top = rcy + m_nButtonSquare + 6;
+ rc.right = rc.left + 2 * ( m_nButtonSquare + 4 );
+ rc.bottom = rc.top + 15;
+
+ helper.DrawColoredText( "Arial", 9, FW_NORMAL, RGB( 200, 200, 200 ), rc,
+ "%i/%i", current->UndoCurrent(), current->UndoLevels() );
+ }
+
+ }
+ else
+ {
+ if ( current->GetSelected() )
+ {
+ DrawExpressionFocusRect( helper, rcx, rcy, rcw, rch - m_nDescriptionHeight, RGB( 127, 127, 220 ) );
+ }
+ }
+}
+
+void mxExpressionTray::redraw()
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ bool updateSelection = false;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active && active->GetNumExpressions() != m_nPreviousExpressionCount )
+ {
+ m_nTopOffset = 0;
+
+ RepositionSlider();
+ m_nPreviousExpressionCount = active->GetNumExpressions();
+ }
+
+ CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
+ HandleToolRedraw( helper );
+
+ int w, h;
+ w = w2();
+ h = h2();
+
+ if ( active )
+ {
+ RECT clipRect;
+ helper.GetClientRect( clipRect );
+
+ clipRect.top += TOP_GAP + GetCaptionHeight();
+
+ helper.StartClipping( clipRect );
+
+ if ( m_nLastNumExpressions != active->GetNumExpressions() )
+ {
+ m_nTopOffset = 0;
+ m_nLastNumExpressions = active->GetNumExpressions();
+ RepositionSlider();
+ updateSelection = true;
+ }
+
+ int selected = active->GetSelectedExpression();
+
+ int rcx, rcy, rcw, rch;
+
+ int c = 0;
+ while ( c < active->GetNumExpressions() )
+ {
+ if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
+ {
+ c++;
+ continue;
+ }
+
+ CExpression *current = active->GetExpression( c );
+ if ( !current )
+ break;
+
+ DrawThumbNail( active, current, helper, rcx, rcy, rcw, rch, c, selected, updateSelection );
+
+ c++;
+ }
+
+ helper.StopClipping();
+
+ }
+ else
+ {
+
+ RECT rc;
+ helper.GetClientRect( rc );
+
+ // Arial 36 normal
+ char sz[ 256 ];
+ sprintf( sz, "No expression file loaded" );
+
+ int pointsize = 18;
+
+ int textlen = helper.CalcTextWidth( "Arial", pointsize, FW_NORMAL, sz );
+
+ RECT rcText;
+ rcText.top = ( rc.bottom - rc.top ) / 2 - pointsize / 2;
+ rcText.bottom = rcText.top + pointsize + 10;
+ int fullw = rc.right - rc.left;
+
+ rcText.left = rc.left + ( fullw - textlen ) / 2;
+ rcText.right = rcText.left + textlen;
+
+ helper.DrawColoredText( "Arial", pointsize, FW_NORMAL, RGB( 80, 80, 80 ), rcText, sz );
+ }
+
+
+// ValidateRect( (HWND)getHandle(), &rc );
+}
+
+int mxExpressionTray::GetCellUnderPosition( int x, int y )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return -1;
+
+ int rcx, rcy, rcw, rch;
+ int c = 0;
+ while ( c < active->GetNumExpressions() )
+ {
+ if ( !ComputeRect( c, rcx, rcy, rcw, rch ) )
+ {
+ c++;
+ continue;
+ }
+
+ if ( x >= rcx && x <= rcx + rcw &&
+ y >= rcy && y <= rcy + rch )
+ {
+ return c;
+ }
+
+ c++;
+ }
+ return -1;
+}
+
+void mxExpressionTray::RepositionSlider( void )
+{
+ int trueh = h2() - GetCaptionHeight();
+
+ int heightpixels = trueh / m_nGranularity;
+ int rangepixels = ComputePixelsNeeded() / m_nGranularity;
+
+ if ( rangepixels < heightpixels )
+ {
+ m_nTopOffset = 0;
+ slScrollbar->setVisible( false );
+ }
+ else
+ {
+ slScrollbar->setVisible( true );
+ }
+
+ slScrollbar->setBounds( w2() - 16, GetCaptionHeight() + TOP_GAP, 16, trueh - TOP_GAP );
+
+ m_nTopOffset = max( 0, m_nTopOffset );
+ m_nTopOffset = min( rangepixels, m_nTopOffset );
+
+ slScrollbar->setRange( 0, rangepixels );
+ slScrollbar->setValue( m_nTopOffset );
+ slScrollbar->setPagesize( heightpixels );
+}
+
+void mxExpressionTray::AB( void )
+{
+ if ( m_nPrevCell == -1 && m_nCurCell == -1 )
+ return;
+
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ if ( m_nPrevCell >= 0 && m_nPrevCell < active->GetNumExpressions() )
+ {
+ active->SelectExpression( m_nPrevCell );
+ }
+}
+
+int mxExpressionTray::CountSelected( void )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return 0;
+
+ int c = 0;
+ for ( int i = 0; i < active->GetNumExpressions(); i++ )
+ {
+ CExpression *exp = active->GetExpression( i );
+ if ( !exp )
+ continue;
+
+ if ( exp->GetSelected() )
+ {
+ c++;
+ }
+ }
+
+ return c;
+}
+
+void mxExpressionTray::SetClickedCell( int cell )
+{
+ m_nClickedCell = cell;
+}
+
+void mxExpressionTray::ShowRightClickMenu( int mx, int my )
+{
+ CExpClass *active = expressions->GetActiveClass();
+ if ( !active )
+ return;
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ CExpression *exp = NULL;
+ if ( m_nClickedCell != -1 )
+ {
+ exp = active->GetExpression( m_nClickedCell );
+ }
+
+ pop->add( "New Expression...", IDC_CONTEXT_NEWEXP );
+ if ( exp )
+ {
+ pop->addSeparator();
+ pop->add( va( "Edit '%s'...", exp->name ), IDC_CONTEXT_EDITEXP );
+ pop->add( va( "Save '%s'", exp->name ), IDC_CONTEXT_SAVEEXP );
+
+ if ( exp->CanUndo() || exp->CanRedo() )
+ {
+ pop->add( va( "Revert '%s'", exp->name ), IDC_CONTEXT_REVERT );
+ }
+ pop->addSeparator();
+ pop->add( va( "Delete '%s'", exp->name ), IDC_CONTEXT_DELETEXP );
+ pop->addSeparator();
+ pop->add( va( "Re-create thumbnail for '%s'", exp->name ), IDC_CONTEXT_CREATEBITMAP );
+ }
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::DrawFocusRect( void )
+{
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &m_rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+static bool IsWindowOrChild( mxWindow *parent, HWND test )
+{
+ HWND parentHwnd = (HWND)parent->getHandle();
+ if ( test == parentHwnd ||
+ IsChild( parentHwnd, test ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+int mxExpressionTray::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_EXPRESSIONCLASS:
+ {
+ int index = g_pExpressionClass->getSelectedIndex();
+ if ( index >= 0 )
+ {
+ CExpClass *current = expressions->GetClass( index );
+ if ( current )
+ {
+ // Switch classname
+ expressions->ActivateExpressionClass( current );
+ current->SelectExpression( 0 );
+ }
+ }
+ }
+ break;
+ case IDC_CONTEXT_NEWEXP:
+ g_pFlexPanel->NewExpression();
+ break;
+ case IDC_CONTEXT_EDITEXP:
+ if ( m_nClickedCell != -1 )
+ {
+ g_pFlexPanel->EditExpression();
+ }
+ break;
+ case IDC_CONTEXT_REVERT:
+ if ( m_nClickedCell != -1 )
+ {
+ g_pFlexPanel->RevertExpression( m_nClickedCell );
+ }
+ break;
+ case IDC_CONTEXT_SAVEEXP:
+ if ( m_nClickedCell != -1 )
+ {
+ g_pFlexPanel->SaveExpression( m_nClickedCell );
+ }
+ break;
+ case IDC_CONTEXT_DELETEXP:
+ if ( m_nClickedCell != -1 )
+ {
+ g_pControlPanel->DeleteExpression( m_nClickedCell );
+ }
+ break;
+ case IDC_TRAYSCROLL:
+ {
+ if (event->modifiers == SB_THUMBTRACK)
+ {
+ int offset = event->height;
+
+ slScrollbar->setValue( offset );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEUP )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset -= m_nGranularity;
+ offset = max( offset, slScrollbar->getMinValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ else if ( event->modifiers == SB_PAGEDOWN )
+ {
+ int offset = slScrollbar->getValue();
+
+ offset += m_nGranularity;
+ offset = min( offset, slScrollbar->getMaxValue() );
+
+ slScrollbar->setValue( offset );
+ InvalidateRect( (HWND)slScrollbar->getHandle(), NULL, TRUE );
+
+ m_nTopOffset = offset;
+
+ redraw();
+ }
+ }
+ break;
+ case IDC_AB:
+ {
+ AB();
+ }
+ break;
+ case IDC_THUMBNAIL_INCREASE:
+ {
+ ThumbnailIncrease();
+ }
+ break;
+ case IDC_THUMBNAIL_DECREASE:
+ {
+ ThumbnailDecrease();
+ }
+ break;
+ case IDC_CONTEXT_CREATEBITMAP:
+ {
+ if ( m_nClickedCell >= 0 )
+ {
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+ CExpression *exp = active->GetExpression( m_nClickedCell );
+ if ( exp )
+ {
+ active->SelectExpression( m_nClickedCell );
+ exp->CreateNewBitmap( models->GetActiveModelIndex() );
+ redraw();
+ }
+ }
+ }
+ }
+ break;
+ }
+ break;
+ }
+ case mxEvent::MouseDown:
+ {
+ if ( !( event->buttons & mxEvent::MouseRightButton ) )
+ {
+ // Figure out cell #
+ int cell = GetCellUnderPosition( event->x, event->y );
+ CExpClass *active = expressions->GetActiveClass();
+ if ( active )
+ {
+
+ if ( cell == m_nCurCell && cell >= 0 && cell < active->GetNumExpressions() )
+ {
+ mxETButton *btn = GetItemUnderCursor( event->x, event->y );
+ if ( btn && btn->m_fnCallback )
+ {
+ (this->*(btn->m_fnCallback))( cell );
+ return iret;
+ }
+ }
+
+ if ( cell >= 0 && cell < active->GetNumExpressions() )
+ {
+ active->SelectExpression( cell, event->modifiers & mxEvent::KeyShift ? false : true );
+
+ int cx, cy, cw, ch;
+ if ( ComputeRect( cell, cx, cy, cw, ch ) )
+ {
+ m_bDragging = true;
+ m_nDragCell = cell;
+
+ m_nXStart = (short)event->x;
+ m_nYStart = (short)event->y;
+
+ m_rcFocus.left = cx;
+ m_rcFocus.top = cy;
+ m_rcFocus.right = cx + cw;
+ m_rcFocus.bottom = cy + ch - m_nDescriptionHeight;
+
+ POINT pt;
+ pt.x = pt.y = 0;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ OffsetRect( &m_rcFocus, pt.x, pt.y );
+
+ m_rcOrig = m_rcFocus;
+
+ DrawFocusRect();
+ }
+ }
+ else
+ {
+ Deselect();
+ active->DeselectExpression();
+ redraw();
+ }
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ {
+ if ( m_bDragging )
+ {
+ // Draw drag line of some kind
+ DrawFocusRect();
+
+ // update pos
+ m_rcFocus = m_rcOrig;
+ OffsetRect( &m_rcFocus, ( (short)event->x - m_nXStart ),
+ ( (short)event->y - m_nYStart ) );
+
+ DrawFocusRect();
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ iret = 1;
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ SetClickedCell( GetCellUnderPosition( (short)event->x, (short)event->y ) );
+ ShowRightClickMenu( (short)event->x, (short)event->y );
+ return iret;
+ }
+
+ int cell = GetCellUnderPosition( event->x, event->y );
+ CExpClass *active = expressions->GetActiveClass();
+
+ if ( m_bDragging )
+ {
+ DrawFocusRect();
+ m_bDragging = false;
+ // See if we let go on top of the choreo view
+
+ if ( active )
+ {
+ // Convert x, y to screen space
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ HWND maybeTool = WindowFromPoint( pt );
+
+ // Now tell choreo view
+ CExpression *exp = active->GetExpression( m_nDragCell );
+ if ( exp && maybeTool )
+ {
+ if ( IsWindowOrChild( g_pChoreoView, maybeTool ) )
+ {
+ if ( g_pChoreoView->CreateExpressionEvent( pt.x, pt.y, active, exp ) )
+ {
+ return iret;
+ }
+ }
+
+ if ( IsWindowOrChild( g_pExpressionTool, maybeTool ) )
+ {
+ if ( g_pExpressionTool->SetFlexAnimationTrackFromExpression( pt.x, pt.y, active, exp ) )
+ {
+ return iret;
+ }
+ }
+ }
+ }
+ }
+
+ if ( active )
+ {
+ // Over a new cell
+ if ( cell >= 0 &&
+ cell < active->GetNumExpressions() &&
+ cell != m_nCurCell &&
+ m_nCurCell != -1 )
+ {
+ // Swap cells
+ CExpression *exp = active->GetExpression( m_nCurCell );
+ if ( exp )
+ {
+ active->SwapExpressionOrder( m_nCurCell, cell );
+ active->SetDirty( true );
+ active->SelectExpression( cell );
+ }
+ }
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int width = w2();
+
+ int ch = GetCaptionHeight();
+
+ g_pExpressionClass->setBounds( 5, 5 + ch, width - 120, 20 );
+
+ m_pABButton->setBounds( width - 60, 4 + ch, 60, 16 );
+ m_pThumbnailIncreaseButton->setBounds( width - 60 - 40, 4 + ch, 16, 16 );
+ m_pThumbnailDecreaseButton->setBounds( width - 60 - 20, 4 + ch, 16, 16 );
+
+ m_nTopOffset = 0;
+ RepositionSlider();
+
+ redraw();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ // Figure out cell #
+ POINT pt;
+
+ pt.x = event->x;
+ pt.y = event->y;
+
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ if ( event->height < 0 )
+ {
+ m_nTopOffset = min( m_nTopOffset + 10, slScrollbar->getMaxValue() );
+ }
+ else
+ {
+ m_nTopOffset = max( m_nTopOffset - 10, 0 );
+ }
+ RepositionSlider();
+ redraw();
+ iret = 1;
+ }
+ break;
+ };
+
+ if ( iret )
+ {
+ SetActiveTool( this );
+ }
+ return iret;
+}
+
+void mxExpressionTray::ET_Undo( int cell )
+{
+ g_pControlPanel->UndoExpression( cell );
+}
+
+void mxExpressionTray::ET_Redo( int cell )
+{
+ g_pControlPanel->RedoExpression( cell );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::ThumbnailIncrease( void )
+{
+ if ( m_nSnapshotWidth + THUMBNAIL_SIZE_STEP <= MAX_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth += THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::ThumbnailDecrease( void )
+{
+ if ( m_nSnapshotWidth - THUMBNAIL_SIZE_STEP >= MIN_THUMBNAILSIZE )
+ {
+ m_nSnapshotWidth -= THUMBNAIL_SIZE_STEP;
+ g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ Con_Printf( "Thumbnail size %i x %i\n", m_nSnapshotWidth, m_nSnapshotWidth );
+
+ redraw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxExpressionTray::RestoreThumbnailSize( void )
+{
+ m_nSnapshotWidth = g_viewerSettings.thumbnailsize;
+ m_nSnapshotWidth = max( MIN_THUMBNAILSIZE, m_nSnapshotWidth );
+ m_nSnapshotWidth = min( MAX_THUMBNAILSIZE, m_nSnapshotWidth );
+
+ g_viewerSettings.thumbnailsize = m_nSnapshotWidth;
+
+ m_nSnapshotHeight = m_nSnapshotWidth + m_nDescriptionHeight;
+
+ redraw();
+}
+
+void mxExpressionTray::ReloadBitmaps( void )
+{
+ CExpClass *cl;
+ int c = expressions->GetNumClasses();
+ EnableStickySnapshotMode();
+ for ( int i = 0 ; i < c; i++ )
+ {
+ cl = expressions->GetClass( i );
+ if ( !cl )
+ continue;
+
+ cl->ReloadBitmaps();
+ }
+ DisableStickySnapshotMode();
+ redraw();
+}
+
+bool IsUsingPerPlayerExpressions()
+{
+ bool bPerPlayerExpressions = false;
+ if ( CommandLine()->CheckParm( "-perplayerexpressions" ) )
+ {
+ bPerPlayerExpressions = true;
+ }
+ else
+ {
+ // Returns the search path, each path is separated by ;s. Returns the length of the string returned
+ char pSearchPath[2048];
+ if ( g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, sizeof(pSearchPath) ) )
+ {
+ Q_FixSlashes( pSearchPath );
+ if ( Q_stristr( pSearchPath, "\\tf" ) )
+ {
+ bPerPlayerExpressions = true;
+ }
+ }
+ }
+ return bPerPlayerExpressions;
+}
+
+void mxExpressionTray::OnModelChanged()
+{
+ if ( IsUsingPerPlayerExpressions() )
+ {
+ Msg( "Closing current phoneme set\n" );
+
+ if ( !g_pControlPanel->Closeall() )
+ return;
+
+ // See if per-model overrides exist for this model
+ char fn[ MAX_PATH ];
+ Q_snprintf( fn, sizeof( fn ), "expressions/%s/phonemes/phonemes.txt", models->GetActiveModelName() );
+
+ // Load appropriate classes
+ char rootDir[ MAX_PATH ];
+ Q_snprintf( rootDir, sizeof( rootDir ), "%s/phonemes/", models->GetActiveModelName() );
+ FacePoser_SetPhonemeRootDir( rootDir );
+
+ FacePoser_EnsurePhonemesLoaded();
+ }
+
+ ReloadBitmaps();
+ RestoreThumbnailSize();
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/mxexpressiontray.h b/utils/hlfaceposer/mxexpressiontray.h
new file mode 100644
index 0000000..e59520c
--- /dev/null
+++ b/utils/hlfaceposer/mxexpressiontray.h
@@ -0,0 +1,161 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( MXEXPRESSIONTRAY_H )
+#define MXEXPRESSIONTRAY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define IDC_TRAYSCROLL 1001
+#define IDC_CONTEXT_NEWEXP 1002
+#define IDC_CONTEXT_EDITEXP 1003
+#define IDC_CONTEXT_SAVEEXP 1004
+#define IDC_CONTEXT_DELETEXP 1005
+#define IDC_CONTEXT_REVERT 1012
+#define IDC_AB 1014
+#define IDC_THUMBNAIL_INCREASE 1015
+#define IDC_THUMBNAIL_DECREASE 1016
+#define IDC_CONTEXT_CREATEBITMAP 1017
+
+#define COLOR_TRAYBACKGROUND RGB( 240, 240, 220 )
+
+class ControlPanel;
+class FlexPanel;
+class mxScrollbar;
+class mxCheckBox;
+class CChoreoView;
+class CExpression;
+class CExpClass;
+class mxButton;
+class CChoreoWidgetDrawHelper;
+
+#include "faceposertoolwindow.h"
+#include "mxbitmaptools.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class mxExpressionTray : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ mxExpressionTray( mxWindow *parent, int id = 0 );
+ virtual ~mxExpressionTray ( void );
+
+ virtual void redraw ();
+ virtual bool PaintBackground( void );
+
+ virtual int handleEvent (mxEvent *event);
+
+ void ThumbnailIncrease( void );
+ void ThumbnailDecrease( void );
+ void RestoreThumbnailSize( void );
+
+ void AB( void );
+
+ void Select( int exp, bool deselect = true );
+ void Deselect( void );
+ int CountSelected( void );
+
+ void SetCellSize( int cellsize );
+
+ void ReloadBitmaps( void );
+ virtual void OnModelChanged();
+
+private: // Data structures
+
+ typedef void (mxExpressionTray::*ETMEMBERFUNC)( int cell );
+
+ class mxETButton
+ {
+ public:
+ mxETButton *next;
+ char m_szName[ 32 ];
+ bool m_bActive;
+ RECT m_rc;
+ char m_szToolTip[ 128 ];
+ mxbitmapdata_t *m_pImage;
+
+ ETMEMBERFUNC m_fnCallback;
+ };
+
+private: // Methods
+ void ChangeWeightOfExpressionInGroup( CExpClass *active, CExpression *exp, CExpression *group );
+ int GetCellUnderPosition( int x, int y );
+
+ bool ComputeRect( int cell, int& rcx, int& rcy, int& rcw, int& rch );
+ int ComputePixelsNeeded( void );
+
+ void RepositionSlider();
+ void SetClickedCell( int cell );
+ void ShowRightClickMenu( int mx, int my );
+
+ void DrawThumbNail( CExpClass *active, CExpression *current, CChoreoWidgetDrawHelper& helper,
+ int rcx, int rcy, int rcw, int rch, int c, int selected, bool updateselection );
+
+ void DrawDirtyFlag( CChoreoWidgetDrawHelper& helper, CExpression *current, int rcx, int rcy, int rcw, int rch );
+ void DrawExpressionFocusRect( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, COLORREF clr );
+ void DrawExpressionDescription( CChoreoWidgetDrawHelper& helper, int x, int y, int w, int h, const char *expressionname, const char *description );
+
+ void CreateButtons( void );
+ void DeleteAllButtons( void );
+ void AddButton( const char *name, const char *tooltip, const char *bitmap,
+ ETMEMBERFUNC pfnCallback, bool active, int x, int y, int w, int h );
+ mxETButton *GetItemUnderCursor( int x, int y );
+ void DrawButton( CChoreoWidgetDrawHelper& helper, int cell, mxETButton *btn );
+ void ActivateButton( const char *name, bool active );
+ mxETButton *FindButton( const char *name );
+
+ void ET_Undo( int cell );
+ void ET_Redo( int cell );
+
+ void DrawFocusRect( void );
+
+private: // Data
+
+ mxETButton *m_pButtons;
+
+ mxScrollbar *slScrollbar;
+
+ int m_nTopOffset;
+
+ int m_nLastNumExpressions;
+
+ int m_nGranularity;
+
+ // For A/B
+ int m_nPrevCell;
+ int m_nCurCell;
+
+ // For context menu
+ int m_nClickedCell;
+
+ // Formatting
+ int m_nButtonSquare;
+
+ int m_nGap;
+ int m_nDescriptionHeight;
+ int m_nSnapshotWidth;
+ int m_nSnapshotHeight;
+
+ // For detecting that the slider thumbs need to be recomputed
+ int m_nPreviousExpressionCount;
+
+ bool m_bDragging;
+ RECT m_rcFocus;
+ RECT m_rcOrig;
+ int m_nDragCell;
+ int m_nXStart;
+ int m_nYStart;
+
+ mxButton *m_pABButton;
+ mxButton *m_pThumbnailIncreaseButton;
+ mxButton *m_pThumbnailDecreaseButton;
+};
+
+extern mxExpressionTray *g_pExpressionTrayTool;
+
+#endif // MXEXPRESSIONTRAY_H \ No newline at end of file
diff --git a/utils/hlfaceposer/mxstatuswindow.cpp b/utils/hlfaceposer/mxstatuswindow.cpp
new file mode 100644
index 0000000..5ce6fbb
--- /dev/null
+++ b/utils/hlfaceposer/mxstatuswindow.cpp
@@ -0,0 +1,311 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <mxtk/mx.h>
+#include "mxStatusWindow.h"
+#include "hlfaceposer.h"
+#include "choreowidgetdrawhelper.h"
+#include "MDLViewer.h"
+#include "faceposertoolwindow.h"
+
+extern double realtime;
+
+mxStatusWindow *g_pStatusWindow = NULL;
+
+#define STATUS_SCROLLBAR_SIZE 12
+#define STATUS_FONT_SIZE 9
+
+mxStatusWindow::mxStatusWindow(mxWindow *parent, int x, int y, int w, int h, const char *label /*= 0*/ )
+: mxWindow( parent, x, y, w, h, label ), IFacePoserToolWindow( "Status Window", "Output" ), m_pScrollbar(NULL)
+{
+ for ( int i = 0; i < MAX_TEXT_LINES; i++ )
+ {
+ m_rgTextLines[ i ].m_szText[ 0 ] = 0;
+ m_rgTextLines[ i ].rgb = CONSOLE_COLOR;
+ m_rgTextLines[ i ].curtime = 0;
+ }
+ m_nCurrentLine = 0;
+
+ m_pScrollbar = new mxScrollbar( this, 0, 0, STATUS_SCROLLBAR_SIZE, 100, IDC_STATUS_SCROLL, mxScrollbar::Vertical );
+ m_pScrollbar->setRange( 0, 1000 );
+ m_pScrollbar->setPagesize( 100 );
+}
+
+mxStatusWindow::~mxStatusWindow()
+{
+ g_pStatusWindow = NULL;
+}
+
+void mxStatusWindow::redraw()
+{
+// if ( !ToolCanDraw() )
+// return;
+
+ if ( !m_pScrollbar )
+ return;
+
+ CChoreoWidgetDrawHelper helper( this, RGB( 0, 0, 0 ) );
+ HandleToolRedraw( helper );
+
+ RECT rc;
+ helper.GetClientRect( rc );
+
+ RECT rcText = rc;
+
+ int lineheight = ( STATUS_FONT_SIZE + 2 );
+
+ InflateRect( &rcText, -4, 0 );
+ rcText.bottom = h2() - 4;
+ rcText.top = rcText.bottom - lineheight;
+
+ //int minval = m_pScrollbar->getMinValue();
+ int maxval = m_pScrollbar->getMaxValue();
+ int pagesize = m_pScrollbar->getPagesize();
+ int curval = m_pScrollbar->getValue();
+
+ int offset = ( maxval - pagesize ) - curval;
+ offset = ( offset + lineheight - 1 ) / lineheight;
+
+ offset = max( 0, offset );
+ //offset = 0;
+ //offset += 10;
+ //offset = max( 0, offset );
+
+ for ( int i = 0; i < MAX_TEXT_LINES - offset; i++ )
+ {
+ int rawline = m_nCurrentLine - i - 1;
+ if ( rawline <= 0 )
+ continue;
+
+ if ( rcText.bottom < 0 )
+ break;
+
+ int line = ( rawline - offset ) & TEXT_LINE_MASK;
+
+ char *ptext = m_rgTextLines[ line ].m_szText;
+
+ RECT rcTime = rcText;
+ rcTime.right = rcTime.left + 50;
+
+ char sz[ 32 ];
+ sprintf( sz, "%.3f", m_rgTextLines[ line ].curtime );
+
+ int len = helper.CalcTextWidth( "Arial", STATUS_FONT_SIZE, FW_NORMAL, sz );
+
+ rcTime.left = rcTime.right - len - 5;
+
+ helper.DrawColoredText( "Arial", STATUS_FONT_SIZE, FW_NORMAL, RGB( 255, 255, 150 ), rcTime, sz );
+
+ rcTime = rcText;
+ rcTime.left += 50;
+
+ helper.DrawColoredText( "Arial", STATUS_FONT_SIZE, FW_NORMAL, m_rgTextLines[ line ].rgb, rcTime, ptext );
+
+ OffsetRect( &rcText, 0, -lineheight );
+ }
+
+ DrawActiveTool();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool mxStatusWindow::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+void mxStatusWindow::StatusPrint( COLORREF clr, bool overwrite, const char *text )
+{
+ float curtime = (float)Plat_FloatTime();
+
+ char sz[32];
+ sprintf( sz, "%.3f ", curtime );
+
+ OutputDebugString( sz );
+ OutputDebugString( text );
+
+ char fixedtext[ 512 ];
+ char *in, *out;
+ in = (char *)text;
+ out = fixedtext;
+
+ int c = 0;
+ while ( *in && c < 511 )
+ {
+ if ( *in == '\n' || *in == '\r' )
+ {
+ in++;
+ }
+ else
+ {
+ *out++ = *in++;
+ c++;
+ }
+ }
+ *out = 0;
+
+ if ( overwrite )
+ {
+ m_nCurrentLine--;
+ }
+
+ int i = m_nCurrentLine & TEXT_LINE_MASK;
+
+ strncpy( m_rgTextLines[ i ].m_szText, fixedtext, 511 );
+ m_rgTextLines[ i ].m_szText[ 511 ] = 0;
+
+ m_rgTextLines[ i ].rgb = clr;
+ m_rgTextLines[ i ].curtime = curtime;
+
+ m_nCurrentLine++;
+
+ if ( m_nCurrentLine <= MAX_TEXT_LINES )
+ {
+ PositionSliders( 0 );
+ }
+ m_pScrollbar->setValue( m_pScrollbar->getMaxValue() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : sboffset -
+//-----------------------------------------------------------------------------
+void mxStatusWindow::PositionSliders( int sboffset )
+{
+ int lineheight = ( STATUS_FONT_SIZE + 2 );
+
+ int linesused = min( (int)MAX_TEXT_LINES, m_nCurrentLine );
+ linesused = max( linesused, 1 );
+
+ int trueh = h2() - GetCaptionHeight();
+
+ int vpixelsneeded = max( linesused * lineheight, trueh );
+ m_pScrollbar->setVisible( linesused * lineheight > trueh );
+
+
+ m_pScrollbar->setPagesize( trueh );
+ m_pScrollbar->setRange( 0, vpixelsneeded );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int mxStatusWindow::handleEvent( mxEvent *event )
+{
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Size:
+ {
+ m_pScrollbar->setBounds( w2() - STATUS_SCROLLBAR_SIZE, GetCaptionHeight(), STATUS_SCROLLBAR_SIZE, h2()-GetCaptionHeight() );
+ PositionSliders( 0 );
+ m_pScrollbar->setValue( m_pScrollbar->getMaxValue() );
+ iret = 1;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ iret = 0;
+ break;
+ case IDC_STATUS_SCROLL:
+ {
+ if ( event->event == mxEvent::Action &&
+ event->modifiers == SB_THUMBTRACK)
+ {
+ int offset = event->height;
+ m_pScrollbar->setValue( offset );
+ PositionSliders( offset );
+ DrawActiveTool();
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ return iret;
+}
+#include "StudioModel.h"
+
+#include "faceposer_models.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void mxStatusWindow::DrawActiveTool()
+{
+ RECT rcTool;
+ rcTool.left = 0;
+ rcTool.top = GetCaptionHeight() + 2;
+ rcTool.bottom = h2();
+ rcTool.right = w2() - 16;
+
+ rcTool.bottom = rcTool.top + 10;
+ rcTool.left = rcTool.right - 500;
+
+ char sz[ 256 ];
+
+ IFacePoserToolWindow *activeTool = IFacePoserToolWindow::GetActiveTool();
+
+ static float lastrealtime = 0.0f;
+
+ float dt = (float)realtime - lastrealtime;
+ dt = clamp( dt, 0.0f, 1.0f );
+
+ float fps = 0.0f;
+ if ( dt > 0.0001f )
+ {
+ fps = 1.0f / dt;
+ }
+
+ sprintf( sz, "%s (%i) at %.3f (%.2f fps) (soundcount %i)",
+ activeTool ? activeTool->GetToolName() : "None",
+ g_MDLViewer->GetCurrentFrame(),
+ (float)realtime,
+ fps,
+ models->CountActiveSources() );
+
+ lastrealtime = realtime;
+
+ int len = CChoreoWidgetDrawHelper::CalcTextWidth( "Courier New", 10, FW_NORMAL, sz );
+
+ CChoreoWidgetDrawHelper helper( this, rcTool, RGB( 32, 0, 0 ) );
+
+ rcTool.left = rcTool.right - len - 15;
+
+ helper.DrawColoredText( "Courier New", 10, FW_NORMAL, RGB( 255, 255, 200 ), rcTool, sz );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void mxStatusWindow::Think( float dt )
+{
+ DrawActiveTool();
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/mxstatuswindow.h b/utils/hlfaceposer/mxstatuswindow.h
new file mode 100644
index 0000000..aca0686
--- /dev/null
+++ b/utils/hlfaceposer/mxstatuswindow.h
@@ -0,0 +1,61 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef MXSTATUSWINDOW_H
+#define MXSTATUSWINDOW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "faceposertoolwindow.h"
+
+class mxScrollbar;
+
+#define IDC_STATUS_SCROLL 1000
+
+class mxStatusWindow : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ mxStatusWindow (mxWindow *parent, int x, int y, int w, int h, const char *label = 0 );
+ ~mxStatusWindow();
+
+ void StatusPrint( COLORREF clr, bool overwrite, const char *text );
+
+ virtual void DrawActiveTool();
+
+ virtual void redraw();
+ virtual bool PaintBackground( void );
+
+ virtual int handleEvent( mxEvent *event );
+
+ virtual void Think( float dt );
+
+private:
+
+ void PositionSliders( int sboffset );
+
+ enum
+ {
+ MAX_TEXT_LINES = 1024,
+ TEXT_LINE_MASK = MAX_TEXT_LINES - 1,
+ };
+
+ struct TextLine
+ {
+ char m_szText[ 512 ];
+ COLORREF rgb;
+ float curtime;
+ };
+
+ TextLine m_rgTextLines[ MAX_TEXT_LINES ];
+ int m_nCurrentLine;
+
+ mxScrollbar *m_pScrollbar;
+};
+
+extern mxStatusWindow *g_pStatusWindow;
+
+#endif // MXSTATUSWINDOW_H
diff --git a/utils/hlfaceposer/phonemeeditor.cpp b/utils/hlfaceposer/phonemeeditor.cpp
new file mode 100644
index 0000000..e33f6c1
--- /dev/null
+++ b/utils/hlfaceposer/phonemeeditor.cpp
@@ -0,0 +1,8799 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include <Assert.h>
+#include <stdio.h>
+#include <math.h>
+#include "hlfaceposer.h"
+#include "PhonemeEditor.h"
+#include "PhonemeEditorColors.h"
+#include "snd_audio_source.h"
+#include "snd_wave_source.h"
+#include "ifaceposersound.h"
+#include "choreowidgetdrawhelper.h"
+#include "mxBitmapButton.h"
+#include "phonemeproperties.h"
+#include "tier2/riff.h"
+#include "StudioModel.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "InputProperties.h"
+#include "phonemeextractor/PhonemeExtractor.h"
+#include "PhonemeConverter.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "ChoreoView.h"
+#include "filesystem.h"
+#include "UtlBuffer.h"
+#include "AudioWaveOutput.h"
+#include "StudioModel.h"
+#include "viewerSettings.h"
+#include "ControlPanel.h"
+#include "faceposer_models.h"
+#include "tier1/strtools.h"
+#include "tabwindow.h"
+#include "MatSysWin.h"
+#include "soundflags.h"
+#include "mdlviewer.h"
+#include "filesystem_init.h"
+#include "WaveBrowser.h"
+#include "tier2/p4helpers.h"
+#include "vstdlib/random.h"
+
+extern IUniformRandomStream *random;
+
+float SnapTime( float input, float granularity );
+
+#define MODE_TAB_OFFSET 20
+
+// 10x magnification
+#define MAX_TIME_ZOOM 1000
+// 10% per step
+#define TIME_ZOOM_STEP 2
+
+#define SCRUBBER_HEIGHT 15
+
+#define TAG_TOP ( 25 + SCRUBBER_HEIGHT )
+#define TAG_BOTTOM ( TAG_TOP + 20 )
+
+#define PLENTY_OF_TIME 99999.9
+#define MINIMUM_WORD_GAP 0.02f
+#define MINIMUM_PHONEME_GAP 0.01f
+#define DEFAULT_WORD_LENGTH 0.25f
+#define DEFAULT_PHONEME_LENGTH 0.1f
+
+#define WORD_DATA_EXTENSION ".txt"
+
+// #define ITEM_GAP_EPSILON 0.0025f
+struct PhonemeEditorColor
+{
+ int color_number; // For readability
+ int mode_number; // -1 for all
+ COLORREF root_color;
+ COLORREF gray_color; // if mode is wrong...
+};
+
+static PhonemeEditorColor g_PEColors[ NUM_COLORS ] =
+{
+ { COLOR_PHONEME_BACKGROUND, -1, RGB( 240, 240, 220 ) },
+ { COLOR_PHONEME_TEXT, -1, RGB( 63, 63, 63 ) },
+ { COLOR_PHONEME_LIGHTTEXT, 0, RGB( 180, 180, 120 ) },
+ { COLOR_PHONEME_PLAYBACKTICK, 0, RGB( 255, 0, 0 ) },
+ { COLOR_PHONEME_WAVDATA, 0, RGB( 128, 31, 63 ) },
+ { COLOR_PHONEME_TIMELINE, 0, RGB( 31, 31, 127 ) },
+ { COLOR_PHONEME_TIMELINE_MAJORTICK, 0, RGB( 200, 200, 255 ) },
+ { COLOR_PHONEME_TIMELINE_MINORTICK, 0, RGB( 210, 210, 240 ) },
+ { COLOR_PHONEME_EXTRACTION_RESULT_FAIL, 0, RGB( 180, 180, 0 ) },
+ { COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS, 0, RGB( 100, 180, 100 ) },
+ { COLOR_PHONEME_EXTRACTION_RESULT_ERROR, 0, RGB( 255, 31, 31 ) },
+ { COLOR_PHONEME_EXTRACTION_RESULT_OTHER, 0, RGB( 63, 63, 63 ) },
+ { COLOR_PHONEME_TAG_BORDER, 0, RGB( 160, 100, 100 ) },
+ { COLOR_PHONEME_TAG_BORDER_SELECTED, 0, RGB( 255, 40, 60 ) },
+ { COLOR_PHONEME_TAG_FILLER_NORMAL, 0, RGB( 210, 210, 190 ) },
+ { COLOR_PHONEME_TAG_SELECTED, 0, RGB( 200, 130, 130 ) },
+ { COLOR_PHONEME_TAG_TEXT, 0, RGB( 63, 63, 63 ) },
+ { COLOR_PHONEME_TAG_TEXT_SELECTED, 0, RGB( 250, 250, 250 ) },
+ { COLOR_PHONEME_WAV_ENDPOINT, 0, RGB( 0, 0, 200 ) },
+ { COLOR_PHONEME_AB, 0, RGB( 63, 190, 210 ) },
+ { COLOR_PHONEME_AB_LINE, 0, RGB( 31, 150, 180 ) },
+ { COLOR_PHONEME_AB_TEXT, 0, RGB( 100, 120, 120 ) },
+ { COLOR_PHONEME_ACTIVE_BORDER, 0, RGB( 150, 240, 180 ) },
+ { COLOR_PHONEME_SELECTED_BORDER, 0, RGB( 255, 0, 0 ) },
+ { COLOR_PHONEME_TIMING_TAG, -1, RGB( 0, 100, 200 ) },
+
+ { COLOR_PHONEME_EMPHASIS_BG, 1, RGB( 230, 230, 200 ) },
+ { COLOR_PHONEME_EMPHASIS_BG_STRONG, 1, RGB( 163, 201, 239 ) },
+ { COLOR_PHONEME_EMPHASIS_BG_WEAK, 1, RGB( 237, 239, 163 ) },
+ { COLOR_PHONEME_EMPHASIS_BORDER, 1, RGB( 200, 200, 200 ) },
+ { COLOR_PHONEME_EMPHASIS_LINECOLOR, 1, RGB( 0, 0, 255 ) },
+ { COLOR_PHONEME_EMPHASIS_DOTCOLOR, 1, RGB( 0, 0, 255 ) },
+ { COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED, 1, RGB( 240, 80, 20 ) },
+ { COLOR_PHONEME_EMPHASIS_TEXT, 1, RGB( 0, 0, 0 ) },
+ { COLOR_PHONEME_EMPHASIS_MIDLINE, 1, RGB( 100, 150, 200 ) },
+};
+
+struct Extractor
+{
+ PE_APITYPE apitype;
+ CSysModule *module;
+ IPhonemeExtractor *extractor;
+};
+
+CUtlVector< Extractor > g_Extractors;
+
+
+bool DoesExtractorExistFor( PE_APITYPE type )
+{
+ for ( int i=0; i < g_Extractors.Count(); i++ )
+ {
+ if ( g_Extractors[i].apitype == type )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the RIFF i/o interface on stdio
+//-----------------------------------------------------------------------------
+class StdIOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName )
+ {
+ return (int)filesystem->Open( pFileName, "rb" );
+ }
+
+ int read( void *pOutput, int size, int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Read( pOutput, size, (FileHandle_t)file );
+ }
+
+ void seek( int file, int pos )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+ }
+
+ unsigned int tell( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Tell( (FileHandle_t)file );
+ }
+
+ unsigned int size( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Size( (FileHandle_t)file );
+ }
+
+ void close( int file )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Close( (FileHandle_t)file );
+ }
+};
+
+class StdIOWriteBinary : public IFileWriteBinary
+{
+public:
+ int create( const char *pFileName )
+ {
+ MakeFileWriteable( pFileName );
+ return (int)filesystem->Open( pFileName, "wb" );
+ }
+
+ int write( void *pData, int size, int file )
+ {
+ return filesystem->Write( pData, size, (FileHandle_t)file );
+ }
+
+ void close( int file )
+ {
+ filesystem->Close( (FileHandle_t)file );
+ }
+
+ void seek( int file, int pos )
+ {
+ filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+ }
+
+ unsigned int tell( int file )
+ {
+ return filesystem->Tell( (FileHandle_t)file );
+ }
+};
+
+// Interface objects
+static StdIOWriteBinary io_out;
+static StdIOReadBinary io_in;
+
+class CPhonemeModeTab : public CTabWindow
+{
+public:
+ typedef CTabWindow BaseClass;
+
+ CPhonemeModeTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
+ CTabWindow( parent, x, y, w, h, id, style )
+ {
+ SetInverted( true );
+ }
+
+ virtual void ShowRightClickMenu( int mx, int my )
+ {
+ // Nothing
+ }
+
+ void Init( void )
+ {
+ add( "Phonemes" );
+ add( "Emphasis" );
+ select( 0 );
+ }
+};
+
+PhonemeEditor * g_pPhonemeEditor = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+PhonemeEditor::PhonemeEditor( mxWindow *parent ) :
+ IFacePoserToolWindow( "PhonemeEditor", "Phoneme Editor" ),
+ mxWindow( parent, 0, 0, 0, 0 )
+{
+ SetAutoProcess( false );
+
+ m_flPlaybackRate = 1.0f;
+
+ m_flScrub = 0.0f;
+ m_flScrubTarget = 0.0f;
+
+ m_CurrentMode = MODE_PHONEMES;
+ Emphasis_Init();
+ SetupPhonemeEditorColors();
+
+ m_bRedoPending = false;
+ m_nUndoLevel = 0;
+
+ m_bWordsActive = false;
+
+ m_pWaveFile = NULL;
+ m_pMixer = NULL;
+ m_pEvent = NULL;
+ m_nClickX = 0;
+
+ m_WorkFile.m_bDirty = false;
+ m_WorkFile.m_szWaveFile[ 0 ] = 0;
+ m_WorkFile.m_szWorkingFile[ 0 ] = 0;
+ m_WorkFile.m_szBasePath[ 0 ] = 0;
+
+ m_nTickHeight = 20;
+
+ m_flPixelsPerSecond = 500.0f;
+ m_nTimeZoom = 100;
+ m_nTimeZoomStep = TIME_ZOOM_STEP;
+
+ m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_PHONEME_SCROLL, mxScrollbar::Horizontal );
+
+
+ m_hPrevCursor = 0;
+ m_nStartX = 0;
+ m_nStartY = 0;
+ m_nLastX = 0;
+ m_nLastY = 0;
+ m_nDragType = DRAGTYPE_NONE;
+
+ SetClickedPhoneme( -1, -1 );
+
+ m_nSelection[ 0 ] = m_nSelection[ 1 ] = 0;
+ m_bSelectionActive = false;
+
+ m_nSelectedPhonemeCount = 0;
+ m_nSelectedWordCount = 0;
+
+ m_btnSave = new mxButton( this, 0, 0, 16, 16, "Save (Ctrl+S)", IDC_SAVE_LINGUISTIC );
+ m_btnRedoPhonemeExtraction = new mxButton( this, 38, 14, 80, 16, "Re-extract (Ctrl+R)", IDC_REDO_PHONEMEEXTRACTION );
+
+ m_btnLoad = new mxButton( this, 0, 0, 0, 0, "Load (Ctrl+O)", IDC_LOADWAVEFILE );
+ m_btnPlay = new mxButton( this, 0, 0, 16, 16, "Play (Spacebar)", IDC_PLAYBUTTON );
+
+ m_pPlaybackRate = new mxSlider( this, 0, 0, 16, 16, IDC_PLAYBACKRATE );
+ m_pPlaybackRate->setRange( 0.0, 2.0, 40 );
+ m_pPlaybackRate->setValue( m_flPlaybackRate );
+
+ m_pModeTab = new CPhonemeModeTab( this, 0, 0, 500, 20, IDC_MODE_TAB );
+ m_pModeTab->Init();
+
+ m_nLastExtractionResult = SR_RESULT_NORESULT;
+
+ ClearDragLimit();
+
+ SetSuffix( " - Normal" );
+ m_flScrubberTimeOffset = 0.0f;
+
+ LoadPhonemeConverters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::OnDelete()
+{
+ if ( m_pWaveFile )
+ {
+ char fn[ 512 ];
+ Q_snprintf( fn, sizeof( fn ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
+ filesystem->RemoveFile( fn, "GAME" );
+ }
+
+ delete m_pWaveFile;
+ m_pWaveFile = NULL;
+
+ m_Tags.Reset();
+ m_TagsExt.Reset();
+
+ UnloadPhonemeConverters();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::CanClose()
+{
+ if ( !GetDirty() )
+ return true;
+
+ int retval = mxMessageBox( this, va( "Save current changes to %s", m_WorkFile.m_szWaveFile ),
+ "Phoneme Editor", MX_MB_QUESTION | MX_MB_YESNOCANCEL );
+
+ // Cancel
+ if ( retval == 2 )
+ {
+ return false;
+ }
+
+ // Yes
+ if ( retval == 0 )
+ {
+ CommitChanges();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+PhonemeEditor::~PhonemeEditor( void )
+{
+}
+
+void PhonemeEditor::SetupPhonemeEditorColors( void )
+{
+ int i;
+ for ( i = 0; i < NUM_COLORS; i++ )
+ {
+ PhonemeEditorColor *p = &g_PEColors[ i ];
+ Assert( p->color_number == i );
+
+ if ( p->mode_number == -1 )
+ {
+ p->gray_color = p->root_color;
+ }
+ else
+ {
+ COLORREF bgColor = g_PEColors[ COLOR_PHONEME_BACKGROUND ].root_color;
+
+ int bgr, bgg, bgb;
+
+ bgr = GetRValue( bgColor );
+ bgg = GetGValue( bgColor );
+ bgb = GetBValue( bgColor );
+
+ int r, g, b;
+
+ r = GetRValue( p->root_color );
+ g = GetGValue( p->root_color );
+ b = GetBValue( p->root_color );
+
+ int avg = ( r + g + b ) / 3;
+ int bgavg = ( bgr + bgg + bgb ) / 3;
+
+ // Bias toward bg color
+ avg += ( bgavg - avg ) / 2.5;
+
+ p->gray_color = RGB( avg, avg, avg );
+ }
+ }
+}
+
+COLORREF PhonemeEditor::PEColor( int colornum )
+{
+ COLORREF clr = RGB( 0, 0, 0 );
+ if ( colornum < 0 || colornum >= NUM_COLORS )
+ {
+ Assert( 0 );
+ return clr;
+ }
+
+ PhonemeEditorColor *p = &g_PEColors[ colornum ];
+
+ if ( p->mode_number == -1 )
+ {
+ return p->root_color;
+ }
+
+ int modenum = (int)GetMode();
+
+ if ( p->mode_number == modenum )
+ {
+ return p->root_color;
+ }
+
+ return p->gray_color;
+}
+
+void PhonemeEditor::EditWord( CWordTag *pWord, bool positionDialog /*= false*/ )
+{
+ if ( !pWord )
+ {
+ Con_Printf( "PhonemeEditor::EditWord: pWord == NULL\n" );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Edit Word" );
+ strcpy( params.m_szPrompt, "Current Word:" );
+ V_strcpy_safe( params.m_szInputText, pWord->GetWord() );
+
+ params.m_nLeft = -1;
+ params.m_nTop = -1;
+
+ params.m_bPositionDialog = positionDialog;
+ if ( params.m_bPositionDialog )
+ {
+ RECT rcWord;
+ GetWordRect( pWord, rcWord );
+
+ // Convert to screen coords
+ POINT pt;
+ pt.x = rcWord.left;
+ pt.y = rcWord.top;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ params.m_nLeft = pt.x;
+ params.m_nTop = pt.y;
+ }
+
+ int iret = InputProperties( &params );
+ SetFocus( (HWND)getHandle() );
+ if ( !iret )
+ {
+ return;
+ }
+
+ // Validate parameters
+ if ( CSentence::CountWords( params.m_szInputText ) != 1 )
+ {
+ Con_ErrorPrintf( "Edit word: %s has multiple words in it!!!\n", params.m_szInputText );
+ return;
+ }
+
+ SetFocus( (HWND)getHandle() );
+
+ SetDirty( true );
+
+ PushUndo();
+
+ // Set the word and clear out the phonemes
+ // ->m_nPhonemeCode = TextToPhoneme( params.m_szName );
+ pWord->SetWord( params.m_szInputText );
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPhoneme -
+// positionDialog -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditPhoneme( CPhonemeTag *pPhoneme, bool positionDialog /*= false*/ )
+{
+ if ( !pPhoneme )
+ {
+ Con_Printf( "PhonemeEditor::EditPhoneme: pPhoneme == NULL\n" );
+ return;
+ }
+
+ CPhonemeParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
+ V_strcpy_safe( params.m_szName, ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
+
+ params.m_nLeft = -1;
+ params.m_nTop = -1;
+
+ params.m_bPositionDialog = positionDialog;
+ if ( params.m_bPositionDialog )
+ {
+ RECT rcPhoneme;
+ GetPhonemeRect( pPhoneme, rcPhoneme );
+
+ // Convert to screen coords
+ POINT pt;
+ pt.x = rcPhoneme.left;
+ pt.y = rcPhoneme.top;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ params.m_nLeft = pt.x;
+ params.m_nTop = pt.y;
+ }
+
+ int iret = PhonemeProperties( &params );
+ SetFocus( (HWND)getHandle() );
+
+ if ( !iret )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ pPhoneme->SetPhonemeCode( TextToPhoneme( params.m_szName ) );
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditPhoneme( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CPhonemeTag *pPhoneme = GetClickedPhoneme();
+ if ( !pPhoneme )
+ return;
+
+ EditPhoneme( pPhoneme, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditWord( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CWordTag *pWord = GetClickedWord();
+ if ( !pWord )
+ return;
+
+ EditWord( pWord, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dragtype -
+// startx -
+// cursor -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
+{
+ m_nDragType = dragtype;
+ m_nStartX = startx;
+ m_nLastX = startx;
+ m_nStartY = starty;
+ m_nLastY = starty;
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+ m_hPrevCursor = SetCursor( cursor );
+
+ m_FocusRects.Purge();
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ RECT rcStart;
+ rcStart.left = startx;
+ rcStart.right = startx;
+
+ bool addrect = true;
+ switch ( dragtype )
+ {
+ default:
+ case DRAGTYPE_SCRUBBER:
+ {
+ RECT rcScrub;
+ GetScrubHandleRect( rcScrub, true );
+
+ rcStart = rcScrub;
+ rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
+ rcStart.right = rcStart.left;
+ rcStart.bottom = h2() - 18 - MODE_TAB_OFFSET;
+ }
+ break;
+ case DRAGTYPE_EMPHASIS_SELECT:
+ {
+ RECT rcEmphasis;
+ Emphasis_GetRect( rc, rcEmphasis );
+
+ rcStart.top = starty;
+ rcStart.bottom = starty;
+ }
+ break;
+ case DRAGTYPE_EMPHASIS_MOVE:
+ {
+ SetDirty( true );
+
+ PushUndo();
+
+ Emphasis_MouseDrag( startx, starty );
+ m_Tags.Resort();
+
+ addrect = false;
+ }
+ break;
+ case DRAGTYPE_SELECTSAMPLES:
+ case DRAGTYPE_MOVESELECTIONSTART:
+ case DRAGTYPE_MOVESELECTIONEND:
+ rcStart.top = rc.top;
+ rcStart.bottom = rc.bottom;
+ break;
+ case DRAGTYPE_MOVESELECTION:
+ {
+ rcStart.top = rc.top;
+ rcStart.bottom = rc.bottom;
+
+ // Compute left/right pixels for selection
+ rcStart.left = GetPixelForSample( m_nSelection[ 0 ] );
+ rcStart.right = GetPixelForSample( m_nSelection[ 1 ] );
+ }
+ break;
+ case DRAGTYPE_PHONEME:
+ {
+ GetPhonemeTrayTopBottom( rcStart );
+ m_bWordsActive = false;
+ }
+ break;
+ case DRAGTYPE_WORD:
+ {
+ GetWordTrayTopBottom( rcStart );
+ m_bWordsActive = true;
+ }
+ break;
+ case DRAGTYPE_MOVEWORD:
+ {
+ TraverseWords( &PhonemeEditor::ITER_AddFocusRectSelectedWords, 0.0f );
+ addrect = false;
+ m_bWordsActive = true;
+ }
+ break;
+ case DRAGTYPE_MOVEPHONEME:
+ {
+ TraversePhonemes( &PhonemeEditor::ITER_AddFocusRectSelectedPhonemes, 0.0f );
+ addrect = false;
+ m_bWordsActive = false;
+ }
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ {
+ rcStart.top = TAG_TOP;
+ rcStart.bottom = TAG_BOTTOM;
+ rcStart.left -= 10;
+ rcStart.right += 10;
+ }
+ break;
+ }
+
+ if ( addrect )
+ {
+ AddFocusRect( rcStart );
+ }
+
+ DrawFocusRect( "start" );
+
+ SetDragLimit( m_nDragType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int PhonemeEditor::handleEvent( mxEvent *event )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ case IDC_EXPORT_SENTENCE:
+ {
+ OnExport();
+ }
+ break;
+ case IDC_IMPORT_SENTENCE:
+ {
+ OnImport();
+ }
+ break;
+ case IDC_PLAYBACKRATE:
+ {
+ m_flPlaybackRate = m_pPlaybackRate->getValue();
+ redraw();
+ }
+ break;
+ case IDC_MODE_TAB:
+ {
+ // The mode changed, so reset stuff here
+ EditorMode newMode = (EditorMode)m_pModeTab->getSelectedIndex();
+ bool needpaint = ( m_CurrentMode != newMode );
+ m_CurrentMode = newMode;
+ if ( needpaint )
+ {
+ switch ( GetMode() )
+ {
+ default:
+ case MODE_PHONEMES:
+ SetSuffix( " - Normal" );
+ break;
+ case MODE_EMPHASIS:
+ SetSuffix( " - Emphasis Track" );
+ break;
+ }
+
+ OnModeChanged();
+ redraw();
+ }
+ }
+ break;
+ case IDC_EMPHASIS_DELETE:
+ Emphasis_Delete();
+ break;
+ case IDC_EMPHASIS_DESELECT:
+ Emphasis_DeselectAll();
+ break;
+ case IDC_EMPHASIS_SELECTALL:
+ Emphasis_SelectAll();
+ break;
+ case IDC_API_SAPI:
+ OnSAPI();
+ break;
+ case IDC_API_LIPSINC:
+ OnLipSinc();
+ break;
+ case IDC_PLAYBUTTON:
+ Play();
+ break;
+ case IDC_UNDO:
+ Undo();
+ break;
+ case IDC_REDO:
+ Redo();
+ break;
+ case IDC_CLEARUNDO:
+ ClearUndo();
+ break;
+ case IDC_ADDTAG:
+ AddTag();
+ break;
+ case IDC_DELETETAG:
+ DeleteTag();
+ break;
+ case IDC_COMMITEXTRACTED:
+ CommitExtracted();
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_CLEAREXTRACTED:
+ ClearExtracted();
+ break;
+ case IDC_SEPARATEPHONEMES:
+ SeparatePhonemes();
+ break;
+ case IDC_SNAPPHONEMES:
+ SnapPhonemes();
+ break;
+ case IDC_SEPARATEWORDS:
+ SeparateWords();
+ break;
+ case IDC_SNAPWORDS:
+ SnapWords();
+ break;
+ case IDC_EDITWORDLIST:
+ EditWordList();
+ break;
+ case IDC_EDIT_PHONEME:
+ EditPhoneme();
+ break;
+ case IDC_EDIT_WORD:
+ EditWord();
+ break;
+ case IDC_EDIT_INSERTPHONEMEBEFORE:
+ EditInsertPhonemeBefore();
+ break;
+ case IDC_EDIT_INSERTPHONEMEAFTER:
+ EditInsertPhonemeAfter();
+ break;
+ case IDC_EDIT_INSERTWORDBEFORE:
+ EditInsertWordBefore();
+ break;
+ case IDC_EDIT_INSERTWORDAFTER:
+ EditInsertWordAfter();
+ break;
+ case IDC_EDIT_DELETEPHONEME:
+ EditDeletePhoneme();
+ break;
+ case IDC_EDIT_DELETEWORD:
+ EditDeleteWord();
+ break;
+ case IDC_EDIT_INSERTFIRSTPHONEMEOFWORD:
+ EditInsertFirstPhonemeOfWord();
+ break;
+ case IDC_PHONEME_PLAY_ORIG:
+ {
+ StopPlayback();
+ if ( m_pWaveFile )
+ {
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ sound->PlaySound( m_pWaveFile, VOL_NORM, &m_pMixer );
+ }
+ }
+ break;
+ case IDC_PHONEME_SCROLL:
+ if (event->modifiers == SB_THUMBTRACK)
+ {
+ MoveTimeSliderToPos( event->height );
+ }
+ else if ( event->modifiers == SB_PAGEUP )
+ {
+ int offset = m_pHorzScrollBar->getValue();
+
+ offset -= 10;
+ offset = max( offset, m_pHorzScrollBar->getMinValue() );
+
+ MoveTimeSliderToPos( offset );
+ }
+ else if ( event->modifiers == SB_PAGEDOWN )
+ {
+ int offset = m_pHorzScrollBar->getValue();
+
+ offset += 10;
+ offset = min( offset, m_pHorzScrollBar->getMaxValue() );
+
+ MoveTimeSliderToPos( offset );
+ }
+ break;
+ case IDC_REDO_PHONEMEEXTRACTION:
+ if ( m_Tags.m_Words.Size() <= 0 )
+ {
+ // This calls redo LISET if some words are actually entered
+ EditWordList();
+ }
+ else
+ {
+ RedoPhonemeExtraction();
+ }
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_REDO_PHONEMEEXTRACTION_SELECTION:
+ {
+ RedoPhonemeExtractionSelected();
+ }
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_DESELECT:
+ Deselect();
+ redraw();
+ break;
+ case IDC_PLAY_EDITED:
+ PlayEditedWave( false );
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_PLAY_EDITED_SELECTION:
+ PlayEditedWave( true );
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_SAVE_LINGUISTIC:
+ CommitChanges();
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_LOADWAVEFILE:
+ LoadWaveFile();
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_CANCELPLAYBACK:
+ StopPlayback();
+ SetFocus( (HWND)getHandle() );
+ break;
+ case IDC_SELECT_WORDSRIGHT:
+ SelectWords( true );
+ break;
+ case IDC_SELECT_WORDSLEFT:
+ SelectWords( false );
+ break;
+ case IDC_SELECT_PHONEMESRIGHT:
+ SelectPhonemes( true );
+ break;
+ case IDC_SELECT_PHONEMESLEFT:
+ SelectPhonemes( false );
+ break;
+ case IDC_DESELECT_PHONEMESANDWORDS:
+ DeselectPhonemes();
+ DeselectWords();
+ redraw();
+ break;
+ case IDC_CLEANUP:
+ CleanupWordsAndPhonemes( true );
+ redraw();
+ break;
+ case IDC_REALIGNPHONEMES:
+ RealignPhonemesToWords( true );
+ redraw();
+ break;
+ case IDC_REALIGNWORDS:
+ RealignWordsToPhonemes( true );
+ redraw();
+ break;
+ case IDC_TOGGLE_VOICEDUCK:
+ OnToggleVoiceDuck();
+ break;
+ }
+
+ if ( iret == 1 )
+ {
+ SetActiveTool( this );
+ SetFocus( (HWND)getHandle() );
+ }
+ }
+ break;
+ case mxEvent::MouseWheeled:
+ {
+ // Zoom time in / out
+ if ( event->height > 0 )
+ {
+ m_nTimeZoom = min( m_nTimeZoom + m_nTimeZoomStep, MAX_TIME_ZOOM );
+ }
+ else
+ {
+ m_nTimeZoom = max( m_nTimeZoom - m_nTimeZoomStep, m_nTimeZoomStep );
+ }
+ RepositionHSlider();
+ iret = 1;
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int bw = 100;
+ int x = 5;
+ int by = h2() - 18 - MODE_TAB_OFFSET;
+
+ m_pModeTab->setBounds( 0, h2() - MODE_TAB_OFFSET, w2(), MODE_TAB_OFFSET );
+
+ m_btnRedoPhonemeExtraction->setBounds( x, by, bw, 16 );
+ x += bw;
+ m_btnSave->setBounds( x, by, bw, 16 );
+ x += bw;
+ m_btnLoad->setBounds( x, by, bw, 16 );
+ x += bw;
+ m_btnPlay->setBounds( x, by, bw, 16 );
+ x += bw;
+
+ m_pPlaybackRate->setBounds( x, by, 100, 16 );
+
+ RepositionHSlider();
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ iret = 1;
+
+ CPhonemeTag *pt;
+ CWordTag *wt;
+
+ pt = GetPhonemeTagUnderMouse( (short)event->x, (short)event->y );
+ wt = GetWordTagUnderMouse( (short)event->x, (short)event->y );
+
+ bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+ bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( IsMouseOverWordRow( (short)event->y ) )
+ {
+ ShowWordMenu( wt, (short)event->x, (short)event->y );
+ }
+ else if ( IsMouseOverPhonemeRow( (short)event->y ) )
+ {
+ ShowPhonemeMenu( pt, (short)event->x, (short)event->y );
+ }
+ else if ( IsMouseOverTagRow( (short)event->y ) )
+ {
+ ShowTagMenu( (short)event->x, (short)event->y );
+ }
+ else if ( IsMouseOverScrubArea( event ) )
+ {
+ float t = GetTimeForPixel( (short)event->x );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ redraw();
+ }
+ else
+ {
+ ShowContextMenu( (short)event->x, (short)event->y );
+ }
+ return iret;
+ }
+
+ if ( m_nDragType == DRAGTYPE_NONE )
+ {
+ CountSelected();
+
+ int type = IsMouseOverBoundary( event );
+
+ if ( IsMouseOverScrubArea( event ) )
+ {
+ if ( IsMouseOverScrubHandle( event ) )
+ {
+ StartDragging( DRAGTYPE_SCRUBBER,
+ (short)event->x,
+ (short)event->y,
+ LoadCursor( NULL, IDC_SIZEWE ) );
+
+ float t = GetTimeForPixel( (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 );
+
+ DrawScrubHandle();
+ iret = true;
+ }
+ else
+ {
+ float t = GetTimeForPixel( (short)event->x );
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTargetTime( t );
+
+ iret = true;
+
+ }
+ return iret;
+ }
+ else if ( GetMode() == MODE_EMPHASIS )
+ {
+ CEmphasisSample *sample = Emphasis_GetSampleUnderMouse( event );
+ if ( sample )
+ {
+ if ( shiftdown )
+ {
+ sample->selected = !sample->selected;
+ redraw();
+ }
+ else if ( sample->selected )
+ {
+ StartDragging( DRAGTYPE_EMPHASIS_MOVE, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ Emphasis_DeselectAll();
+ redraw();
+ }
+
+ StartDragging( DRAGTYPE_EMPHASIS_SELECT, (short)event->x, (short)event->y, NULL );
+ }
+ return true;
+ }
+ else if ( ctrldown )
+ {
+ // Add a sample point
+ float t = GetTimeForPixel( (short)event->x );
+
+ RECT rcWork;
+ GetWorkspaceRect( rcWork );
+ RECT rcEmphasis;
+ Emphasis_GetRect( rcWork, rcEmphasis );
+
+ int eh = rcEmphasis.bottom - rcEmphasis.top;
+ int dy = (short)event->y - rcEmphasis.top;
+
+ CEmphasisSample sample;
+ sample.time = t;
+ Assert( eh >= 0 );
+ sample.value = (float)( dy ) / ( float ) eh;
+ sample.value = 1.0f - clamp( sample.value, 0.0f, 1.0f );
+ sample.selected = false;
+
+ Emphasis_AddSample( sample );
+
+ redraw();
+
+ return true;
+ }
+ else
+ {
+ if ( !shiftdown )
+ {
+ Emphasis_DeselectAll();
+ redraw();
+ }
+
+ StartDragging( DRAGTYPE_EMPHASIS_SELECT, (short)event->x, (short)event->y, NULL );
+ return true;
+ }
+ }
+ else
+ {
+ if ( type == BOUNDARY_PHONEME && m_nSelectedPhonemeCount <= 1 )
+ {
+ StartDragging( DRAGTYPE_PHONEME, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
+ return true;
+ }
+ else if ( type == BOUNDARY_WORD && m_nSelectedWordCount <= 1 )
+ {
+ StartDragging( DRAGTYPE_WORD, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
+ return true;
+ }
+ else if ( IsMouseOverSamples( (short)event->x, (short)event->y ) )
+ {
+ if ( !m_bSelectionActive )
+ {
+ StartDragging( DRAGTYPE_SELECTSAMPLES, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ // Either move, move edge if ctrl key is held, or deselect
+ if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
+ {
+ if ( IsMouseOverSelectionStartEdge( event ) )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTIONSTART, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelectionEndEdge( event ) )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTIONEND, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ if ( shiftdown )
+ {
+ StartDragging( DRAGTYPE_MOVESELECTION, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ }
+ }
+ else
+ {
+ Deselect();
+ redraw();
+ return iret;
+ }
+ }
+ return true;
+ }
+ }
+
+ if ( IsMouseOverTag( (short)event->x, (short)event->y ) )
+ {
+ StartDragging( DRAGTYPE_EVENTTAG_MOVE, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
+ return true;
+ }
+ else
+ {
+ if ( pt )
+ {
+ // Can only move when holding down shift key
+ if ( shiftdown )
+ {
+ pt->m_bSelected = true;
+ StartDragging( DRAGTYPE_MOVEPHONEME,
+ (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ else
+ {
+ // toggle the selection
+ pt->m_bSelected = !pt->m_bSelected;
+ }
+
+
+ m_bWordsActive = false;
+
+ redraw();
+ return true;
+ }
+ else if ( wt )
+ {
+
+ // Can only move when holding down shift key
+ if ( shiftdown )
+ {
+ wt->m_bSelected = true;
+ StartDragging( DRAGTYPE_MOVEWORD,
+ (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ else
+ {
+ // toggle the selection
+ wt->m_bSelected = !wt->m_bSelected;
+ }
+
+ m_bWordsActive = true;
+
+ redraw();
+ return true;
+ }
+ else if ( type == BOUNDARY_NONE )
+ {
+ DeselectPhonemes();
+ DeselectWords();
+ redraw();
+ return true;
+ }
+ }
+ }
+ }
+ break;
+ case mxEvent::MouseMove:
+ case mxEvent::MouseDrag:
+ {
+ OnMouseMove( event );
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ int mx = (short)event->x;
+
+ LimitDrag( mx );
+
+ event->x = (short)mx;
+
+ DrawFocusRect( "finish" );
+
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = 0;
+ }
+
+ switch ( m_nDragType )
+ {
+ case DRAGTYPE_WORD:
+ FinishWordMove( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_PHONEME:
+ FinishPhonemeMove( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_SELECTSAMPLES:
+ FinishSelect( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_MOVESELECTION:
+ FinishMoveSelection( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_MOVESELECTIONSTART:
+ FinishMoveSelectionStart( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_MOVESELECTIONEND:
+ FinishMoveSelectionEnd( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_MOVEWORD:
+ FinishWordDrag( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_MOVEPHONEME:
+ FinishPhonemeDrag( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_EVENTTAG_MOVE:
+ FinishEventTagDrag( m_nStartX, (short)event->x );
+ break;
+ case DRAGTYPE_EMPHASIS_MOVE:
+ {
+ Emphasis_MouseDrag( (short)event->x, (short)event->y );
+ m_Tags.Resort();
+
+ PushRedo();
+
+ redraw();
+ }
+ break;
+ case DRAGTYPE_EMPHASIS_SELECT:
+ {
+ Emphasis_SelectPoints();
+ redraw();
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ float t = GetTimeForPixel( (short)event->x );
+ t += m_flScrubberTimeOffset;
+ m_flScrubberTimeOffset = 0.0f;
+
+ ClampTimeToSelectionInterval( t );
+
+ SetScrubTime( t );
+ SetScrubTargetTime( t );
+
+ sound->Flush();
+
+ DrawScrubHandle();
+ }
+ break;
+ default:
+ break;
+ }
+
+ m_nDragType = DRAGTYPE_NONE;
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyUp:
+ {
+ bool shiftDown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
+ bool ctrlDown = GetAsyncKeyState( VK_CONTROL ) ? true : false;
+
+ switch( event->key )
+ {
+ case VK_TAB:
+ {
+ int direction = shiftDown ? -1 : 1;
+ SelectNextWord( direction );
+ }
+ break;
+ case VK_NEXT:
+ case VK_PRIOR:
+ {
+ m_bWordsActive = event->key == VK_PRIOR ? true : false;
+ redraw();
+ }
+ break;
+ case VK_UP:
+ case VK_RETURN:
+ if ( m_bWordsActive )
+ {
+ if ( event->key == VK_UP ||
+ ctrlDown )
+ {
+ CountSelected();
+
+ if ( m_nSelectedWordCount == 1 )
+ {
+ // Find the selected one
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word || !word->m_bSelected )
+ continue;
+
+ EditWord( word, true );
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( event->key == VK_UP ||
+ ctrlDown )
+ {
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount == 1 )
+ {
+ // Find the selected one
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ if ( !phoneme->m_bSelected )
+ continue;
+
+ EditPhoneme( phoneme, true );
+ }
+ }
+ }
+ }
+ }
+ break;
+ case VK_DELETE:
+ if ( GetMode() == MODE_EMPHASIS )
+ {
+ Emphasis_Delete();
+ }
+ else
+ {
+ if ( m_bWordsActive )
+ {
+ EditDeleteWord();
+ }
+ else
+ {
+ EditDeletePhoneme();
+ }
+ }
+ break;
+ case VK_INSERT:
+ if ( m_bWordsActive )
+ {
+ if ( shiftDown )
+ {
+ EditInsertWordBefore();
+ }
+ else
+ {
+ EditInsertWordAfter();
+ }
+ }
+ else
+ {
+ if ( shiftDown )
+ {
+ EditInsertPhonemeBefore();
+ }
+ else
+ {
+ EditInsertPhonemeAfter();
+ }
+ }
+ break;
+ case VK_SPACE:
+ if ( m_pWaveFile && sound->IsSoundPlaying( m_pMixer ) )
+ {
+ Con_Printf( "Stopping playback\n" );
+ m_btnPlay->setLabel( "Play (Spacebar)" );
+ StopPlayback();
+ }
+ else
+ {
+ Con_Printf( "Playing .wav\n" );
+ m_btnPlay->setLabel( "Stop[ (Spacebar)" );
+ PlayEditedWave( m_bSelectionActive );
+ }
+ break;
+ case VK_SHIFT:
+ case VK_CONTROL:
+ {
+ // Force mouse move
+ POINT pt;
+ GetCursorPos( &pt );
+ SetCursorPos( pt.x, pt.y );
+ return 0;
+ }
+ break;
+ case VK_ESCAPE:
+ {
+ // If playing sound, stop it, otherwise, deselect all
+ if ( !StopPlayback() )
+ {
+ Deselect();
+ DeselectPhonemes();
+ DeselectWords();
+ Emphasis_DeselectAll();
+ redraw();
+ }
+ }
+ break;
+ case 'O':
+ {
+ if ( ctrlDown )
+ {
+ LoadWaveFile();
+ }
+ }
+ break;
+ case 'S':
+ {
+ if ( ctrlDown )
+ {
+ CommitChanges();
+ }
+ }
+ break;
+ case 'T':
+ {
+ if ( ctrlDown )
+ {
+ // Edit sentence text
+ EditWordList();
+ }
+ }
+ break;
+ case 'G':
+ {
+ if ( ctrlDown )
+ {
+ // Commit extraction
+ CommitExtracted();
+ }
+ }
+ break;
+ case 'R':
+ {
+ if ( ctrlDown )
+ {
+ RedoPhonemeExtraction();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ SetFocus( (HWND)getHandle() );
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyDown:
+ {
+ switch ( event->key )
+ {
+ case 'Z':
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ Undo();
+ }
+ break;
+ case 'Y':
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ Redo();
+ }
+ break;
+
+
+ case VK_RIGHT:
+ case VK_LEFT:
+ {
+ int direction = event->key == VK_LEFT ? -1 : 1;
+
+ if ( !m_bWordsActive )
+ {
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ ExtendSelectedPhonemeEndTime( direction );
+ }
+ else if ( GetAsyncKeyState( VK_SHIFT ) )
+ {
+ ShiftSelectedPhoneme( direction );
+ }
+ else
+ {
+ SelectNextPhoneme( direction );
+ }
+ }
+ else
+ {
+ if ( GetAsyncKeyState( VK_CONTROL ) )
+ {
+ ExtendSelectedWordEndTime( direction );
+ }
+ else if ( GetAsyncKeyState( VK_SHIFT ) )
+ {
+ ShiftSelectedWord( direction );
+ }
+ else
+ {
+ SelectNextWord( direction );
+ }
+ }
+ }
+ break;
+ case VK_RETURN:
+ {
+ }
+ break;
+ case VK_SHIFT:
+ case VK_CONTROL:
+ {
+ // Force mouse move
+ POINT pt;
+ GetCursorPos( &pt );
+ //SetCursorPos( pt.x -1, pt.y );
+ SetCursorPos( pt.x, pt.y );
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ iret = 1;
+ }
+ break;
+ }
+ return iret;
+}
+
+void PhonemeEditor::DrawWords( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence& sentence, int type, bool showactive /* = true */ )
+{
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ int ypos = rcWorkSpace.top + m_nTickHeight + 2;
+
+ if ( type == 1 )
+ {
+ ypos += m_nTickHeight + 5;
+ }
+
+ const char *fontName = "Arial";
+
+ bool drawselected;
+ for ( int pass = 0; pass < 2 ; pass++ )
+ {
+ drawselected = pass == 0 ? false : true;
+
+ for (int k = 0; k < sentence.m_Words.Size(); k++)
+ {
+ CWordTag *word = sentence.m_Words[ k ];
+ if ( !word )
+ continue;
+
+ if ( word->m_bSelected != drawselected )
+ continue;
+
+ bool hasselectedphonemes = false;
+ for ( int p = 0; p < word->m_Phonemes.Size() && !hasselectedphonemes; p++ )
+ {
+ CPhonemeTag *t = word->m_Phonemes[ p ];
+ if ( t->m_bSelected )
+ {
+ hasselectedphonemes = true;
+ }
+ }
+
+ float t1 = word->m_flStartTime;
+ float t2 = word->m_flEndTime;
+
+ // Tag it
+ float frac = ( t1 - starttime ) / ( endtime - starttime );
+
+ int xpos = ( int )( frac * rcWorkSpace.right );
+
+ if ( frac <= 0.0 )
+ xpos = 0;
+
+ // Draw duration
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+ if ( frac2 < 0.0 )
+ continue;
+
+ int xpos2 = ( int )( frac2 * rcWorkSpace.right );
+
+ // Draw line and vertical ticks
+ RECT rcWord;
+ rcWord.left = xpos;
+ rcWord.right = xpos2;
+ rcWord.top = ypos - m_nTickHeight + 1;
+ rcWord.bottom = ypos;
+
+ drawHelper.DrawFilledRect(
+ PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_SELECTED : COLOR_PHONEME_TAG_FILLER_NORMAL ),
+ rcWord );
+
+ COLORREF border = PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_BORDER_SELECTED : COLOR_PHONEME_TAG_BORDER );
+
+ if ( showactive && m_bWordsActive )
+ {
+ drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_ACTIVE_BORDER ), xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight + 4 );
+ }
+
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos2, ypos );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos, ypos - m_nTickHeight );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos2, ypos, xpos2, ypos - m_nTickHeight );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight );
+
+ if ( hasselectedphonemes )
+ {
+ drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_SELECTED_BORDER ), xpos, ypos - 3, xpos2, ypos );
+ }
+
+ //if ( frac >= 0.0 && frac <= 1.0 )
+ {
+ int fontsize = 9;
+
+ RECT rcText;
+ rcText.left = xpos;
+ rcText.right = xpos + 500;
+ rcText.top = ypos - m_nTickHeight + 4;
+ rcText.bottom = rcText.top + fontsize + 2;
+
+ int length = drawHelper.CalcTextWidth( fontName, fontsize, FW_NORMAL, "%s", word->GetWord() );
+
+ rcText.right = max( (LONG)xpos2 - 2, rcText.left + length + 1 );
+
+ int w = rcText.right - rcText.left;
+ if ( w > length )
+ {
+ rcText.left += ( w - length ) / 2;
+ }
+
+ drawHelper.DrawColoredText(
+ fontName,
+ fontsize,
+ FW_NORMAL,
+ PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_TEXT_SELECTED : COLOR_PHONEME_TAG_TEXT ),
+ rcText,
+ "%s", word->GetWord() );
+ }
+
+ }
+ }
+}
+
+void PhonemeEditor::DrawPhonemes( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence& sentence, int type, bool showactive /* = true */ )
+{
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ int ypos = rcWorkSpace.bottom - m_nTickHeight - 2;
+
+ if ( type == 1 )
+ {
+ ypos -= ( m_nTickHeight + 5 );
+ }
+
+ const char *fontName = "Arial";
+
+ bool drawselected;
+ for ( int pass = 0; pass < 2 ; pass++ )
+ {
+ drawselected = pass == 0 ? false : true;
+
+ for ( int i = 0; i < sentence.m_Words.Size(); i++ )
+ {
+ CWordTag *w = sentence.m_Words[ i ];
+ if ( !w )
+ continue;
+
+ if ( w->m_bSelected != drawselected )
+ continue;
+
+ for ( int k = 0; k < w->m_Phonemes.Size(); k++ )
+ {
+ CPhonemeTag *pPhoneme = w->m_Phonemes[ k ];
+
+ float t1 = pPhoneme->GetStartTime();
+ float t2 = pPhoneme->GetEndTime();
+
+ // Tag it
+ float frac = ( t1 - starttime ) / ( endtime - starttime );
+
+ int xpos = ( int )( frac * rcWorkSpace.right );
+ if ( frac <= 0.0 )
+ {
+ xpos = 0;
+ }
+
+ // Draw duration
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+ if ( frac2 < 0.0 )
+ {
+ continue;
+ }
+
+ int xpos2 = ( int )( frac2 * rcWorkSpace.right );
+
+ RECT rcFrame;
+ rcFrame.left = xpos;
+ rcFrame.right = xpos2;
+ rcFrame.top = ypos - m_nTickHeight + 1;
+ rcFrame.bottom = ypos;
+
+ drawHelper.DrawFilledRect(
+ PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_SELECTED : COLOR_PHONEME_TAG_FILLER_NORMAL ),
+ rcFrame );
+
+ COLORREF border = PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_BORDER_SELECTED : COLOR_PHONEME_TAG_BORDER );
+
+ if ( showactive && !m_bWordsActive )
+ {
+ drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_ACTIVE_BORDER ), xpos, ypos - 3, xpos2, ypos );
+ }
+
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos, ypos - m_nTickHeight );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos2, ypos, xpos2, ypos - m_nTickHeight );
+ drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos2, ypos );
+
+ if ( w->m_bSelected )
+ {
+ drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_SELECTED_BORDER ), xpos, ypos - m_nTickHeight + 1, xpos2, ypos - m_nTickHeight + 4 );
+ }
+
+ //if ( frac >= 0.0 && frac <= 1.0 )
+ {
+
+ int fontsize = 9;
+
+ RECT rcText;
+ rcText.left = xpos;
+ rcText.right = xpos + 500;
+ rcText.top = ypos - m_nTickHeight + 4;
+ rcText.bottom = rcText.top + fontsize + 2;
+
+ int length = drawHelper.CalcTextWidth( fontName, fontsize, FW_NORMAL, "%s", ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
+
+ rcText.right = max( (LONG)xpos2 - 2, rcText.left + length + 1 );
+
+ int w = rcText.right - rcText.left;
+ if ( w > length )
+ {
+ rcText.left += ( w - length ) / 2;
+ }
+
+ drawHelper.DrawColoredText(
+ fontName,
+ fontsize,
+ FW_NORMAL,
+ PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_TEXT_SELECTED : COLOR_PHONEME_TAG_TEXT ),
+ rcText,
+ "%s", ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rc -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
+{
+ if ( !m_pEvent || !m_pWaveFile )
+ return;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" );
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ for ( int i = 0; i < m_pEvent->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = m_pEvent->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ float tagtime = tag->GetPercentage() * m_pWaveFile->GetRunningLength();
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ float frac = ( tagtime - starttime ) / ( endtime - starttime );
+
+ int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f );
+
+ RECT rcMark;
+ rcMark = rc;
+ rcMark.top = rc.bottom - 8;
+ rcMark.bottom = rc.bottom;
+ rcMark.left = left - 4;
+ rcMark.right = left + 4;
+
+ drawHelper.DrawTriangleMarker( rcMark, PEColor( COLOR_PHONEME_TIMING_TAG ) );
+
+ RECT rcText;
+ rcText = rc;
+ rcText.bottom = rc.bottom - 10;
+ rcText.top = rcText.bottom - 10;
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
+ rcText.left = left - len / 2;
+ rcText.right = rcText.left + len + 2;
+
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rcText, tag->GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::redraw( void )
+{
+ if ( !ToolCanDraw() )
+ return;
+
+ CChoreoWidgetDrawHelper drawHelper( this );
+ HandleToolRedraw( drawHelper );
+
+ if ( !m_pWaveFile )
+ return;
+
+ HDC dc = drawHelper.GrabDC();
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ // Now draw the time legend
+ RECT rcLabel;
+ float granularity = 0.5f;
+
+ drawHelper.DrawColoredLine( PEColor( COLOR_PHONEME_TIMELINE ), PS_SOLID, 1, rc.left, rc.bottom - m_nTickHeight, rc.right, rc.bottom - m_nTickHeight );
+
+ if ( GetMode() != MODE_EMPHASIS )
+ {
+ Emphasis_Redraw( drawHelper, rc );
+ }
+
+ sound->RenderWavToDC(
+ dc,
+ rc,
+ PEColor( COLOR_PHONEME_WAVDATA ),
+ starttime,
+ endtime,
+ m_pWaveFile,
+ m_bSelectionActive,
+ m_nSelection[ 0 ],
+ m_nSelection[ 1 ] );
+
+ float f = SnapTime( starttime, granularity );
+ while ( f <= endtime )
+ {
+ float frac = ( f - starttime ) / ( endtime - starttime );
+ if ( frac >= 0.0f && frac <= 1.0f )
+ {
+ drawHelper.DrawColoredLine( ( COLOR_PHONEME_TIMELINE_MAJORTICK ), PS_SOLID, 1, (int)( frac * rc.right ), rc.top, (int)( frac * rc.right ), rc.bottom - m_nTickHeight );
+
+ rcLabel.left = (int)( frac * rc.right );
+ rcLabel.bottom = rc.bottom;
+ rcLabel.top = rcLabel.bottom - 10;
+
+ 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, PEColor( COLOR_PHONEME_TEXT ), rcLabel, sz );
+ }
+ f += granularity;
+ }
+
+ HBRUSH br = CreateSolidBrush( PEColor( COLOR_PHONEME_TEXT ) );
+
+ FrameRect( dc, &rc, br );
+
+ DeleteObject( br );
+
+ RECT rcTags = rc;
+ rcTags.top = TAG_TOP;
+ rcTags.bottom = TAG_BOTTOM;
+
+ DrawRelativeTags( drawHelper, rcTags );
+
+ int fontsize = 9;
+ RECT rcText = rc;
+ rcText.top = rcText.bottom + 5;
+ rcText.left += 5;
+ rcText.bottom = rcText.top + fontsize + 1;
+ rcText.right -= 5;
+
+ int fontweight = FW_NORMAL;
+
+ const char *font = "Arial";
+
+ if ( m_nLastExtractionResult != SR_RESULT_NORESULT )
+ {
+ COLORREF clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_OTHER );
+ switch ( m_nLastExtractionResult )
+ {
+ case SR_RESULT_ERROR:
+ clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_ERROR );
+ break;
+ case SR_RESULT_SUCCESS:
+ clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS );
+ break;
+ case SR_RESULT_FAILED:
+ clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_FAIL );
+ break;
+ default:
+ break;
+ }
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, clr, rcText,
+ "Last Extraction Result: %s", GetExtractionResultString( m_nLastExtractionResult ) );
+
+ OffsetRect( &rcText, 0, fontsize + 1 );
+ }
+
+ if ( m_pEvent && !Q_stristr( m_pEvent->GetParameters(), ".wav" ) )
+ {
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "Sound: '%s', file: %s, length %.2f seconds",
+ m_pEvent->GetParameters(),
+ m_WorkFile.m_szWaveFile,
+ m_pWaveFile->GetRunningLength() );
+ }
+ else
+ {
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "File: %s, length %.2f seconds", m_WorkFile.m_szWaveFile, m_pWaveFile->GetRunningLength() );
+ }
+
+ OffsetRect( &rcText, 0, fontsize + 1 );
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "Number of samples %i at %ikhz (%i bits/sample) %s", (int) (m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() ), m_pWaveFile->SampleRate(), (m_pWaveFile->SampleSize()<<3), m_Tags.GetVoiceDuck() ? "duck other audio" : "no ducking" );
+
+ OffsetRect( &rcText, 0, fontsize + 1 );
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "[ %i ] Words [ %i ] Phonemes / Zoom %i %%", m_Tags.m_Words.Size(), m_Tags.CountPhonemes(), m_nTimeZoom );
+
+ if ( m_pEvent )
+ {
+ OffsetRect( &rcText, 0, fontsize + 1 );
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "Event %s", m_pEvent->GetName() );
+ }
+
+ OffsetRect( &rcText, 0, fontsize + 1 );
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ "Using: %s", GetSpeechAPIName() );
+
+
+ char text[ 4096 ];
+ sprintf( text, "Sentence Text: %s", m_Tags.GetText() );
+
+ int halfwidth = ( rc.right - rc.left ) / 2;
+
+ rcText = rc;
+ rcText.left = halfwidth;
+ rcText.top = rcText.bottom + 5;
+ rcText.right = rcText.left + halfwidth * 0.6;
+
+ drawHelper.CalcTextRect( font, fontsize, fontweight, halfwidth, rcText, text );
+
+ drawHelper.DrawColoredTextMultiline( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
+ text );
+
+ CWordTag *cw = GetSelectedWord();
+ if ( cw )
+ {
+ char wordInfo[ 512 ];
+ sprintf( wordInfo, "Word: %s, start %.2f end %.2f, duration %.2f ms phonemes %i",
+ cw->GetWord(), cw->m_flStartTime, cw->m_flEndTime, 1000.0f * ( cw->m_flEndTime - cw->m_flStartTime ),
+ cw->m_Phonemes.Size() );
+
+ int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, wordInfo );
+
+ OffsetRect( &rcText, 0, ( rcText.bottom - rcText.top ) + 2 );
+
+ rcText.left = rcText.right - length - 10;
+ rcText.bottom = rcText.top + fontsize + 1;
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText, wordInfo );
+ }
+
+ CPhonemeTag *cp = GetSelectedPhoneme();
+ if ( cp )
+ {
+ char phonemeInfo[ 512 ];
+ sprintf( phonemeInfo, "Phoneme: %s, start %.2f end %.2f, duration %.2f ms",
+ ConvertPhoneme( cp->GetPhonemeCode() ), cp->GetStartTime(), cp->GetEndTime(), 1000.0f * ( cp->GetEndTime() - cp->GetStartTime() ) );
+
+ int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, phonemeInfo );
+
+ OffsetRect( &rcText, 0, ( rcText.bottom - rcText.top ) + 2 );
+
+ rcText.left = rcText.right - length - 10;
+ rcText.bottom = rcText.top + fontsize + 1;
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText, phonemeInfo );
+ }
+
+ // Draw playback rate
+ {
+ char sz[ 48 ];
+ sprintf( sz, "Speed: %.2fx", m_flPlaybackRate );
+
+ int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, sz);
+
+ rcText = rc;
+ rcText.top = rc.bottom + 60;
+ rcText.bottom = rcText.top + fontsize + 1;
+ rcText.left = m_pPlaybackRate->x() + m_pPlaybackRate->w() - x();
+ rcText.right = rcText.left + length + 2;
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight,
+ PEColor( COLOR_PHONEME_TEXT ), rcText, sz );
+ }
+
+ if ( m_UndoStack.Size() > 0 )
+ {
+ int length = drawHelper.CalcTextWidth( font, fontsize, fontweight,
+ "Undo levels: %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+
+ rcText = rc;
+ rcText.top = rc.bottom + 60;
+ rcText.bottom = rcText.top + fontsize + 1;
+ rcText.right -= 5;
+ rcText.left = rcText.right - length - 10;
+
+ drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS ), rcText,
+ "Undo levels: %i/%i", m_nUndoLevel, m_UndoStack.Size() );
+ }
+
+ float endfrac = ( m_pWaveFile->GetRunningLength() - starttime ) / ( endtime - starttime );
+ if ( endfrac >= 0.0f && endfrac <= 1.0f )
+ {
+ int endpos = ( int ) ( rc.right * endfrac );
+
+ drawHelper.DrawColoredLine( PEColor( COLOR_PHONEME_WAV_ENDPOINT ), PS_DOT, 2, endpos, rc.top, endpos, rc.bottom - m_nTickHeight );
+ }
+
+ DrawPhonemes( drawHelper, rc, m_Tags, 0 );
+
+ DrawPhonemes( drawHelper, rc, m_TagsExt, 1, false );
+
+ DrawWords( drawHelper, rc, m_Tags, 0 );
+
+ DrawWords( drawHelper, rc, m_TagsExt, 1, false );
+
+ if ( GetMode() == MODE_EMPHASIS )
+ {
+ Emphasis_Redraw( drawHelper, rc );
+ }
+
+ DrawScrubHandle( drawHelper );
+}
+
+#define MOTION_RANGE 3000
+#define MOTION_MAXSTEP 500
+//-----------------------------------------------------------------------------
+// Purpose: Brown noise simulates brownian motion centered around 127.5 but we cap the walking
+// to just a couple of units
+// Input : *buffer -
+// count -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void WriteBrownNoise( void *buffer, int count )
+{
+ int currentValue = 127500;
+ int maxValue = currentValue + ( MOTION_RANGE / 2 );
+ int minValue = currentValue - ( MOTION_RANGE / 2 );
+
+ unsigned char *pos = ( unsigned char *)buffer;
+
+ while ( --count >= 0 )
+ {
+ currentValue += random->RandomInt( -MOTION_MAXSTEP, MOTION_MAXSTEP );
+ currentValue = min( maxValue, currentValue );
+ currentValue = max( minValue, currentValue );
+
+ // Downsample to 0-255 range
+ *pos++ = (unsigned char)( ( (float)currentValue / 1000.0f ) + 0.5f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Replace with brownian noice parts of the wav file that we dont' want processed by the
+// speech recognizer
+// Input : store -
+// *format -
+// chunkname -
+// *buffer -
+// buffersize -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ResampleChunk( IterateOutputRIFF& store, void *format, int chunkname, char *buffer, int buffersize, int start_silence /*=0*/, int end_silence /*=0*/ )
+{
+ WAVEFORMATEX *pFormat = ( WAVEFORMATEX * )format;
+ Assert( pFormat );
+
+ if ( pFormat->wFormatTag == WAVE_FORMAT_PCM )
+ {
+ int silience_time = start_silence + end_silence;
+
+ // Leave room for silence at start + end
+ int resamplesize = buffersize + silience_time * pFormat->nSamplesPerSec;
+ char *resamplebuffer = new char[ resamplesize + 4 ];
+ memset( resamplebuffer, (unsigned char)128, resamplesize + 4 );
+
+ int startpos = (int)( start_silence * pFormat->nSamplesPerSec );
+
+ if ( startpos > 0 )
+ {
+ WriteBrownNoise( resamplebuffer, startpos );
+ }
+
+ if ( startpos + buffersize < resamplesize )
+ {
+ WriteBrownNoise( &resamplebuffer[ startpos + buffersize ], resamplesize - ( startpos + buffersize ) );
+ }
+
+ memcpy( &resamplebuffer[ startpos ], buffer, buffersize );
+
+ store.ChunkWriteData( resamplebuffer, resamplesize );
+ return;
+ }
+
+ store.ChunkWriteData( buffer, buffersize );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ReadLinguisticTags( void )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ CAudioSource *wave = sound->LoadSound( m_WorkFile.m_szWorkingFile );
+ if ( !wave )
+ return;
+
+ m_Tags.Reset();
+
+ CSentence *sentence = wave->GetSentence();
+ if ( sentence )
+ {
+ // Copy data from sentence to m_Tags
+ m_Tags.Reset();
+ m_Tags = *sentence;
+ }
+
+ delete wave;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Switch wave files
+// Input : *wavefile -
+// force -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SetCurrentWaveFile( const char *wavefile, bool force /*=false*/, CChoreoEvent *event /*=NULL*/ )
+{
+ // No change?
+ if ( !force && !stricmp( m_WorkFile.m_szWaveFile, wavefile ) )
+ return;
+
+ StopPlayback();
+
+ if ( GetDirty() )
+ {
+ int retval = mxMessageBox( this, va( "Save current changes to %s", m_WorkFile.m_szWaveFile ),
+ "Phoneme Editor", MX_MB_QUESTION | MX_MB_YESNOCANCEL );
+
+ // Cancel
+ if ( retval == 2 )
+ return;
+
+ // Yes
+ if ( retval == 0 )
+ {
+ CommitChanges();
+ }
+ }
+
+ ClearExtracted();
+
+ m_Tags.Reset();
+ m_TagsExt.Reset();
+
+ Deselect();
+
+ if ( m_pWaveFile )
+ {
+ char fn[ 512 ];
+ Q_snprintf( fn, sizeof( fn ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
+ filesystem->RemoveFile( fn, "GAME" );
+ }
+
+ delete m_pWaveFile;
+ m_pWaveFile = NULL;
+
+ SetDirty( false );
+
+ // Set up event and scene
+ m_pEvent = event;
+
+ // Try an dload new sound
+ m_pWaveFile = sound->LoadSound( wavefile );
+ Q_strncpy( m_WorkFile.m_szWaveFile, wavefile, sizeof( m_WorkFile.m_szWaveFile ) );
+
+ char fullpath[ 512 ];
+ filesystem->RelativePathToFullPath( wavefile, "GAME", fullpath, sizeof( fullpath ) );
+ int len = Q_strlen( fullpath );
+ int charstocopy = len - Q_strlen( wavefile ) + 1;
+ m_WorkFile.m_szBasePath[ 0 ] = 0;
+ if ( charstocopy >= 0 )
+ {
+ Q_strncpy( m_WorkFile.m_szBasePath, fullpath, charstocopy );
+ m_WorkFile.m_szBasePath[ charstocopy ] = 0;
+ }
+ Q_StripExtension( wavefile, m_WorkFile.m_szWorkingFile, sizeof( m_WorkFile.m_szWorkingFile ) );
+ Q_strncat( m_WorkFile.m_szWorkingFile, "_work.wav", sizeof( m_WorkFile.m_szWorkingFile ), COPY_ALL_CHARACTERS );
+
+ Q_FixSlashes( m_WorkFile.m_szWaveFile );
+ Q_FixSlashes( m_WorkFile.m_szWorkingFile );
+ Q_FixSlashes( m_WorkFile.m_szBasePath );
+
+ if ( !m_pWaveFile )
+ {
+ Con_ErrorPrintf( "Couldn't set current .wav file to %s\n", m_WorkFile.m_szWaveFile );
+ return;
+ }
+
+ Con_Printf( "Current .wav file set to %s\n", m_WorkFile.m_szWaveFile );
+
+ g_pWaveBrowser->SetCurrent( m_WorkFile.m_szWaveFile );
+
+ // Copy over and overwrite file
+ FPCopyFile( m_WorkFile.m_szWaveFile, m_WorkFile.m_szWorkingFile, false );
+ // Make it writable
+ MakeFileWriteable( m_WorkFile.m_szWorkingFile );
+
+ ReadLinguisticTags();
+
+ Deselect();
+
+ RepositionHSlider();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::MoveTimeSliderToPos( int x )
+{
+ m_nLeftOffset = x;
+ m_pHorzScrollBar->setValue( m_nLeftOffset );
+ InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
+ redraw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int PhonemeEditor::ComputeHPixelsNeeded( void )
+{
+ int pixels = 0;
+
+ if ( m_pWaveFile )
+ {
+ float maxtime = m_pWaveFile->GetRunningLength();
+ maxtime += 1.0f;
+ pixels = (int)( maxtime * GetPixelsPerSecond() );
+ }
+
+ return pixels;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::RepositionHSlider( void )
+{
+ int pixelsneeded = ComputeHPixelsNeeded();
+
+ if ( pixelsneeded <= w2() )
+ {
+ m_pHorzScrollBar->setVisible( false );
+ }
+ else
+ {
+ m_pHorzScrollBar->setVisible( true );
+ }
+
+ m_pHorzScrollBar->setBounds( 0, GetCaptionHeight(), w2(), 12 );
+
+ m_pHorzScrollBar->setRange( 0, pixelsneeded );
+ m_pHorzScrollBar->setValue( 0 );
+ m_nLeftOffset = 0;
+
+ m_pHorzScrollBar->setPagesize( w2() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float PhonemeEditor::GetPixelsPerSecond( void )
+{
+ return m_flPixelsPerSecond * GetTimeZoomScale();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float PhonemeEditor::GetTimeZoomScale( void )
+{
+ return ( float )m_nTimeZoom / 100.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : scale -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SetTimeZoomScale( int scale )
+{
+ m_nTimeZoom = scale;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Think( float dt )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
+ ScrubThink( dt, scrubbing );
+
+ if ( m_pMixer && !sound->IsSoundPlaying( m_pMixer ) )
+ {
+ m_pMixer = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+int PhonemeEditor::IsMouseOverBoundary( mxEvent *event )
+{
+ int mx, my;
+
+ mx = (short)event->x;
+ my = (short)event->y;
+
+ // Deterime if phoneme boundary is under the cursor
+ //
+ if ( !m_pWaveFile )
+ return BOUNDARY_NONE;
+
+ if ( !(event->modifiers & mxEvent::KeyCtrl ) )
+ {
+ return BOUNDARY_NONE;
+ }
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( IsMouseOverPhonemeRow( my ) )
+ {
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ int mouse_tolerance = 3;
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+
+ for ( int k = 0; k < word->m_Phonemes.Size(); k++ )
+ {
+ CPhonemeTag *pPhoneme = word->m_Phonemes[ k ];
+
+ float t1 = pPhoneme->GetStartTime();
+ float t2 = pPhoneme->GetEndTime();
+
+ // Tag it
+ float frac1 = ( t1 - starttime ) / ( endtime - starttime );
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+
+ int xpos1 = ( int )( frac1 * w2() );
+ int xpos2 = ( int )( frac2 * w2() );
+ if ( abs( xpos1 - mx ) <= mouse_tolerance ||
+ abs( xpos2 - mx ) <= mouse_tolerance )
+ {
+ return BOUNDARY_PHONEME;
+ }
+ }
+ }
+ }
+
+ if ( IsMouseOverWordRow( my ) )
+ {
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ int mouse_tolerance = 3;
+
+ for ( int k = 0; k < m_Tags.m_Words.Size(); k++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ k ];
+
+ float t1 = word->m_flStartTime;
+ float t2 = word->m_flEndTime;
+
+ // Tag it
+ float frac1 = ( t1 - starttime ) / ( endtime - starttime );
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+
+ int xpos1 = ( int )( frac1 * w2() );
+ int xpos2 = ( int )( frac2 * w2() );
+ if ( ( abs( xpos1 - mx ) <= mouse_tolerance ) ||
+ ( abs( xpos2 - mx ) <= mouse_tolerance ) )
+ {
+ return BOUNDARY_WORD;
+ }
+ }
+ }
+
+ return BOUNDARY_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::DrawFocusRect( char *reason )
+{
+ 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 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &rc -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::GetWorkspaceRect( RECT &rc )
+{
+ GetClientRect( (HWND)getHandle(), &rc );
+
+ rc.top += TAG_BOTTOM;
+ rc.bottom = rc.bottom - 75 - MODE_TAB_OFFSET;
+
+ InflateRect( &rc, -1, -1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ShowWordMenu( CWordTag *word, int mx, int my )
+{
+ CountSelected();
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ Assert( pop );
+
+ pop->add( va( "Edit sentence text..." ), IDC_EDITWORDLIST );
+
+ if ( m_nSelectedWordCount > 0 && word )
+ {
+ pop->addSeparator();
+
+ pop->add( va( "Delete %s", m_nSelectedWordCount > 1 ? "words" : va( "'%s'", word->GetWord() ) ), IDC_EDIT_DELETEWORD );
+
+ if ( m_nSelectedWordCount == 1 )
+ {
+ int index = IndexOfWord( word );
+ bool valid = false;
+ if ( index != -1 )
+ {
+ SetClickedPhoneme( index, -1 );
+ valid = true;
+ }
+
+ if ( valid )
+ {
+ pop->add( va( "Edit word '%s'...", word->GetWord() ), IDC_EDIT_WORD );
+
+ float nextGap = GetTimeGapToNextWord( true, word );
+ float prevGap = GetTimeGapToNextWord( false, word );
+
+ if ( nextGap > MINIMUM_WORD_GAP ||
+ prevGap > MINIMUM_WORD_GAP )
+ {
+ pop->addSeparator();
+ if ( prevGap > MINIMUM_WORD_GAP )
+ {
+ pop->add( va( "Insert word before '%s'...", word->GetWord() ), IDC_EDIT_INSERTWORDBEFORE );
+ }
+ if ( nextGap > MINIMUM_WORD_GAP )
+ {
+ pop->add( va( "Insert word after '%s'...", word->GetWord() ), IDC_EDIT_INSERTWORDAFTER );
+ }
+ }
+
+ if ( word->m_Phonemes.Size() == 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Add phoneme to '%s'...", word->GetWord() ), IDC_EDIT_INSERTFIRSTPHONEMEOFWORD );
+ }
+
+ pop->addSeparator();
+ pop->add( va( "Select all words after '%s'", word->GetWord() ), IDC_SELECT_WORDSRIGHT );
+ pop->add( va( "Select all words before '%s'", word->GetWord() ), IDC_SELECT_WORDSLEFT );
+ }
+ }
+ }
+
+ if ( AreSelectedWordsContiguous() && m_nSelectedWordCount > 1 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Merge words" ), IDC_SNAPWORDS );
+
+ if ( m_nSelectedWordCount == 2 )
+ {
+ pop->add( va( "Separate words" ), IDC_SEPARATEWORDS );
+ }
+ }
+
+ if ( m_nSelectedWordCount > 0 )
+ {
+ pop->addSeparator();
+
+ pop->add( va( "Deselect all" ), IDC_DESELECT_PHONEMESANDWORDS );
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Realign phonemes to words" ), IDC_REALIGNPHONEMES );
+ }
+
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ShowPhonemeMenu( CPhonemeTag *pho, int mx, int my )
+{
+ CountSelected();
+
+ SetClickedPhoneme( -1, -1 );
+
+ if ( !pho )
+ return;
+
+ if ( m_Tags.CountPhonemes() == 0 )
+ {
+ Con_Printf( "No phonemes, try extracting from .wav first\n" );
+ return;
+ }
+
+ mxPopupMenu *pop = new mxPopupMenu();
+ bool valid = false;
+ CWordTag *tag = m_Tags.GetWordForPhoneme( pho );
+ if ( tag )
+ {
+ int wordNum = IndexOfWord( tag );
+ int pi = tag->IndexOfPhoneme( pho );
+
+ SetClickedPhoneme( wordNum, pi );
+ valid = true;
+ }
+
+ if ( valid )
+ {
+ if ( m_nSelectedPhonemeCount == 1 )
+ {
+ pop->add( va( "Edit '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_PHONEME );
+
+ float nextGap = GetTimeGapToNextPhoneme( true, pho );
+ float prevGap = GetTimeGapToNextPhoneme( false, pho );
+
+ if ( nextGap > MINIMUM_PHONEME_GAP ||
+ prevGap > MINIMUM_PHONEME_GAP )
+ {
+ pop->addSeparator();
+ if ( prevGap > MINIMUM_PHONEME_GAP )
+ {
+ pop->add( va( "Insert phoneme before '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_INSERTPHONEMEBEFORE );
+ }
+ if ( nextGap > MINIMUM_PHONEME_GAP )
+ {
+ pop->add( va( "Insert phoneme after '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_INSERTPHONEMEAFTER );
+ }
+ }
+
+ pop->addSeparator();
+ pop->add( va( "Select all phonemes after '%s'", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_SELECT_PHONEMESRIGHT );
+ pop->add( va( "Select all phonemes before '%s'",ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_SELECT_PHONEMESLEFT );
+
+ pop->addSeparator();
+ }
+
+ if ( AreSelectedPhonemesContiguous() && m_nSelectedPhonemeCount > 1 )
+ {
+ pop->add( va( "Merge phonemes" ), IDC_SNAPPHONEMES );
+ if ( m_nSelectedPhonemeCount == 2 )
+ {
+ pop->add( va( "Separate phonemes" ), IDC_SEPARATEPHONEMES );
+ }
+
+ pop->addSeparator();
+ }
+
+ if ( m_nSelectedPhonemeCount >= 1 )
+ {
+ pop->add( va( "Delete %s",
+ m_nSelectedPhonemeCount == 1 ? va( "'%s'", ConvertPhoneme( pho->GetPhonemeCode() ) ) : "phonemes" ), IDC_EDIT_DELETEPHONEME );
+
+ pop->addSeparator();
+ pop->add( va( "Deselect all" ), IDC_DESELECT_PHONEMESANDWORDS );
+ }
+ }
+
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Realign words to phonemes" ), IDC_REALIGNWORDS );
+ }
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : float
+//-----------------------------------------------------------------------------
+float PhonemeEditor::GetTimeForPixel( int mx )
+{
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float time = (float)mx / GetPixelsPerSecond() + starttime;
+
+ return time;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// **pp1 -
+// **pp2 -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::FindSpanningPhonemes( float time, CPhonemeTag **pp1, CPhonemeTag **pp2 )
+{
+ Assert( pp1 && pp2 );
+
+ *pp1 = NULL;
+ *pp2 = NULL;
+
+ // Three pixels
+ double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 3;
+
+ CPhonemeTag *previous = NULL;
+
+ for ( int w = 0; w < m_Tags.m_Words.Size(); w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+
+ for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *current = word->m_Phonemes[ i ];
+ double dt;
+
+ if ( !previous )
+ {
+ dt = fabs( current->GetStartTime() - time );
+ if ( dt < time_epsilon )
+ {
+ *pp2 = current;
+ return true;
+ }
+ }
+ else
+ {
+ int found = 0;
+
+ dt = fabs( previous->GetEndTime() - time );
+ if ( dt < time_epsilon )
+ {
+ *pp1 = previous;
+ found++;
+ }
+
+ dt = fabs( current->GetStartTime() - time );
+ if ( dt < time_epsilon )
+ {
+ *pp2 = current;
+ found++;
+ }
+
+ if ( found != 0 )
+ {
+ return true;
+ }
+ }
+
+ previous = current;
+ }
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ // Check last word, but only if it has some phonemes
+ CWordTag *lastWord = m_Tags.m_Words[ m_Tags.m_Words.Size() - 1 ];
+ if ( lastWord &&
+ ( lastWord->m_Phonemes.Size() > 0 ) )
+ {
+
+ CPhonemeTag *last = lastWord->m_Phonemes[ lastWord->m_Phonemes.Size() - 1 ];
+ float dt;
+ dt = fabs( last->GetEndTime() - time );
+ if ( dt < time_epsilon )
+ {
+ *pp1 = last;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+// **pp1 -
+// **pp2 -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::FindSpanningWords( float time, CWordTag **pp1, CWordTag **pp2 )
+{
+ Assert( pp1 && pp2 );
+
+ *pp1 = NULL;
+ *pp2 = NULL;
+
+ // Three pixels
+ double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 3;
+
+ CWordTag *previous = NULL;
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *current = m_Tags.m_Words[ i ];
+ double dt;
+
+ if ( !previous )
+ {
+ dt = fabs( current->m_flStartTime - time );
+ if ( dt < time_epsilon )
+ {
+ *pp2 = current;
+ return true;
+ }
+ }
+ else
+ {
+ int found = 0;
+
+ dt = fabs( previous->m_flEndTime - time );
+ if ( dt < time_epsilon )
+ {
+ *pp1 = previous;
+ found++;
+ }
+
+ dt = fabs( current->m_flStartTime - time );
+ if ( dt < time_epsilon )
+ {
+ *pp2 = current;
+ found++;
+ }
+
+ if ( found != 0 )
+ {
+ return true;
+ }
+ }
+
+ previous = current;
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ CWordTag *last = m_Tags.m_Words[ m_Tags.m_Words.Size() - 1 ];
+ float dt;
+ dt = fabs( last->m_flEndTime - time );
+ if ( dt < time_epsilon )
+ {
+ *pp1 = last;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int PhonemeEditor::FindWordForTime( float time )
+{
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *pCurrent = m_Tags.m_Words[ i ];
+
+ if ( time < pCurrent->m_flStartTime )
+ continue;
+
+ if ( time > pCurrent->m_flEndTime )
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+void PhonemeEditor::FinishWordDrag( int startx, int endx )
+{
+ float clicktime = GetTimeForPixel( startx );
+ float endtime = GetTimeForPixel( endx );
+
+ float dt = endtime - clicktime;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraverseWords( &PhonemeEditor::ITER_MoveSelectedWords, dt );
+
+ RealignPhonemesToWords( false );
+ CleanupWordsAndPhonemes( false );
+
+ PushRedo();
+
+ redraw();
+}
+
+void PhonemeEditor::FinishWordMove( int startx, int endx )
+{
+ float clicktime = GetTimeForPixel( startx );
+ float endtime = GetTimeForPixel( endx );
+
+ // Find the phonemes who have the closest start/endtime to the starting click time
+ CWordTag *current, *next;
+
+ if ( !FindSpanningWords( clicktime, &current, &next ) )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ if ( current && !next )
+ {
+ // cap movement
+ current->m_flEndTime += ( endtime - clicktime );
+ }
+ else if ( !current && next )
+ {
+ // cap movement
+ next->m_flStartTime += ( endtime - clicktime );
+ }
+ else
+ {
+ // cap movement
+ endtime = min( endtime, next->m_flEndTime - 1.0f / GetPixelsPerSecond() );
+ endtime = max( endtime, current->m_flStartTime + 1.0f / GetPixelsPerSecond() );
+
+ current->m_flEndTime = endtime;
+ next->m_flStartTime = endtime;
+ }
+
+ RealignPhonemesToWords( false );
+ CleanupWordsAndPhonemes( false );
+
+ PushRedo();
+
+ redraw();
+}
+
+CPhonemeTag *PhonemeEditor::FindPhonemeForTime( float time )
+{
+ for ( int w = 0 ; w < m_Tags.m_Words.Size(); w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+
+
+ for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *pCurrent = word->m_Phonemes[ i ];
+
+ if ( time < pCurrent->GetStartTime() )
+ continue;
+
+ if ( time > pCurrent->GetEndTime() )
+ continue;
+
+ return pCurrent;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : phoneme -
+// startx -
+// endx -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::FinishPhonemeDrag( int startx, int endx )
+{
+ float clicktime = GetTimeForPixel( startx );
+ float endtime = GetTimeForPixel( endx );
+
+ float dt = endtime - clicktime;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraversePhonemes( &PhonemeEditor::ITER_MoveSelectedPhonemes, dt );
+
+ RealignWordsToPhonemes( false );
+ CleanupWordsAndPhonemes( false );
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : phoneme -
+// startx -
+// endx -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::FinishPhonemeMove( int startx, int endx )
+{
+ float clicktime = GetTimeForPixel( startx );
+ float endtime = GetTimeForPixel( endx );
+
+ // Find the phonemes who have the closest start/endtime to the starting click time
+ CPhonemeTag *current, *next;
+
+ if ( !FindSpanningPhonemes( clicktime, &current, &next ) )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ if ( current && !next )
+ {
+ // cap movement
+ current->AddEndTime( endtime - clicktime );
+ }
+ else if ( !current && next )
+ {
+ // cap movement
+ next->AddStartTime( endtime - clicktime );
+ }
+ else
+ {
+ // cap movement
+ endtime = min( endtime, next->GetEndTime() - 1.0f / GetPixelsPerSecond() );
+ endtime = max( endtime, current->GetStartTime() + 1.0f / GetPixelsPerSecond() );
+
+ current->SetEndTime( endtime );
+ next->SetStartTime( endtime );
+ }
+
+ RealignWordsToPhonemes( false );
+ CleanupWordsAndPhonemes( false );
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dirty -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SetDirty( bool dirty, bool clearundo /*=true*/ )
+{
+ m_WorkFile.m_bDirty = dirty;
+
+ if ( !dirty && clearundo )
+ {
+ WipeUndo();
+ redraw();
+ }
+
+ SetPrefix( dirty ? "* " : "" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::GetDirty( void )
+{
+ return m_WorkFile.m_bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditInsertPhonemeBefore( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CPhonemeTag *cp = GetSelectedPhoneme();
+ if ( !cp )
+ return;
+
+ float gap = GetTimeGapToNextPhoneme( false, cp );
+ if ( gap < MINIMUM_PHONEME_GAP )
+ {
+ Con_Printf( "Can't insert before, gap of %.2f ms is too small\n", 1000.0f * gap );
+ return;
+ }
+
+ // Don't have really long phonemes
+ gap = min( gap, DEFAULT_PHONEME_LENGTH );
+
+ CWordTag *word = m_Tags.GetWordForPhoneme( cp );
+ if ( !word )
+ {
+ Con_Printf( "EditInsertPhonemeBefore: phoneme not a member of any known word!!!\n" );
+ return;
+ }
+
+ int clicked = word->IndexOfPhoneme( cp );
+ if ( clicked < 0 )
+ {
+ Con_Printf( "EditInsertPhonemeBefore: phoneme not a member of any specified word!!!\n" );
+ Assert( 0 );
+ return;
+ }
+
+ CPhonemeTag phoneme;
+
+ CPhonemeParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
+ strcpy( params.m_szName, "" );
+
+ int iret = PhonemeProperties( &params );
+ SetFocus( (HWND)getHandle() );
+ if ( !iret )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ phoneme.SetPhonemeCode( TextToPhoneme( params.m_szName ) );
+ phoneme.SetTag( params.m_szName );
+
+ phoneme.SetEndTime( cp->GetStartTime() );
+ phoneme.SetStartTime( cp->GetStartTime() - gap );
+ phoneme.m_bSelected = true;
+ cp->m_bSelected = false;
+
+ word->m_Phonemes.InsertBefore( clicked, new CPhonemeTag( phoneme ) );
+
+ PushRedo();
+
+ // Add it
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditInsertPhonemeAfter( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CPhonemeTag *cp = GetSelectedPhoneme();
+ if ( !cp )
+ return;
+
+ float gap = GetTimeGapToNextPhoneme( true, cp );
+ if ( gap < MINIMUM_PHONEME_GAP )
+ {
+ Con_Printf( "Can't insert after, gap of %.2f ms is too small\n", 1000.0f * gap );
+ return;
+ }
+
+ // Don't have really long phonemes
+ gap = min( gap, DEFAULT_PHONEME_LENGTH );
+
+ CWordTag *word = m_Tags.GetWordForPhoneme( cp );
+ if ( !word )
+ {
+ Con_Printf( "EditInsertPhonemeAfter: phoneme not a member of any known word!!!\n" );
+ return;
+ }
+
+ int clicked = word->IndexOfPhoneme( cp );
+ if ( clicked < 0 )
+ {
+ Con_Printf( "EditInsertPhonemeAfter: phoneme not a member of any specified word!!!\n" );
+ Assert( 0 );
+ return;
+ }
+
+ CPhonemeTag phoneme;
+
+ CPhonemeParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
+ strcpy( params.m_szName, "" );
+
+ int iret = PhonemeProperties( &params );
+ SetFocus( (HWND)getHandle() );
+
+ if ( !iret )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ phoneme.SetPhonemeCode( TextToPhoneme( params.m_szName ) );
+ phoneme.SetTag( params.m_szName );
+
+ phoneme.SetEndTime( cp->GetEndTime() + gap );
+ phoneme.SetStartTime( cp->GetEndTime() );
+ phoneme.m_bSelected = true;
+ cp->m_bSelected = false;
+
+ word->m_Phonemes.InsertAfter( clicked, new CPhonemeTag( phoneme ) );
+
+ PushRedo();
+
+ // Add it
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditInsertWordBefore( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CWordTag *cw = GetSelectedWord();
+ if ( !cw )
+ return;
+
+ float gap = GetTimeGapToNextWord( false, cw );
+ if ( gap < MINIMUM_WORD_GAP )
+ {
+ Con_Printf( "Can't insert before, gap of %.2f ms is too small\n", 1000.0f * gap );
+ return;
+ }
+
+ // Don't have really long words
+ gap = min( gap, DEFAULT_WORD_LENGTH );
+
+ int clicked = IndexOfWord( cw );
+ if ( clicked < 0 )
+ {
+ Con_Printf( "EditInsertWordBefore: word not in sentence!!!\n" );
+ Assert( 0 );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Insert Word" );
+ strcpy( params.m_szPrompt, "Word:" );
+ strcpy( params.m_szInputText, "" );
+
+ params.m_nLeft = -1;
+ params.m_nTop = -1;
+
+ params.m_bPositionDialog = true;
+ if ( params.m_bPositionDialog )
+ {
+ RECT rcWord;
+ GetWordRect( cw, rcWord );
+
+ // Convert to screen coords
+ POINT pt;
+ pt.x = rcWord.left;
+ pt.y = rcWord.top;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ params.m_nLeft = pt.x;
+ params.m_nTop = pt.y;
+ }
+
+ int iret = InputProperties( &params );
+ SetFocus( (HWND)getHandle() );
+ if ( !iret )
+ {
+ return;
+ }
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ return;
+ }
+
+ int wordCount = CSentence::CountWords( params.m_szInputText );
+ if ( wordCount > 1 )
+ {
+ Con_Printf( "Can only insert one word at a time, %s has %i words in it!\n",
+ params.m_szInputText, wordCount );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ CWordTag newword;
+
+ newword.SetWord( params.m_szInputText );
+
+ newword.m_flEndTime = cw->m_flStartTime;
+ newword.m_flStartTime = cw->m_flStartTime - gap;
+ newword.m_bSelected = true;
+ cw->m_bSelected = false;
+
+ m_Tags.m_Words.InsertBefore( clicked, new CWordTag( newword ) );
+
+ PushRedo();
+
+ // Add it
+ redraw();
+
+ // Jump to phoneme insertion UI
+ EditInsertFirstPhonemeOfWord();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditInsertWordAfter( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CWordTag *cw = GetSelectedWord();
+ if ( !cw )
+ return;
+
+ float gap = GetTimeGapToNextWord( true, cw );
+ if ( gap < MINIMUM_WORD_GAP )
+ {
+ Con_Printf( "Can't insert after, gap of %.2f ms is too small\n", 1000.0f * gap );
+ return;
+ }
+
+ // Don't have really long words
+ gap = min( gap, DEFAULT_WORD_LENGTH );
+
+ int clicked = IndexOfWord( cw );
+ if ( clicked < 0 )
+ {
+ Con_Printf( "EditInsertWordBefore: word not in sentence!!!\n" );
+ Assert( 0 );
+ return;
+ }
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Insert Word" );
+ strcpy( params.m_szPrompt, "Word:" );
+ strcpy( params.m_szInputText, "" );
+
+ params.m_nLeft = -1;
+ params.m_nTop = -1;
+
+ params.m_bPositionDialog = true;
+ if ( params.m_bPositionDialog )
+ {
+ RECT rcWord;
+ GetWordRect( cw, rcWord );
+
+ // Convert to screen coords
+ POINT pt;
+ pt.x = rcWord.left;
+ pt.y = rcWord.top;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ params.m_nLeft = pt.x;
+ params.m_nTop = pt.y;
+ }
+
+ int iret = InputProperties( &params );
+ SetFocus( (HWND)getHandle() );
+ if ( !iret )
+ {
+ return;
+ }
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ return;
+ }
+
+ int wordCount = CSentence::CountWords( params.m_szInputText );
+ if ( wordCount > 1 )
+ {
+ Con_Printf( "Can only insert one word at a time, %s has %i words in it!\n",
+ params.m_szInputText, wordCount );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ CWordTag newword;
+
+ newword.SetWord( params.m_szInputText );
+
+ newword.m_flEndTime = cw->m_flEndTime + gap;
+ newword.m_flStartTime = cw->m_flEndTime;
+ newword.m_bSelected = true;
+ cw->m_bSelected = false;
+
+ CWordTag *w = new CWordTag( newword );
+ Assert( w );
+ if ( w )
+ {
+ m_Tags.m_Words.InsertAfter( clicked, w );
+ }
+
+ PushRedo();
+
+ // Add it
+ redraw();
+
+ EditInsertFirstPhonemeOfWord();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditDeletePhoneme( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount < 1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ for ( int i = m_Tags.m_Words.Size() - 1; i >= 0; i-- )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = word->m_Phonemes.Size() - 1; j >= 0; j-- )
+ {
+ CPhonemeTag *p = word->m_Phonemes[ j ];
+ if ( !p || !p->m_bSelected )
+ continue;
+
+ // Delete it
+ word->m_Phonemes.Remove( j );
+ }
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditDeleteWord( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedWordCount < 1 )
+ {
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ for ( int i = m_Tags.m_Words.Size() - 1; i >= 0; i-- )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word || !word->m_bSelected )
+ continue;
+
+ m_Tags.m_Words.Remove( i );
+ }
+
+ PushRedo();
+
+ redraw();
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::PlayEditedWave( bool selection /* = false */ )
+{
+ StopPlayback();
+
+ if ( !m_pWaveFile )
+ return;
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ SaveLinguisticData();
+
+ SetScrubTime( 0.0f );
+ SetScrubTargetTime( m_pWaveFile->GetRunningLength() );
+}
+
+typedef struct channel_s
+{
+ int leftvol;
+ int rightvol;
+ int rleftvol;
+ int rrightvol;
+ float pitch;
+} channel_t;
+
+bool PhonemeEditor::CreateCroppedWave( char const *filename, int startsample, int endsample )
+{
+ Assert( sound );
+
+ CAudioWaveOutput *pWaveOutput = ( CAudioWaveOutput * )sound->GetAudioOutput();
+ if ( !pWaveOutput )
+ return false;
+
+ CAudioSource *wave = sound->LoadSound( m_WorkFile.m_szWaveFile );
+ if ( !wave )
+ return false;
+
+ CAudioMixer *pMixer = wave->CreateMixer();
+ if ( !pMixer )
+ return false;
+
+ // Create out put file
+ OutFileRIFF riffout( filename, io_out );
+ // Create output iterator
+ IterateOutputRIFF store( riffout );
+
+ WAVEFORMATEX format;
+ format.cbSize = sizeof( format );
+
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nAvgBytesPerSec = (int)wave->SampleRate();
+ format.nChannels = 1;
+ format.wBitsPerSample = 8;
+ format.nSamplesPerSec = (int)wave->SampleRate();
+ format.nBlockAlign = 1; // (int)wave->SampleSize();
+
+ store.ChunkWrite( WAVE_FMT, &format, sizeof( format ) );
+
+ // Pull in data and write it out
+
+ int currentsample = 0;
+
+ store.ChunkStart( WAVE_DATA );
+
+ // need a bit of space
+ short samples[ 2 ];
+ channel_t channel;
+ channel.leftvol = 255;
+ channel.rightvol = 255;
+ channel.pitch = 1.0;
+
+ while ( 1 )
+ {
+ pWaveOutput->m_audioDevice.MixBegin();
+
+ if ( !pMixer->MixDataToDevice( &pWaveOutput->m_audioDevice, &channel, currentsample, 1, wave->SampleRate(), true ) )
+ break;
+
+ pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, 1 );
+
+ currentsample = pMixer->GetSamplePosition();
+
+ if ( currentsample >= startsample && currentsample <= endsample )
+ {
+ // left + right (2 channels ) * 16 bits
+ float s1 = (float)( samples[ 0 ] >> 8 );
+ float s2 = (float)( samples[ 1 ] >> 8 );
+
+ float avg = ( s1 + s2 ) / 2.0f;
+ unsigned char chopped = (unsigned char)( avg + 127.0f );
+
+ store.ChunkWriteData( &chopped, sizeof( byte ) );
+ }
+ }
+
+ store.ChunkFinish();
+
+ delete pMixer;
+ delete wave;
+
+ return true;
+}
+
+void PhonemeEditor::SentenceFromString( CSentence& sentence, char const *str )
+{
+ sentence.Reset();
+
+ if ( !str || !str[0] || CSentence::CountWords( str ) == 0 )
+ {
+ return;
+ }
+
+ char word[ 256 ];
+ unsigned char const *in = (unsigned char *)str;
+ char *out = word;
+
+ while ( *in )
+ {
+ if ( *in > 32 )
+ {
+ *out++ = *in++;
+ }
+ else
+ {
+ *out = 0;
+
+ while ( *in && *in <= 32 )
+ {
+ in++;
+ }
+
+ if ( strlen( word ) > 0 )
+ {
+ CWordTag *w = new CWordTag( (char *)word );
+ Assert( w );
+ if ( w )
+ {
+ sentence.m_Words.AddToTail( w );
+ }
+ }
+
+ out = word;
+ }
+ }
+
+ *out = 0;
+ if ( strlen( word ) > 0 )
+ {
+ CWordTag *w = new CWordTag( (char *)word );
+ Assert( w );
+ if ( w )
+ {
+ sentence.m_Words.AddToTail( w );
+ }
+ }
+
+ sentence.SetText( str );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::RedoPhonemeExtractionSelected( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( !CheckSpeechAPI() )
+ return;
+
+ if ( !m_pWaveFile )
+ {
+ Con_Printf( "Can't redo extraction, no wavefile loaded!\n" );
+ Assert( 0 );
+ return;
+ }
+
+ if ( !m_bSelectionActive )
+ {
+ Con_Printf( "Please select a portion of the .wav from which to re-extract phonemes\n" );
+ return;
+ }
+
+ // Now copy data back into original list, offsetting by samplestart time
+ float numsamples = m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate();
+ float selectionstarttime = 0.0f;
+ if ( numsamples > 0.0f )
+ {
+ // Convert sample #'s to time
+ selectionstarttime = ( m_nSelection[ 0 ] / numsamples ) * m_pWaveFile->GetRunningLength();
+ selectionstarttime = max( 0.0f, selectionstarttime );
+ }
+ else
+ {
+ Con_Printf( "Original .wav file %s has no samples!!!\n", m_WorkFile.m_szWaveFile );
+ return;
+ }
+
+ int i;
+ // Create input array of just selected words
+ CSentence m_InputWords;
+ CSentence m_Results;
+
+ CountSelected();
+
+ bool usingselection = true;
+
+ if ( m_nSelectedWordCount == 0 )
+ {
+ // Allow user to type in text
+ // Build word string
+ char wordstring[ 1024 ];
+ strcpy( wordstring, "" );
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Phrase Word List" );
+ strcpy( params.m_szPrompt, "Phrase" );
+
+ strcpy( params.m_szInputText, wordstring );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Edit word list: No words entered!\n" );
+ return;
+ }
+
+ SentenceFromString( m_InputWords, params.m_szInputText );
+
+ if ( m_InputWords.m_Words.Size() == 0 )
+ {
+ Con_Printf( "You must either select words, or type in a set of words in order to extract phonemes!\n" );
+ return;
+ }
+
+ usingselection = false;
+ }
+ else
+ {
+ if ( !AreSelectedWordsContiguous() )
+ {
+ Con_Printf( "Can only redo extraction on a contiguous subset of words\n" );
+ return;
+ }
+
+ char temp[ 4096 ];
+ bool killspace = false;
+ Q_strncpy( temp, m_InputWords.GetText(), sizeof( temp ) );
+
+ // Iterate existing words, looking for contiguous selected words
+ for ( i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word || !word->m_bSelected )
+ continue;
+
+ // Now add "clean slate" to input list
+ m_InputWords.m_Words.AddToTail( new CWordTag( *word ) );
+
+ Q_strncat( temp, word->GetWord(), sizeof( temp ), COPY_ALL_CHARACTERS );
+ Q_strncat( temp, " ", sizeof( temp ), COPY_ALL_CHARACTERS );
+ killspace = true;
+ }
+
+ // Kill terminal space character
+ int len = Q_strlen( temp );
+ if ( killspace && ( len >= 1 ) )
+ {
+ Assert( temp[ len -1 ] == ' ' );
+ temp[ len - 1 ] = 0;
+ }
+
+ m_InputWords.SetText( temp );
+ }
+
+ m_nLastExtractionResult = SR_RESULT_NORESULT;
+
+ char szCroppedFile[ 512 ];
+ char szBaseFile[ 512 ];
+ Q_StripExtension( m_WorkFile.m_szWaveFile, szBaseFile, sizeof( szBaseFile ) );
+ Q_snprintf( szCroppedFile, sizeof( szCroppedFile ), "%s%s_work1.wav", m_WorkFile.m_szBasePath, szBaseFile );
+
+ filesystem->RemoveFile( szCroppedFile, "GAME" );
+
+ if ( !CreateCroppedWave( szCroppedFile, m_nSelection[ 0 ], m_nSelection[ 1 ] ) )
+ {
+ Con_Printf( "Unable to create cropped wave file %s from samples %i to %i\n",
+ szCroppedFile,
+ m_nSelection[ 0 ],
+ m_nSelection[ 1 ] );
+ return;
+ }
+
+ CAudioSource *m_pCroppedWave = sound->LoadSound( szCroppedFile );
+ if ( !m_pCroppedWave )
+ {
+ Con_Printf( "Unable to load cropped wave file %s from samples %i to %i\n",
+ szCroppedFile,
+ m_nSelection[ 0 ],
+ m_nSelection[ 1 ] );
+ return;
+ }
+
+ // Save any pending stuff
+ SaveLinguisticData();
+
+ // Store off copy of complete sentence
+ m_TagsExt = m_Tags;
+
+ char filename[ 512 ];
+ Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, szCroppedFile );
+
+ m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
+ filename,
+ (int)( m_pCroppedWave->GetRunningLength() * m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize() ),
+ Con_Printf,
+ m_InputWords,
+ m_Results );
+
+ if ( m_InputWords.m_Words.Size() != m_Results.m_Words.Size() )
+ {
+ Con_Printf( "Extraction returned %i words, source had %i, try adjusting selection\n",
+ m_Results.m_Words.Size(), m_InputWords.m_Words.Size() );
+
+ filesystem->RemoveFile( filename, "GAME" );
+
+ redraw();
+ return;
+ }
+
+ float bytespersecond = m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize();
+
+ // Tracker 57389:
+ // Total hack to fix a bug where the Lipsinc extractor is messing up the # channels on 16 bit stereo waves
+ if ( m_pPhonemeExtractor->GetAPIType() == SPEECH_API_LIPSINC &&
+ m_pCroppedWave->IsStereoWav() &&
+ m_pCroppedWave->SampleSize() == 16 )
+ {
+ bytespersecond *= 2.0f;
+ }
+
+ // Now convert byte offsets to times
+ for ( i = 0; i < m_Results.m_Words.Size(); i++ )
+ {
+ CWordTag *tag = m_Results.m_Words[ i ];
+ Assert( tag );
+ if ( !tag )
+ continue;
+
+ tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
+ tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
+
+ for ( int j = 0; j < tag->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *ptag = tag->m_Phonemes[ j ];
+ Assert( ptag );
+ if ( !ptag )
+ continue;
+
+ ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
+ ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
+ }
+ }
+
+ if ( usingselection )
+ {
+ // Copy data into m_TagsExt, offseting times by selectionstarttime
+ CWordTag *from;
+ CWordTag *to;
+
+ int fromWord = 0;
+
+ for ( i = 0; i < m_TagsExt.m_Words.Size() ; i++ )
+ {
+ to = m_TagsExt.m_Words[ i ];
+ if ( !to || !to->m_bSelected )
+ continue;
+
+ // Found start of contiguous run
+ if ( fromWord >= m_Results.m_Words.Size() )
+ break;
+
+ from = m_Results.m_Words[ fromWord++ ];
+ Assert( from );
+ if ( !from )
+ continue;
+
+ // Remove all phonemes from destination
+ while ( to->m_Phonemes.Size() > 0 )
+ {
+ CPhonemeTag *p = to->m_Phonemes[ 0 ];
+ Assert( p );
+ to->m_Phonemes.Remove( 0 );
+ delete p;
+ }
+
+ // Now copy phonemes from source
+ for ( int j = 0; j < from->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *fromPhoneme = from->m_Phonemes[ j ];
+ Assert( fromPhoneme );
+ if ( !fromPhoneme )
+ continue;
+
+ CPhonemeTag newPhoneme( *fromPhoneme );
+ // Offset start time
+ newPhoneme.AddStartTime( selectionstarttime );
+ newPhoneme.AddEndTime( selectionstarttime );
+
+ // Add it back in with corrected timing data
+ CPhonemeTag *p = new CPhonemeTag( newPhoneme );
+ Assert( p );
+ if ( p )
+ {
+ to->m_Phonemes.AddToTail( p );
+ }
+ }
+
+ // Done
+ if ( fromWord >= m_Results.m_Words.Size() )
+ break;
+ }
+
+ }
+ else
+ {
+ // Find word just before starting point of selection and
+ // place input words into list starting that that point
+
+ int startWord = 0;
+
+ CWordTag *firstWordOfPhrase = m_Results.m_Words[ 0 ];
+ Assert( firstWordOfPhrase );
+
+ for ( ; startWord < m_TagsExt.m_Words.Size(); startWord++ )
+ {
+ CWordTag *w = m_TagsExt.m_Words[ startWord ];
+ Assert( w );
+ if ( !w )
+ continue;
+
+ if ( w->m_flStartTime > firstWordOfPhrase->m_flStartTime + selectionstarttime )
+ break;
+ }
+
+ for ( i = 0; i < m_Results.m_Words.Size(); i++ )
+ {
+ CWordTag *from = m_Results.m_Words[ i ];
+ Assert( from );
+ if ( !from )
+ continue;
+
+ CWordTag *to = new CWordTag( *from );
+ Assert( to );
+
+ to->m_flStartTime += selectionstarttime;
+ to->m_flEndTime += selectionstarttime;
+
+ // Now adjust phoneme times
+ for ( int j = 0; j < to->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *toPhoneme = to->m_Phonemes[ j ];
+ Assert( toPhoneme );
+ if ( !toPhoneme )
+ continue;
+
+ // Offset start time
+ toPhoneme->AddStartTime( selectionstarttime );
+ toPhoneme->AddEndTime( selectionstarttime );
+ }
+
+ m_TagsExt.m_Words.InsertBefore( startWord++, to );
+ }
+ }
+
+ Con_Printf( "Cleaning up...\n" );
+ filesystem->RemoveFile( filename, "GAME" );
+
+ SetFocus( (HWND)getHandle() );
+
+ redraw();
+}
+
+void PhonemeEditor::RedoPhonemeExtraction( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( !CheckSpeechAPI() )
+ return;
+
+ m_nLastExtractionResult = SR_RESULT_NORESULT;
+
+ if ( !m_pWaveFile )
+ return;
+
+ SaveLinguisticData();
+
+ // Send m_WorkFile.m_szWorkingFile to extractor and retrieve resulting data
+ //
+
+ m_TagsExt.Reset();
+
+ Assert( m_pPhonemeExtractor );
+
+ char filename[ 512 ];
+ Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
+
+ m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
+ filename,
+ (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize() ),
+ Con_Printf,
+ m_Tags,
+ m_TagsExt );
+
+ float bytespersecond = m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize();
+
+ // Now convert byte offsets to times
+ int i;
+ for ( i = 0; i < m_TagsExt.m_Words.Size(); i++ )
+ {
+ CWordTag *tag = m_TagsExt.m_Words[ i ];
+ Assert( tag );
+ if ( !tag )
+ continue;
+
+ tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
+ tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
+
+ for ( int j = 0; j < tag->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *ptag = tag->m_Phonemes[ j ];
+ Assert( ptag );
+ if ( !ptag )
+ continue;
+
+ ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
+ ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
+ }
+ }
+
+ SetFocus( (HWND)getHandle() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Deselect( void )
+{
+ m_nSelection[ 0 ] = m_nSelection[ 1 ] = 0;
+ m_bSelectionActive = false;
+}
+
+void PhonemeEditor::ITER_SelectSpanningWords( CWordTag *word, float amount )
+{
+ Assert( word );
+ word->m_bSelected = false;
+
+ if ( !m_bSelectionActive )
+ return;
+
+ if ( !m_pWaveFile )
+ return;
+
+ float numsamples = m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate();
+ if ( numsamples > 0.0f )
+ {
+ // Convert sample #'s to time
+ float starttime = ( m_nSelection[ 0 ] / numsamples ) * m_pWaveFile->GetRunningLength();
+ float endtime = ( m_nSelection[ 1 ] / numsamples ) * m_pWaveFile->GetRunningLength();
+
+ if ( word->m_flEndTime >= starttime &&
+ word->m_flStartTime <= endtime )
+ {
+ word->m_bSelected = true;
+
+ m_bWordsActive = true;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : start -
+// end -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SelectSamples( int start, int end )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ // Make sure order is correct
+ if ( end < start )
+ {
+ int temp = end;
+ end = start;
+ start = temp;
+ }
+
+ Deselect();
+
+ m_nSelection[ 0 ] = start;
+ m_nSelection[ 1 ] = end;
+ m_bSelectionActive = true;
+
+ // Select any words that span the selection
+ //
+ TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
+
+ redraw();
+}
+
+void PhonemeEditor::FinishMoveSelection( int startx, int mx )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ int sampleStart = GetSampleForMouse( startx );
+ int sampleEnd = GetSampleForMouse( mx );
+
+ int delta = sampleEnd - sampleStart;
+
+ for ( int i = 0; i < 2; i++ )
+ {
+ m_nSelection[ i ] += delta;
+ }
+
+ // Select any words that span the selection
+ //
+ TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
+
+ redraw();
+}
+
+void PhonemeEditor::FinishMoveSelectionStart( int startx, int mx )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ int sampleStart = GetSampleForMouse( startx );
+ int sampleEnd = GetSampleForMouse( mx );
+
+ int delta = sampleEnd - sampleStart;
+
+ m_nSelection[ 0 ] += delta;
+
+ if ( m_nSelection[ 0 ] >= m_nSelection[ 1 ] )
+ {
+ Deselect();
+ }
+
+ // Select any words that span the selection
+ //
+ TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
+
+ redraw();
+}
+
+void PhonemeEditor::FinishMoveSelectionEnd( int startx, int mx )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ int sampleStart = GetSampleForMouse( startx );
+ int sampleEnd = GetSampleForMouse( mx );
+
+ int delta = sampleEnd - sampleStart;
+
+ m_nSelection[ 1 ] += delta;
+
+ if ( m_nSelection[ 1 ] <= m_nSelection[ 0 ] )
+ {
+ Deselect();
+ }
+
+ // Select any words that span the selection
+ //
+ TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : startx -
+// mx -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::FinishSelect( int startx, int mx )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ // Don't select really small areas
+ if ( abs( startx - mx ) < 2 )
+ return;
+
+ int sampleStart = GetSampleForMouse( startx );
+ int sampleEnd = GetSampleForMouse( mx );
+
+ SelectSamples( sampleStart, sampleEnd );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::IsMouseOverSamples( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ // Deterime if phoneme boundary is under the cursor
+ //
+ if ( !m_pWaveFile )
+ return false;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Over tag
+ if ( my >= TAG_TOP && my <= TAG_BOTTOM )
+ return false;
+
+ if ( IsMouseOverPhonemeRow( my ) )
+ return false;
+
+ if ( IsMouseOverWordRow( my ) )
+ return false;
+
+ RECT rcWord;
+ GetWordTrayTopBottom( rcWord );
+ RECT rcPhoneme;
+ GetPhonemeTrayTopBottom( rcPhoneme );
+
+ if ( my < rcWord.bottom )
+ return false;
+
+ if ( my > rcPhoneme.top )
+ return false;
+
+ return true;
+}
+
+void PhonemeEditor::GetScreenStartAndEndTime( float &starttime, float& endtime )
+{
+ starttime = m_nLeftOffset / GetPixelsPerSecond();
+ endtime = w2() / GetPixelsPerSecond() + starttime;
+}
+
+float PhonemeEditor::GetTimePerPixel( void )
+{
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ float starttime, endtime;
+ GetScreenStartAndEndTime( starttime, endtime );
+
+ if ( rc.right - rc.left <= 0 )
+ {
+ return ( endtime - starttime );
+ }
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+ return timeperpixel;
+}
+
+int PhonemeEditor::GetPixelForSample( int sample )
+{
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( !m_pWaveFile )
+ return rc.left;
+
+ // Determine start/stop positions
+ int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
+ if ( totalsamples <= 0 )
+ {
+ return rc.left;
+ }
+
+ float starttime, endtime;
+ GetScreenStartAndEndTime( starttime, endtime );
+
+ float sampleFrac = (float)sample / (float)totalsamples;
+ float sampleTime = sampleFrac * (float)m_pWaveFile->GetRunningLength();
+
+ if ( endtime - starttime < 0.0f )
+ {
+ return rc.left;
+ }
+
+ float windowFrac = ( sampleTime - starttime ) / ( endtime - starttime );
+
+ return rc.left + (int)( windowFrac * ( rc.right - rc.left ) );
+}
+
+int PhonemeEditor::GetSampleForMouse( int mx )
+{
+ if ( !m_pWaveFile )
+ return 0;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
+
+ float starttime, endtime;
+ GetScreenStartAndEndTime( starttime, endtime );
+
+ if ( GetPixelsPerSecond() <= 0 )
+ return 0;
+
+ // Start and end times
+ float clickTime = (float)mx / GetPixelsPerSecond() + starttime;
+
+ // What sample do these correspond to
+ if ( (float)m_pWaveFile->GetRunningLength() <= 0.0f )
+ return 0;
+
+ int sampleNumber = (int) ( (float)totalsamples * clickTime / (float)m_pWaveFile->GetRunningLength() );
+
+ return sampleNumber;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::IsMouseOverSelection( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ if ( !m_pWaveFile )
+ return false;
+
+ if ( !m_bSelectionActive )
+ return false;
+
+ if ( !IsMouseOverSamples( mx, my ) )
+ return false;
+
+ int sampleNumber = GetSampleForMouse( mx );
+
+ if ( sampleNumber >= m_nSelection[ 0 ] - 3 &&
+ sampleNumber <= m_nSelection[ 1 ] + 3 )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool PhonemeEditor::IsMouseOverSelectionStartEdge( mxEvent *event )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ if ( !m_pWaveFile )
+ return false;
+
+ int mx, my;
+ mx = (short)event->x;
+ my = (short)event->y;
+
+ if ( !(event->modifiers & mxEvent::KeyCtrl ) )
+ return false;
+
+ if ( !IsMouseOverSelection( mx, my ) )
+ return false;
+
+ int sample = GetSampleForMouse( mx );
+
+ int mouse_tolerance = 5;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ float timeperpixel = GetTimePerPixel();
+
+ int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
+
+ if ( abs( sample - m_nSelection[ 0 ] ) < mouse_tolerance * samplesperpixel )
+ {
+ return true;
+ }
+ return false;
+}
+
+bool PhonemeEditor::IsMouseOverSelectionEndEdge( mxEvent *event )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ if ( !m_pWaveFile )
+ return false;
+
+ int mx, my;
+ mx = (short)event->x;
+ my = (short)event->y;
+
+ if ( !(event->modifiers & mxEvent::KeyCtrl ) )
+ return false;
+
+ if ( !IsMouseOverSelection( mx, my ) )
+ return false;
+
+ int sample = GetSampleForMouse( mx );
+
+ int mouse_tolerance = 5;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( GetPixelsPerSecond() <= 0.0f )
+ return false;
+
+ if ( ( rc.right - rc.left ) <= 0 )
+ return false;
+
+ // Determine start/stop positions
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+ int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
+
+ if ( abs( sample - m_nSelection[ 1 ] ) < mouse_tolerance * samplesperpixel )
+ {
+ return true;
+ }
+ return false;
+}
+
+void PhonemeEditor::OnImport()
+{
+ char filename[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
+ {
+ return;
+ }
+
+ ImportValveDataChunk( filename );
+}
+
+void PhonemeEditor::OnExport()
+{
+ if ( !m_pWaveFile )
+ return;
+
+ char filename[ 512 ];
+ if ( !FacePoser_ShowSaveFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
+ {
+ return;
+ }
+
+ Q_SetExtension( filename, WORD_DATA_EXTENSION, sizeof( filename ) );
+
+ ExportValveDataChunk( filename );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : store -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::StoreValveDataChunk( IterateOutputRIFF& store )
+{
+ // Buffer and dump data
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ m_Tags.SaveToBuffer( buf );
+
+ // Copy into store
+ store.ChunkWriteData( buf.Base(), buf.TellPut() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *tempfile -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ExportValveDataChunk( char const *tempfile )
+{
+ if ( m_Tags.m_Words.Count() <= 0 )
+ {
+ Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Sentence has no word data\n" );
+ return;
+ }
+
+ FileHandle_t fh = filesystem->Open( tempfile, "wb" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Unable to write to %s (read-only?)\n", tempfile );
+ return;
+ }
+ else
+ {
+ // Buffer and dump data
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ m_Tags.SaveToBuffer( buf );
+
+ filesystem->Write( buf.Base(), buf.TellPut(), fh );
+ filesystem->Close(fh);
+
+ Con_Printf( "Exported %i words to %s\n", m_Tags.m_Words.Count(), tempfile );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *tempfile -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ImportValveDataChunk( char const *tempfile )
+{
+ FileHandle_t fh = filesystem->Open( tempfile, "rb" );
+ if ( !fh )
+ {
+ Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: Unable to read from %s\n", tempfile );
+ return;
+ }
+
+ int len = filesystem->Size( fh );
+ if ( len <= 4 )
+ {
+ Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: File %s has length 0\n", tempfile );
+ return;
+ }
+
+ ClearExtracted();
+
+ unsigned char *buf = new unsigned char[ len + 1 ];
+
+ filesystem->Read( buf, len, fh );
+ filesystem->Close( fh );
+
+ m_TagsExt.InitFromDataChunk( (void *)( buf ), len );
+
+ delete[] buf;
+
+ Con_Printf( "Imported %i words from %s\n", m_TagsExt.m_Words.Count(), tempfile );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy file over, but update phoneme lump with new data
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SaveLinguisticData( void )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ InFileRIFF riff( m_WorkFile.m_szWaveFile, io_in );
+ Assert( riff.RIFFName() == RIFF_WAVE );
+
+ // set up the iterator for the whole file (root RIFF is a chunk)
+ IterateRIFF walk( riff, riff.RIFFSize() );
+
+ char fullout[ 512 ];
+ Q_snprintf( fullout, sizeof( fullout ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
+
+ OutFileRIFF riffout( fullout, io_out );
+
+ IterateOutputRIFF store( riffout );
+
+ bool formatset = false;
+ WAVEFORMATEX format;
+
+ bool wordtrackwritten = false;
+
+ // Walk input chunks and copy to output
+ while ( walk.ChunkAvailable() )
+ {
+ unsigned int originalPos = store.ChunkGetPosition();
+
+ store.ChunkStart( walk.ChunkName() );
+
+ bool skipchunk = false;
+
+ switch ( walk.ChunkName() )
+ {
+ case WAVE_VALVEDATA:
+ // Overwrite data
+ StoreValveDataChunk( store );
+ wordtrackwritten = true;
+ break;
+ case WAVE_FMT:
+ {
+ formatset = true;
+
+ char *buffer = new char[ walk.ChunkSize() ];
+ Assert( buffer );
+ walk.ChunkRead( buffer );
+
+ format = *(WAVEFORMATEX *)buffer;
+
+ store.ChunkWriteData( buffer, walk.ChunkSize() );
+
+ delete[] buffer;
+ }
+ break;
+ case WAVE_DATA:
+ {
+ Assert( formatset );
+
+ char *buffer = new char[ walk.ChunkSize() ];
+ Assert( buffer );
+
+ walk.ChunkRead( buffer );
+ // Resample it
+ ResampleChunk( store, (void *)&format, walk.ChunkName(), buffer, walk.ChunkSize() );
+
+ delete[] buffer;
+ }
+ break;
+ default:
+ store.CopyChunkData( walk );
+ break;
+ }
+
+ store.ChunkFinish();
+ if ( skipchunk )
+ {
+ store.ChunkSetPosition( originalPos );
+ }
+
+ walk.ChunkNext();
+ }
+
+ if ( !wordtrackwritten )
+ {
+ store.ChunkStart( WAVE_VALVEDATA );
+ StoreValveDataChunk( store );
+ store.ChunkFinish();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy phoneme data in from wave file we sent for resprocessing
+//-----------------------------------------------------------------------------
+void PhonemeEditor::RetrieveLinguisticData( void )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ m_Tags.Reset();
+
+ ReadLinguisticTags();
+
+ redraw();
+}
+
+bool PhonemeEditor::StopPlayback( void )
+{
+ bool bret = false;
+ if ( m_pWaveFile )
+ {
+ SetScrubTargetTime( m_flScrub );
+
+ if ( sound->IsSoundPlaying( m_pMixer ) )
+ {
+ sound->StopAll();
+ bret = true;
+ }
+ }
+
+ sound->Flush();
+
+ return bret;
+}
+
+CPhonemeTag *PhonemeEditor::GetPhonemeTagUnderMouse( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return NULL;
+
+ if ( !m_pWaveFile )
+ return NULL;
+
+ // FIXME: Don't read from file, read from arrays after LISET finishes
+ // Deterime if phoneme boundary is under the cursor
+ //
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( !IsMouseOverPhonemeRow( my ) )
+ return NULL;
+
+ if ( GetPixelsPerSecond() <= 0 )
+ return NULL;
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ if ( endtime - starttime <= 0.0f )
+ return NULL;
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ Assert( word );
+ if ( !word )
+ continue;
+
+ for ( int k = 0; k < word->m_Phonemes.Size(); k++ )
+ {
+ CPhonemeTag *pPhoneme = word->m_Phonemes[ k ];
+ Assert( pPhoneme );
+ if ( !pPhoneme )
+ continue;
+
+ float t1 = pPhoneme->GetStartTime();
+ float t2 = pPhoneme->GetEndTime();
+
+ float frac1 = ( t1 - starttime ) / ( endtime - starttime );
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+
+ frac1 = min( 1.0f, frac1 );
+ frac1 = max( 0.0f, frac1 );
+ frac2 = min( 1.0f, frac2 );
+ frac2 = max( 0.0f, frac2 );
+
+ if ( frac1 == frac2 )
+ continue;
+
+ int x1 = ( int )( frac1 * w2() );
+ int x2 = ( int )( frac2 * w2() );
+
+ if ( mx >= x1 && mx <= x2 )
+ {
+ return pPhoneme;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+CWordTag *PhonemeEditor::GetWordTagUnderMouse( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return NULL;
+
+ // Deterime if phoneme boundary is under the cursor
+ //
+ if ( !m_pWaveFile )
+ return NULL;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( !IsMouseOverWordRow( my ) )
+ return NULL;
+
+ if ( GetPixelsPerSecond() <= 0 )
+ return NULL;
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ if ( endtime - starttime <= 0.0f )
+ return NULL;
+
+ for ( int k = 0; k < m_Tags.m_Words.Size(); k++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ k ];
+ Assert( word );
+ if ( !word )
+ continue;
+
+ float t1 = word->m_flStartTime;
+ float t2 = word->m_flEndTime;
+
+ float frac1 = ( t1 - starttime ) / ( endtime - starttime );
+ float frac2 = ( t2 - starttime ) / ( endtime - starttime );
+
+ frac1 = min( 1.0f, frac1 );
+ frac1 = max( 0.0f, frac1 );
+ frac2 = min( 1.0f, frac2 );
+ frac2 = max( 0.0f, frac2 );
+
+ if ( frac1 == frac2 )
+ continue;
+
+ int x1 = ( int )( frac1 * w2() );
+ int x2 = ( int )( frac2 * w2() );
+
+ if ( mx >= x1 && mx <= x2 )
+ {
+ return word;
+ }
+ }
+
+ return NULL;
+}
+
+void PhonemeEditor::DeselectWords( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *w = m_Tags.m_Words[ i ];
+ Assert( w );
+ if ( !w )
+ continue;
+ w->m_bSelected = false;
+ }
+
+}
+
+void PhonemeEditor::DeselectPhonemes( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ for ( int w = 0 ; w < m_Tags.m_Words.Size(); w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+ Assert( word );
+ if ( !word )
+ continue;
+
+ for ( int i = 0 ; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *pt = word->m_Phonemes[ i ];
+ Assert( pt );
+ if ( !pt )
+ continue;
+ pt->m_bSelected = false;
+ }
+ }
+}
+
+void PhonemeEditor::SnapWords( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( m_Tags.m_Words.Size() < 2 )
+ {
+ Con_Printf( "Can't snap, need at least two contiguous selected words\n" );
+ return;
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ for ( int i = 0; i < m_Tags.m_Words.Size() - 1; i++ )
+ {
+ CWordTag *current = m_Tags.m_Words[ i ];
+ CWordTag *next = m_Tags.m_Words[ i + 1 ];
+
+ Assert( current && next );
+
+ if ( !current->m_bSelected || !next->m_bSelected )
+ continue;
+
+ // Move next word to end of current
+ next->m_flStartTime = current->m_flEndTime;
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+void PhonemeEditor::SeparateWords( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( GetPixelsPerSecond() <= 0.0f )
+ return;
+
+ if ( m_Tags.m_Words.Size() < 2 )
+ {
+ Con_Printf( "Can't separate, need at least two contiguous selected words\n" );
+ return;
+ }
+
+ // Three pixels
+ double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ for ( int i = 0; i < m_Tags.m_Words.Size() - 1; i++ )
+ {
+ CWordTag *current = m_Tags.m_Words[ i ];
+ CWordTag *next = m_Tags.m_Words[ i + 1 ];
+
+ Assert( current && next );
+
+ if ( !current->m_bSelected || !next->m_bSelected )
+ continue;
+
+ // Close enough?
+ if ( fabs( current->m_flEndTime - next->m_flStartTime ) > time_epsilon )
+ {
+ Con_Printf( "Can't split %s and %s, already split apart\n",
+ current->GetWord(), next->GetWord() );
+ continue;
+ }
+
+ // Offset next word start time a bit
+ next->m_flStartTime += time_epsilon;
+
+ break;
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+void PhonemeEditor::CreateEvenWordDistribution( const char *wordlist )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if( !m_pWaveFile )
+ return;
+
+ Assert( wordlist );
+ if ( !wordlist )
+ return;
+
+ m_Tags.CreateEventWordDistribution( wordlist, m_pWaveFile->GetRunningLength() );
+
+ redraw();
+}
+
+void PhonemeEditor::EditWordList( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( !m_pWaveFile )
+ return;
+
+ // Build word string
+ char wordstring[ 1024 ];
+ V_strcpy_safe( wordstring, m_Tags.GetText() );
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Word List" );
+ strcpy( params.m_szPrompt, "Sentence:" );
+
+ strcpy( params.m_szInputText, wordstring );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ // Could be foreign language...
+ Warning( "Edit word list: No words entered!\n" );
+ }
+
+ SetDirty( true );
+
+ PushUndo();
+
+ // Clear any current LISET results
+ ClearExtracted();
+
+ // Force text
+ m_Tags.SetText( params.m_szInputText );
+
+ if ( m_Tags.m_Words.Size() == 0 )
+ {
+ // First text we've seen, just distribute words evenly
+ CreateEvenWordDistribution( params.m_szInputText );
+
+ // Redo liset
+ RedoPhonemeExtraction();
+ }
+
+ PushRedo();
+
+ SetFocus( (HWND)getHandle() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overwrite original wave with changes
+//-----------------------------------------------------------------------------
+void PhonemeEditor::CommitChanges( void )
+{
+ SaveLinguisticData();
+
+ // Make it writable - if possible
+ MakeFileWriteable( m_WorkFile.m_szWaveFile );
+
+ //Open a message box to warn the user if the file was unable to be made non-read only
+ if ( !IsFileWriteable( m_WorkFile.m_szWaveFile ) )
+ {
+ mxMessageBox( NULL, va( "Unable to save file '%s'. File is read-only or in use.",
+ m_WorkFile.m_szWaveFile ), g_appTitle, MX_MB_OK );
+ }
+ else
+ {
+ // Copy over and overwrite file
+ FPCopyFile( m_WorkFile.m_szWorkingFile, m_WorkFile.m_szWaveFile, true );
+ Msg( "Changes saved to '%s'\n", m_WorkFile.m_szWaveFile );
+ SetDirty( false, false );
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::LoadWaveFile( void )
+{
+ char filename[ 512 ];
+ if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*.wav" ) )
+ {
+ return;
+ }
+
+ StopPlayback();
+
+ // Strip out the game directory
+ SetCurrentWaveFile( filename );
+}
+
+void PhonemeEditor::SnapPhonemes( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ CPhonemeTag *prev = NULL;
+
+ for ( int w = 0; w < m_Tags.m_Words.Size(); w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+ Assert( word );
+ if ( !word )
+ continue;
+
+ for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *current = word->m_Phonemes[ i ];
+
+ Assert( current );
+
+ if ( current->m_bSelected )
+ {
+ if (prev)
+ {
+ // More start of next to end of previous
+ prev->SetEndTime( current->GetStartTime() );
+ }
+ prev = current;
+ }
+ else
+ {
+ prev = NULL;
+ }
+ }
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+void PhonemeEditor::SeparatePhonemes( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ // Three pixels
+ double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
+
+ CPhonemeTag *prev = NULL;
+
+ for ( int w = 0; w < m_Tags.m_Words.Size(); w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+ Assert( word );
+ if ( !word )
+ continue;
+
+ for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *current = word->m_Phonemes[ i ];
+
+ Assert( current );
+
+ if ( current->m_bSelected )
+ {
+ if ( prev )
+ {
+ // Close enough?
+ if ( fabs( prev->GetEndTime() - current->GetStartTime() ) > time_epsilon )
+ {
+ Con_Printf( "Can't split already split apart\n" );
+ continue;
+ }
+
+ current->AddStartTime( time_epsilon );
+ }
+
+ prev = current;
+ }
+ else
+ {
+ prev = NULL;
+ }
+ }
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+bool PhonemeEditor::IsMouseOverWordRow( int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ RECT rc;
+
+ GetWordTrayTopBottom( rc );
+
+ if ( my < rc.top )
+ return false;
+
+ if ( my > rc.bottom )
+ return false;
+
+ return true;
+}
+
+bool PhonemeEditor::IsMouseOverPhonemeRow( int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ RECT rc;
+
+ GetPhonemeTrayTopBottom( rc );
+
+ if ( my < rc.top )
+ return false;
+
+ if ( my > rc.bottom )
+ return false;
+
+ return true;
+}
+
+void PhonemeEditor::GetPhonemeTrayTopBottom( RECT& rc )
+{
+ RECT wkrc;
+ GetWorkspaceRect( wkrc );
+
+ rc.top = wkrc.bottom - 2 * m_nTickHeight;
+ rc.bottom = wkrc.bottom - m_nTickHeight;
+}
+
+void PhonemeEditor::GetWordTrayTopBottom( RECT& rc )
+{
+ RECT wkrc;
+ GetWorkspaceRect( wkrc );
+
+ rc.top = wkrc.top;
+ rc.bottom = wkrc.top + m_nTickHeight;
+}
+
+int PhonemeEditor::GetMouseForTime( float time )
+{
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ if ( GetPixelsPerSecond() < 0.0f )
+ return rc.left;
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ if ( endtime - starttime <= 0.0f )
+ return rc.left;
+
+ float frac;
+
+ frac = ( time - starttime ) / ( endtime - starttime );
+
+ return rc.left + ( int )( rc.right * frac );
+}
+
+void PhonemeEditor::GetWordRect( const CWordTag *tag, RECT& rc )
+{
+ Assert( tag );
+ GetWordTrayTopBottom( rc );
+
+ rc.left = GetMouseForTime( tag->m_flStartTime );
+ rc.right = GetMouseForTime( tag->m_flEndTime );
+
+}
+
+void PhonemeEditor::GetPhonemeRect( const CPhonemeTag *tag, RECT& rc )
+{
+ Assert( tag );
+
+ GetPhonemeTrayTopBottom( rc );
+ rc.left = GetMouseForTime( tag->GetStartTime() );
+ rc.right = GetMouseForTime( tag->GetEndTime() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::CommitExtracted( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ m_nLastExtractionResult = SR_RESULT_NORESULT;
+
+ if ( !m_TagsExt.m_Words.Size() )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ m_Tags.Reset();
+ m_Tags = m_TagsExt;
+
+ PushRedo();
+
+ ClearExtracted();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ClearExtracted( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ m_nLastExtractionResult = SR_RESULT_NORESULT;
+
+ m_TagsExt.Reset();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : resultCode -
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *PhonemeEditor::GetExtractionResultString( int resultCode )
+{
+ switch ( resultCode )
+ {
+ case SR_RESULT_NORESULT:
+ return "no extraction info.";
+ case SR_RESULT_ERROR:
+ return "an error occurred during extraction.";
+ case SR_RESULT_SUCCESS:
+ return "successful.";
+ case SR_RESULT_FAILED:
+ return "results retrieved, but full recognition failed.";
+ default:
+ break;
+ }
+
+ return "unknown result code.";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : CEventRelativeTag
+//-----------------------------------------------------------------------------
+CEventRelativeTag *PhonemeEditor::GetTagUnderMouse( int mx )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return NULL;
+
+ // Figure out tag positions
+ if ( !m_pEvent || !m_pWaveFile )
+ return NULL;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+ RECT rcTags = rc;
+
+ if ( GetPixelsPerSecond() <= 0.0f )
+ return NULL;
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ if ( endtime - starttime < 0 )
+ return NULL;
+
+ for ( int i = 0; i < m_pEvent->GetNumRelativeTags(); i++ )
+ {
+ CEventRelativeTag *tag = m_pEvent->GetRelativeTag( i );
+ if ( !tag )
+ continue;
+
+ //
+ float tagtime = tag->GetPercentage() * m_pWaveFile->GetRunningLength();
+ if ( tagtime < starttime || tagtime > endtime )
+ continue;
+
+ float frac = ( tagtime - starttime ) / ( endtime - starttime );
+
+ int left = rcTags.left + (int)( frac * ( float )( rcTags.right - rcTags.left ) + 0.5f );
+
+ if ( abs( mx - left ) < 10 )
+ return tag;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::IsMouseOverTag( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ if ( !IsMouseOverTagRow( my ) )
+ return false;
+
+ CEventRelativeTag *tag = GetTagUnderMouse( mx );
+ if ( !tag )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : startx -
+// endx -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::FinishEventTagDrag( int startx, int endx )
+{
+ if ( !m_pWaveFile )
+ return;
+
+ if ( !m_pWaveFile->GetRunningLength() )
+ return;
+
+ // Find starting tag
+ CEventRelativeTag *tag = GetTagUnderMouse( startx );
+ if ( !tag )
+ return;
+
+ if ( GetPixelsPerSecond() <= 0 )
+ return;
+
+ // Convert mouse position to time
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+
+ float clicktime = (float)endx / GetPixelsPerSecond() + starttime;
+
+ float percent = clicktime / m_pWaveFile->GetRunningLength();
+ percent = clamp( percent, 0.0f, 1.0f );
+
+ tag->SetPercentage( percent );
+
+ redraw();
+
+ if ( g_pChoreoView )
+ {
+ g_pChoreoView->InvalidateLayout();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : my -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::IsMouseOverTagRow( int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return false;
+
+ if ( my < TAG_TOP || my > TAG_BOTTOM )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ShowTagMenu( int mx, int my )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ // Figure out tag positions
+ if ( !m_pEvent || !m_pWaveFile )
+ return;
+
+ if ( !IsMouseOverTagRow( my ) )
+ return;
+
+ CEventRelativeTag *tag = GetTagUnderMouse( mx );
+
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( tag )
+ {
+ pop->add( va( "Delete tag '%s'", tag->GetName() ), IDC_DELETETAG );
+ }
+ else
+ {
+ pop->add( va( "Add tag..." ), IDC_ADDTAG );
+ }
+
+ m_nClickX = mx;
+
+ pop->popup( this, mx, my );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::DeleteTag( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ // Figure out tag positions
+ if ( !m_pEvent )
+ return;
+
+ CEventRelativeTag *tag = GetTagUnderMouse( m_nClickX );
+ if ( !tag )
+ return;
+
+ // Remove it
+ m_pEvent->RemoveRelativeTag( tag->GetName() );
+
+ g_pChoreoView->InvalidateLayout();
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::AddTag( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ // Figure out tag positions
+ if ( !m_pEvent || !m_pWaveFile )
+ return;
+
+ CInputParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Event Tag Name" );
+ strcpy( params.m_szPrompt, "Name:" );
+ strcpy( params.m_szInputText, "" );
+
+ if ( !InputProperties( &params ) )
+ return;
+
+ if ( strlen( params.m_szInputText ) <= 0 )
+ {
+ Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
+ return;
+ }
+
+ // Convert mouse position to time
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float clicktime = (float)m_nClickX / GetPixelsPerSecond() + starttime;
+
+ float percent = clicktime / m_pWaveFile->GetRunningLength();
+ percent = min( 1.0f, percent );
+ percent = max( 0.0f, percent );
+
+ m_pEvent->AddRelativeTag( params.m_szInputText, percent );
+
+ g_pChoreoView->InvalidateLayout();
+
+ SetFocus( (HWND)getHandle() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ClearEvent( void )
+{
+ m_pEvent = NULL;
+ redraw();
+}
+
+void PhonemeEditor::TraverseWords( PEWORDITERFUNC pfn, float fparam )
+{
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ (this->*pfn)( word, fparam );
+ }
+}
+
+void PhonemeEditor::TraversePhonemes( PEPHONEMEITERFUNC pfn, float fparam )
+{
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ (this->*pfn)( phoneme, word, fparam );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : amount -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ITER_MoveSelectedWords( CWordTag *word, float amount )
+{
+ if ( !word->m_bSelected )
+ return;
+
+ word->m_flStartTime += amount;
+ word->m_flEndTime += amount;
+}
+
+void PhonemeEditor::ITER_MoveSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
+{
+ if ( !phoneme->m_bSelected )
+ return;
+
+ phoneme->AddStartTime( amount );
+ phoneme->AddEndTime( amount );
+
+}
+
+void PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes( CPhonemeTag *phoneme, CWordTag *word, float amount )
+{
+ if ( !phoneme->m_bSelected )
+ return;
+
+ if ( phoneme->GetEndTime() + amount <= phoneme->GetStartTime() )
+ return;
+
+ phoneme->AddEndTime( amount );
+
+ // Fixme, check for extending into next phoneme
+}
+
+
+void PhonemeEditor::ITER_ExtendSelectedWordEndTimes( CWordTag *word, float amount )
+{
+ if ( !word->m_bSelected )
+ return;
+
+ if ( word->m_flEndTime + amount <= word->m_flStartTime )
+ return;
+
+ word->m_flEndTime += amount;
+
+ // Fixme, check for extending into next word
+}
+
+void PhonemeEditor::ITER_AddFocusRectSelectedWords( CWordTag *word, float amount )
+{
+ if ( !word->m_bSelected )
+ return;
+
+ RECT wordRect;
+ GetWordRect( word, wordRect );
+
+ AddFocusRect( wordRect );
+}
+
+void PhonemeEditor::ITER_AddFocusRectSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
+{
+ if ( !phoneme->m_bSelected )
+ return;
+
+ RECT phonemeRect;
+ GetPhonemeRect( phoneme, phonemeRect );
+
+ AddFocusRect( phonemeRect );
+}
+
+
+void PhonemeEditor::AddFocusRect( RECT& rc )
+{
+ RECT rcFocus = rc;
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ // Convert to screen space?
+ CFocusRect fr;
+ fr.m_rcFocus = rcFocus;
+ fr.m_rcOrig = rcFocus;
+
+ m_FocusRects.AddToTail( fr );
+}
+
+void PhonemeEditor::CountSelected( void )
+{
+ m_nSelectedPhonemeCount = 0;
+ m_nSelectedWordCount = 0;
+
+ TraverseWords( &PhonemeEditor::ITER_CountSelectedWords, 0.0f );
+ TraversePhonemes( &PhonemeEditor::ITER_CountSelectedPhonemes, 0.0f );
+}
+
+void PhonemeEditor::ITER_CountSelectedWords( CWordTag *word, float amount )
+{
+ if ( !word->m_bSelected )
+ return;
+
+ m_nSelectedWordCount++;
+
+}
+
+void PhonemeEditor::ITER_CountSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
+{
+ if ( !phoneme->m_bSelected )
+ return;
+
+ m_nSelectedPhonemeCount++;
+}
+
+// Undo/Redo
+void PhonemeEditor::Undo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 )
+ {
+ m_nUndoLevel--;
+ PEUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->undo );
+ m_Tags = *(u->undo);
+
+ SetClickedPhoneme( -1, -1 );
+ }
+
+ redraw();
+}
+
+void PhonemeEditor::Redo( void )
+{
+ if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 )
+ {
+ PEUndo *u = m_UndoStack[ m_nUndoLevel ];
+ Assert( u->redo );
+ m_Tags = *(u->redo);
+ m_nUndoLevel++;
+
+ SetClickedPhoneme( -1, -1 );
+ }
+
+ redraw();
+}
+
+void PhonemeEditor::PushUndo( void )
+{
+ Assert( !m_bRedoPending );
+ m_bRedoPending = true;
+ WipeRedo();
+
+ // Copy current data
+ CSentence *u = new CSentence();
+ *u = m_Tags;
+ PEUndo *undo = new PEUndo;
+ undo->undo = u;
+ undo->redo = NULL;
+ m_UndoStack.AddToTail( undo );
+ m_nUndoLevel++;
+}
+
+void PhonemeEditor::PushRedo( void )
+{
+ Assert( m_bRedoPending );
+ m_bRedoPending = false;
+
+ // Copy current data
+ CSentence *r = new CSentence();
+ *r = m_Tags;
+ PEUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ];
+ undo->redo = r;
+}
+
+void PhonemeEditor::WipeUndo( void )
+{
+ while ( m_UndoStack.Size() > 0 )
+ {
+ PEUndo *u = m_UndoStack[ 0 ];
+ delete u->undo;
+ delete u->redo;
+ delete u;
+ m_UndoStack.Remove( 0 );
+ }
+ m_nUndoLevel = 0;
+}
+
+void PhonemeEditor::WipeRedo( void )
+{
+ // Wipe everything above level
+ while ( m_UndoStack.Size() > m_nUndoLevel )
+ {
+ PEUndo *u = m_UndoStack[ m_nUndoLevel ];
+ delete u->undo;
+ delete u->redo;
+ delete u;
+ m_UndoStack.Remove( m_nUndoLevel );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : word -
+// phoneme -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SetClickedPhoneme( int word, int phoneme )
+{
+ m_nClickedPhoneme = phoneme;
+ m_nClickedWord = word;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CPhonemeTag
+//-----------------------------------------------------------------------------
+CPhonemeTag *PhonemeEditor::GetClickedPhoneme( void )
+{
+ if ( m_nClickedPhoneme < 0 || m_nClickedWord < 0 )
+ return NULL;
+
+ if ( m_nClickedWord >= m_Tags.m_Words.Size() )
+ return NULL;
+
+ CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
+ if ( !word )
+ return NULL;
+
+ if ( m_nClickedPhoneme >= word->m_Phonemes.Size() )
+ return NULL;
+
+ CPhonemeTag *phoneme = word->m_Phonemes[ m_nClickedPhoneme ];
+ return phoneme;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CWordTag
+//-----------------------------------------------------------------------------
+CWordTag *PhonemeEditor::GetClickedWord( void )
+{
+ if ( m_nClickedWord < 0 )
+ return NULL;
+
+ if ( m_nClickedWord >= m_Tags.m_Words.Size() )
+ return NULL;
+
+ CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
+ return word;
+}
+
+void PhonemeEditor::ShowContextMenu_Phonemes( int mx, int my )
+{
+ CountSelected();
+
+ // Construct main
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( m_pWaveFile )
+ {
+ mxPopupMenu *play = new mxPopupMenu;
+ play->add( va( "Original" ), IDC_PHONEME_PLAY_ORIG );
+ play->add( va( "Edited" ), IDC_PLAY_EDITED );
+ if ( m_bSelectionActive )
+ {
+ play->add( va( "Selection" ), IDC_PLAY_EDITED_SELECTION );
+ }
+
+ pop->addMenu( "Play", play );
+
+ if ( sound->IsSoundPlaying( m_pMixer ) )
+ {
+ pop->add( va( "Cancel playback" ), IDC_CANCELPLAYBACK );
+ }
+
+ pop->addSeparator();
+ }
+
+ pop->add( va( "Load..." ), IDC_LOADWAVEFILE );
+
+ if ( m_pWaveFile )
+ {
+ pop->add( va( "Save" ), IDC_SAVE_LINGUISTIC );
+ }
+
+ if ( m_bSelectionActive )
+ {
+ pop->addSeparator();
+ pop->add( va( "Deselect" ), IDC_DESELECT );
+ }
+
+ if ( m_pWaveFile )
+ {
+ pop->addSeparator();
+ pop->add( va( "Redo Extraction" ), IDC_REDO_PHONEMEEXTRACTION );
+
+ if ( m_nSelectedWordCount < 1 || AreSelectedWordsContiguous() )
+ {
+ pop->add( va( "Redo Extraction of selected words" ), IDC_REDO_PHONEMEEXTRACTION_SELECTION );
+ }
+ }
+
+ if ( m_pWaveFile && m_TagsExt.m_Words.Size() )
+ {
+ pop->addSeparator();
+ pop->add( va( "Commit extraction" ) , IDC_COMMITEXTRACTED );
+ pop->add( va( "Clear extraction" ), IDC_CLEAREXTRACTED );
+ }
+
+ if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Size() )
+ {
+ pop->addSeparator();
+ if ( m_nUndoLevel != 0 )
+ {
+ pop->add( va( "Undo" ), IDC_UNDO );
+ }
+ if ( m_nUndoLevel != m_UndoStack.Size() )
+ {
+ pop->add( va( "Redo" ), IDC_REDO );
+ }
+ pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
+ }
+
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ pop->addSeparator();
+ pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
+ }
+
+ // Show hierarchical options menu
+ {
+ mxPopupMenu *api = 0;
+
+ if ( DoesExtractorExistFor( SPEECH_API_SAPI ) )
+ {
+ api = new mxPopupMenu();
+ api->add( "Microsoft Speech API", IDC_API_SAPI );
+ if ( g_viewerSettings.speechapiindex == SPEECH_API_SAPI )
+ {
+ api->setChecked( IDC_API_SAPI, true );
+ }
+ }
+
+ if ( DoesExtractorExistFor( SPEECH_API_LIPSINC ) )
+ {
+ if ( !api )
+ api = new mxPopupMenu();
+
+ api->add( "Lipsinc Speech API", IDC_API_LIPSINC );
+ if ( g_viewerSettings.speechapiindex == SPEECH_API_LIPSINC )
+ {
+ api->setChecked( IDC_API_LIPSINC, true );
+ }
+ }
+
+ pop->addSeparator();
+ pop->addMenu( "Change Speech API", api );
+ }
+
+ // Import export menu
+ if ( m_pWaveFile )
+ {
+ pop->addSeparator();
+ if ( m_Tags.m_Words.Count() > 0 )
+ {
+ pop->add( "Export word data to " WORD_DATA_EXTENSION "...", IDC_EXPORT_SENTENCE );
+ }
+ pop->add( "Import word data from " WORD_DATA_EXTENSION "...", IDC_IMPORT_SENTENCE );
+ pop->add( va("%s Voice Duck", m_Tags.GetVoiceDuck() ? "Disable" : "Enable" ), IDC_TOGGLE_VOICEDUCK );
+ }
+
+ pop->popup( this, mx, my );
+}
+
+void PhonemeEditor::ShowContextMenu_Emphasis( int mx, int my )
+{
+ Emphasis_CountSelected();
+
+ // Construct main
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ pop->add( va( "Select All" ), IDC_EMPHASIS_SELECTALL );
+ if ( m_nNumSelected > 0 )
+ {
+ pop->add( va( "Deselect All" ), IDC_EMPHASIS_DESELECT );
+ }
+
+ if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Size() )
+ {
+ pop->addSeparator();
+
+ if ( m_nUndoLevel != 0 )
+ {
+ pop->add( va( "Undo" ), IDC_UNDO );
+ }
+ if ( m_nUndoLevel != m_UndoStack.Size() )
+ {
+ pop->add( va( "Redo" ), IDC_REDO );
+ }
+ pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
+ }
+ pop->popup( this, mx, my );
+}
+
+void PhonemeEditor::ShowContextMenu( int mx, int my )
+{
+ switch ( GetMode() )
+ {
+ default:
+ case MODE_PHONEMES:
+ ShowContextMenu_Phonemes( mx, my );
+ break;
+ case MODE_EMPHASIS:
+ ShowContextMenu_Emphasis( mx, my );
+ break;
+ }
+}
+
+void PhonemeEditor::ShiftSelectedPhoneme( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ switch ( m_nSelectedPhonemeCount )
+ {
+ case 1:
+ break;
+ case 0:
+ Con_Printf( "Can't shift phonemes, none selected\n" );
+ return;
+ default:
+ Con_Printf( "Can only shift one phoneme at a time via keyboard\n" );
+ return;
+ }
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+
+ float movetime = timeperpixel * (float)direction;
+
+ float maxmove = ComputeMaxPhonemeShift( direction > 0 ? true : false, false );
+
+ if ( direction > 0 )
+ {
+ if ( movetime > maxmove )
+ {
+ movetime = maxmove;
+ Con_Printf( "Further shift is blocked on right\n" );
+ }
+ }
+ else
+ {
+ if ( movetime < -maxmove )
+ {
+ movetime = -maxmove;
+ Con_Printf( "Further shift is blocked on left\n" );
+ }
+ }
+
+ if ( fabs( movetime ) < 0.0001f )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraversePhonemes( &PhonemeEditor::ITER_MoveSelectedPhonemes, movetime );
+
+ PushRedo();
+
+ m_bWordsActive = false;
+
+ redraw();
+
+ Con_Printf( "Shift phoneme %s\n", direction == -1 ? "left" : "right" );
+}
+
+void PhonemeEditor::ExtendSelectedPhonemeEndTime( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount != 1 )
+ return;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+
+ float movetime = timeperpixel * (float)direction;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraversePhonemes( &PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes, movetime );
+
+ PushRedo();
+
+ m_bWordsActive = false;
+
+ redraw();
+
+ Con_Printf( "Extend phoneme end %s\n", direction == -1 ? "left" : "right" );
+}
+
+void PhonemeEditor::SelectNextPhoneme( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount != 1 )
+ {
+ if ( m_nSelectedWordCount == 1 )
+ {
+ CWordTag *word = GetSelectedWord();
+ if ( word && word->m_Phonemes.Size() > 0 )
+ {
+ m_nSelectedPhonemeCount = 1;
+ CPhonemeTag *p = word->m_Phonemes[ direction ? word->m_Phonemes.Size() - 1 : 0 ];
+ p->m_bSelected = true;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Con_Printf( "Move to next phoneme %s\n", direction == -1 ? "left" : "right" );
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ if ( !phoneme->m_bSelected )
+ continue;
+
+ // Deselect this one and move
+ int nextindex = j + direction;
+ if ( nextindex < 0 )
+ {
+ nextindex = word->m_Phonemes.Size() - 1;
+ }
+ else if ( nextindex >= word->m_Phonemes.Size() )
+ {
+ nextindex = 0;
+ }
+
+ phoneme->m_bSelected = false;
+
+ phoneme = word->m_Phonemes[ nextindex ];
+
+ phoneme->m_bSelected = true;
+
+ m_bWordsActive = false;
+
+ redraw();
+ return;
+ }
+ }
+}
+
+bool PhonemeEditor::IsPhonemeSelected( CWordTag *word )
+{
+ for ( int i = 0 ; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *p = word->m_Phonemes[ i ];
+ if ( !p || !p->m_bSelected )
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
+void PhonemeEditor::SelectNextWord( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedWordCount != 1 &&
+ m_nSelectedPhonemeCount != 1 )
+ {
+ // Selected first word then
+ if ( m_nSelectedWordCount == 0 && m_Tags.m_Words.Size() > 0 )
+ {
+ CWordTag *word = m_Tags.m_Words[ direction ? m_Tags.m_Words.Size() - 1 : 0 ];
+ word->m_bSelected = true;
+ m_nSelectedWordCount = 1;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Con_Printf( "Move to next word %s\n", direction == -1 ? "left" : "right" );
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ if ( m_nSelectedWordCount == 1 )
+ {
+ if ( !word->m_bSelected )
+ continue;
+ }
+ else
+ {
+ if ( !IsPhonemeSelected( word ) )
+ continue;
+ }
+
+ // Deselect word
+ word->m_bSelected = false;
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ if ( !phoneme->m_bSelected )
+ continue;
+
+ phoneme->m_bSelected = false;
+ }
+
+ // Deselect this one and move
+ int nextword = i + direction;
+ if ( nextword < 0 )
+ {
+ nextword = m_Tags.m_Words.Size() - 1;
+ }
+ else if ( nextword >= m_Tags.m_Words.Size() )
+ {
+ nextword = 0;
+ }
+
+ word = m_Tags.m_Words[ nextword ];
+ word->m_bSelected = true;
+
+ if ( word->m_Phonemes.Size() > 0 )
+ {
+ CPhonemeTag *phoneme = NULL;
+
+ if ( direction > 0 )
+ {
+ phoneme = word->m_Phonemes[ 0 ];
+ }
+ else
+ {
+ phoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
+ }
+
+ phoneme->m_bSelected = true;
+ }
+
+ m_bWordsActive = true;
+
+ redraw();
+ return;
+ }
+}
+
+void PhonemeEditor::ShiftSelectedWord( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ switch ( m_nSelectedWordCount )
+ {
+ case 1:
+ break;
+ case 0:
+ Con_Printf( "Can't shift words, none selected\n" );
+ return;
+ default:
+ Con_Printf( "Can only shift one word at a time via keyboard\n" );
+ return;
+ }
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+
+ float movetime = timeperpixel * (float)direction;
+
+ float maxmove = ComputeMaxWordShift( direction > 0 ? true : false, false );
+
+ if ( direction > 0 )
+ {
+ if ( movetime > maxmove )
+ {
+ movetime = maxmove;
+ Con_Printf( "Further shift is blocked on right\n" );
+ }
+ }
+ else
+ {
+ if ( movetime < -maxmove )
+ {
+ movetime = -maxmove;
+ Con_Printf( "Further shift is blocked on left\n" );
+ }
+ }
+
+ if ( fabs( movetime ) < 0.0001f )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraverseWords( &PhonemeEditor::ITER_MoveSelectedWords, movetime );
+
+ PushRedo();
+
+ m_bWordsActive = true;
+
+ redraw();
+
+ Con_Printf( "Shift word %s\n", direction == -1 ? "left" : "right" );
+}
+
+void PhonemeEditor::ExtendSelectedWordEndTime( int direction )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedWordCount != 1 )
+ return;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ // Determine start/stop positions
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+
+ float movetime = timeperpixel * (float)direction;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ TraverseWords( &PhonemeEditor::ITER_ExtendSelectedWordEndTimes, movetime );
+
+ PushRedo();
+
+ m_bWordsActive = true;
+
+ redraw();
+
+ Con_Printf( "Extend word end %s\n", direction == -1 ? "left" : "right" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *word -
+// Output : int
+//-----------------------------------------------------------------------------
+int PhonemeEditor::IndexOfWord( CWordTag *word )
+{
+ for ( int i = 0 ; i < m_Tags.m_Words.Size(); i++ )
+ {
+ if ( m_Tags.m_Words[ i ] == word )
+ return i;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : forward -
+// *currentWord -
+// **nextWord -
+// Output : float
+//-----------------------------------------------------------------------------
+float PhonemeEditor::GetTimeGapToNextWord( bool forward, CWordTag *currentWord, CWordTag **ppNextWord /* = NULL */ )
+{
+ if ( ppNextWord )
+ {
+ *ppNextWord = NULL;
+ }
+
+ if ( !currentWord )
+ return 0.0f;
+
+ int wordnum = IndexOfWord( currentWord );
+ if ( wordnum == -1 )
+ return 0.0f;
+
+ // Go in correct direction
+ int newwordnum = wordnum + ( forward ? 1 : -1 );
+
+ // There is no next word
+ if ( newwordnum >= m_Tags.m_Words.Size() )
+ {
+ return PLENTY_OF_TIME;
+ }
+
+ // There is no previous word
+ if ( newwordnum < 0 )
+ {
+ return PLENTY_OF_TIME;
+ }
+
+ if ( ppNextWord )
+ {
+ *ppNextWord = m_Tags.m_Words[ newwordnum ];
+ }
+
+ // Otherwise, figure out time gap
+ if ( forward )
+ {
+ float currentEnd = currentWord->m_flEndTime;
+ float nextStart = m_Tags.m_Words[ newwordnum ]->m_flStartTime;
+
+ return ( nextStart - currentEnd );
+ }
+ else
+ {
+ float previousEnd = m_Tags.m_Words[ newwordnum ]->m_flEndTime;
+ float currentStart = currentWord->m_flStartTime;
+
+ return ( currentStart - previousEnd );
+ }
+
+
+ Assert( 0 );
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : forward -
+// *currentPhoneme -
+// **word -
+// **phoneme -
+// Output : float
+//-----------------------------------------------------------------------------
+float PhonemeEditor::GetTimeGapToNextPhoneme( bool forward, CPhonemeTag *currentPhoneme,
+ CWordTag **ppword /* = NULL */, CPhonemeTag **ppphoneme /* = NULL */ )
+{
+ if ( ppword )
+ {
+ *ppword = NULL;
+ }
+ if ( ppphoneme )
+ {
+ *ppphoneme = NULL;
+ }
+
+ if ( !currentPhoneme )
+ return 0.0f;
+
+ CWordTag *word = m_Tags.GetWordForPhoneme( currentPhoneme );
+ if ( !word )
+ return 0.0f;
+
+ int wordnum = IndexOfWord( word );
+ Assert( wordnum != -1 );
+
+ int phonemenum = word->IndexOfPhoneme( currentPhoneme );
+ if ( phonemenum < 0 )
+ return 0.0f;
+
+ CPhonemeTag *nextPhoneme = NULL;
+
+ int nextphoneme = phonemenum + ( forward ? 1 : -1 );
+
+ // Try last phoneme of previous word
+ if ( nextphoneme < 0 )
+ {
+ wordnum--;
+ while ( wordnum >= 0 )
+ {
+ if ( ppword )
+ {
+ *ppword = m_Tags.m_Words[ wordnum ];
+ }
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() > 0 )
+ {
+ nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() - 1 ];
+ break;
+ }
+ }
+ wordnum--;
+ }
+ }
+ // Try first phoneme of next word, if there is one
+ else if ( nextphoneme >= word->m_Phonemes.Size() )
+ {
+ wordnum++;
+ while ( wordnum < m_Tags.m_Words.Size() )
+ {
+ if ( ppword )
+ {
+ *ppword = m_Tags.m_Words[ wordnum ];
+ }
+ // Really it can't be zero, but check anyway
+ if ( m_Tags.m_Words.Size() > 0 )
+ {
+ if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() > 0 )
+ {
+ nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ 0 ];
+ break;
+ }
+ }
+ wordnum++;
+ }
+ }
+ else
+ {
+ nextPhoneme = word->m_Phonemes[ nextphoneme ];
+ }
+
+ if ( !nextPhoneme )
+ return PLENTY_OF_TIME;
+
+ if ( ppphoneme )
+ {
+ *ppphoneme = nextPhoneme;
+ }
+
+ // Now compute time delta
+ float dt = 0.0f;
+ if ( forward )
+ {
+ dt = nextPhoneme->GetStartTime() - currentPhoneme->GetEndTime();
+ }
+ else
+ {
+ dt = currentPhoneme->GetStartTime() - nextPhoneme->GetEndTime();
+ }
+
+ return dt;
+}
+
+CPhonemeTag *PhonemeEditor::GetSelectedPhoneme( void )
+{
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount != 1 )
+ return NULL;
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *w = m_Tags.m_Words[ i ];
+ if ( !w )
+ continue;
+
+ for ( int j = 0; j < w->m_Phonemes.Size() ; j++ )
+ {
+ CPhonemeTag *p = w->m_Phonemes[ j ];
+ if ( !p || !p->m_bSelected )
+ continue;
+
+ return p;
+ }
+ }
+ return NULL;
+}
+
+CWordTag *PhonemeEditor::GetSelectedWord( void )
+{
+ CountSelected();
+
+ if ( m_nSelectedWordCount != 1 )
+ return NULL;
+
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *w = m_Tags.m_Words[ i ];
+ if ( !w || !w->m_bSelected )
+ continue;
+
+ return w;
+ }
+ return NULL;
+}
+
+void PhonemeEditor::OnMouseMove( mxEvent *event )
+{
+ int mx = (short)event->x;
+
+ LimitDrag( mx );
+
+ event->x = (short)mx;
+
+ if ( m_nDragType != DRAGTYPE_NONE )
+ {
+ DrawFocusRect( "moving old" );
+
+ for ( int i = 0; i < m_FocusRects.Size(); i++ )
+ {
+ CFocusRect *f = &m_FocusRects[ i ];
+ f->m_rcFocus = f->m_rcOrig;
+
+ switch ( m_nDragType )
+ {
+ default:
+ {
+ // Only X Shifts supported
+ OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ),
+ 0 );
+ }
+ break;
+ case DRAGTYPE_EMPHASIS_SELECT:
+ {
+ RECT rcWork;
+ GetWorkspaceRect( rcWork );
+ RECT rcEmphasis;
+ Emphasis_GetRect( rcWork, rcEmphasis );
+
+ RECT rcFocus;
+
+ rcFocus = f->m_rcOrig;
+
+ rcFocus.left = m_nStartX < (short)event->x ? m_nStartX : (short)event->x;
+ rcFocus.right = m_nStartX < (short)event->x ? (short)event->x : m_nStartX;
+
+ rcFocus.top = m_nStartY < (short)event->y ? m_nStartY : (short)event->y;
+ rcFocus.bottom = m_nStartY < (short)event->y ? (short)event->y : m_nStartY;
+
+ rcFocus.top = clamp( rcFocus.top, rcEmphasis.top, rcEmphasis.bottom );
+ rcFocus.bottom = clamp( rcFocus.bottom, rcEmphasis.top, rcEmphasis.bottom );
+
+ //OffsetRect( &rcFocus, 0, -rcEmphasis.top );
+
+ POINT offset;
+ offset.x = 0;
+ offset.y = 0;
+ ClientToScreen( (HWND)getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ f->m_rcFocus = rcFocus;
+ }
+ break;
+ }
+ }
+
+ if ( m_nDragType == DRAGTYPE_EMPHASIS_MOVE )
+ {
+ redraw();
+ }
+
+ DrawFocusRect( "moving new" );
+ }
+ else
+ {
+ if ( m_hPrevCursor )
+ {
+ SetCursor( m_hPrevCursor );
+ m_hPrevCursor = NULL;
+ }
+
+ CountSelected();
+
+ int overhandle = IsMouseOverBoundary( event );
+ if ( overhandle == BOUNDARY_PHONEME && m_nSelectedPhonemeCount <= 1 )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( overhandle == BOUNDARY_WORD && m_nSelectedWordCount <= 1 )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
+ {
+ if ( IsMouseOverSelectionStartEdge( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else if ( IsMouseOverSelectionEndEdge( event ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
+ }
+ else
+ {
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ }
+ }
+ else
+ {
+ if ( IsMouseOverTag( (short)event->x, (short)event->y ) )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ else
+ {
+ CPhonemeTag *pt = GetPhonemeTagUnderMouse( (short)event->x, (short)event->y );
+ CWordTag *wt = GetWordTagUnderMouse( (short)event->x, (short)event->y );
+ if ( wt || pt )
+ {
+ if ( pt )
+ {
+ // Select it
+ SelectExpression( pt );
+ }
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
+ }
+ }
+ }
+ }
+ }
+
+ switch ( m_nDragType )
+ {
+ default:
+ break;
+ case DRAGTYPE_EMPHASIS_MOVE:
+ {
+ Emphasis_MouseDrag( (short)event->x, (short)event->y );
+ m_Tags.Resort();
+ }
+ break;
+ case DRAGTYPE_SCRUBBER:
+ {
+ float t = GetTimeForPixel( (short)event->x );
+ t += m_flScrubberTimeOffset;
+
+ ClampTimeToSelectionInterval( t );
+
+ float dt = t - m_flScrub;
+
+ SetScrubTargetTime( t );
+
+ ScrubThink( dt, true );
+
+ SetScrubTime( t );
+
+ DrawScrubHandle();
+ }
+ break;
+ }
+
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::EditInsertFirstPhonemeOfWord( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CWordTag *cw = GetSelectedWord();
+ if ( !cw )
+ return;
+
+ if ( cw->m_Phonemes.Size() != 0 )
+ {
+ Con_Printf( "Can't insert first phoneme into %s, already has phonemes\n", cw->GetWord() );
+ return;
+ }
+
+ CPhonemeParams params;
+ memset( &params, 0, sizeof( params ) );
+ strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
+ strcpy( params.m_szName, "" );
+
+ params.m_nLeft = -1;
+ params.m_nTop = -1;
+
+ params.m_bPositionDialog = true;
+ params.m_bMultiplePhoneme = true;
+
+ if ( params.m_bPositionDialog )
+ {
+ RECT rcWord;
+ GetWordRect( cw, rcWord );
+
+ // Convert to screen coords
+ POINT pt;
+ pt.x = rcWord.left;
+ pt.y = rcWord.top;
+
+ ClientToScreen( (HWND)getHandle(), &pt );
+
+ params.m_nLeft = pt.x;
+ params.m_nTop = pt.y;
+ }
+
+ int iret = PhonemeProperties( &params );
+ SetFocus( (HWND)getHandle() );
+ if ( !iret )
+ {
+ return;
+ }
+
+ int phonemeCount = CSentence::CountWords( params.m_szName );
+ if ( phonemeCount <= 0 )
+ {
+ return;
+ }
+
+ float wordLength = cw->m_flEndTime - cw->m_flStartTime;
+ float timePerPhoneme = wordLength / (float)phonemeCount;
+
+ float currentTime = cw->m_flStartTime;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ unsigned char *in;
+ char *out;
+
+ char phonemeName[ 128 ];
+
+ in = (unsigned char *)params.m_szName;
+
+ do
+ {
+ out = phonemeName;
+
+ while ( *in > 32 )
+ {
+ *out++ = *in++;
+ }
+ *out = 0;
+
+ CPhonemeTag phoneme;
+
+ phoneme.SetPhonemeCode( TextToPhoneme( phonemeName ) );
+ phoneme.SetTag( phonemeName );
+
+ phoneme.SetStartTime( currentTime );
+ phoneme.SetEndTime( currentTime + timePerPhoneme );
+ phoneme.m_bSelected = false;
+
+ cw->m_Phonemes.AddToTail( new CPhonemeTag( phoneme ) );
+
+ currentTime += timePerPhoneme;
+
+ if ( !*in )
+ break;
+
+ // Skip whitespace
+ in++;
+
+ } while ( 1 );
+
+ cw->m_Phonemes[ 0 ]->m_bSelected = true;
+
+ PushRedo();
+
+ // Add it
+ redraw();
+}
+
+void PhonemeEditor::SelectPhonemes( bool forward )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount != 1 )
+ return;
+
+ CPhonemeTag *phoneme = GetSelectedPhoneme();
+ if ( !phoneme )
+ return;
+
+ // Figure out it's word and index
+ CWordTag *word = m_Tags.GetWordForPhoneme( phoneme );
+ if ( !word )
+ return;
+
+ int wordNum = IndexOfWord( word );
+ if ( wordNum == -1 )
+ return;
+
+ // Select remaining phonemes in current word
+ int i;
+
+ i = word->IndexOfPhoneme( phoneme );
+ if ( i == -1 )
+ return;
+
+ if ( forward )
+ {
+ // Start at next one
+ i++;
+
+ for ( ; i < word->m_Phonemes.Size(); i++ )
+ {
+ phoneme = word->m_Phonemes[ i ];
+ phoneme->m_bSelected = true;
+ }
+
+ // Now start at next word
+ wordNum++;
+
+ for ( ; wordNum < m_Tags.m_Words.Size(); wordNum++ )
+ {
+ word = m_Tags.m_Words[ wordNum ];
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ phoneme = word->m_Phonemes[ j ];
+ phoneme->m_bSelected = true;
+ }
+ }
+ }
+ else
+ {
+ // Start at previous
+ i--;
+
+ for ( ; i >= 0; i-- )
+ {
+ phoneme = word->m_Phonemes[ i ];
+ phoneme->m_bSelected = true;
+ }
+
+ // Now start at previous word
+ wordNum--;
+
+ for ( ; wordNum >= 0 ; wordNum-- )
+ {
+ word = m_Tags.m_Words[ wordNum ];
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ phoneme = word->m_Phonemes[ j ];
+ phoneme->m_bSelected = true;
+ }
+ }
+ }
+
+ redraw();
+}
+
+void PhonemeEditor::SelectWords( bool forward )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ CountSelected();
+
+ if ( m_nSelectedWordCount != 1 )
+ return;
+
+ // Figure out it's word and index
+ CWordTag *word = GetSelectedWord();
+ if ( !word )
+ return;
+
+ int wordNum = IndexOfWord( word );
+ if ( wordNum == -1 )
+ return;
+
+ if ( forward )
+ {
+ wordNum++;
+
+ for ( ; wordNum < m_Tags.m_Words.Size(); wordNum++ )
+ {
+ word = m_Tags.m_Words[ wordNum ];
+ word->m_bSelected = true;
+ }
+ }
+ else
+ {
+ wordNum--;
+
+ for ( ; wordNum >= 0; wordNum-- )
+ {
+ word = m_Tags.m_Words[ wordNum ];
+ word->m_bSelected = true;
+ }
+
+ }
+
+ redraw();
+}
+
+bool PhonemeEditor::AreSelectedWordsContiguous( void )
+{
+ CountSelected();
+
+ if ( m_nSelectedWordCount < 1 )
+ return false;
+
+ if ( m_nSelectedWordCount == 1 )
+ return true;
+
+ // Find first selected word
+ int runcount = 0;
+ bool parity = false;
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ if ( word->m_bSelected )
+ {
+ if ( !parity )
+ {
+ parity = true;
+ runcount++;
+ }
+ }
+ else
+ {
+ if ( parity )
+ {
+ parity = false;
+ }
+ }
+ }
+
+ if ( runcount == 1 )
+ return true;
+
+ return false;
+}
+
+bool PhonemeEditor::AreSelectedPhonemesContiguous( void )
+{
+ CountSelected();
+
+ if ( m_nSelectedPhonemeCount < 1 )
+ return false;
+
+ if ( m_nSelectedPhonemeCount == 1 )
+ return true;
+
+ // Find first selected word
+ int runcount = 0;
+ bool parity = false;
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ if ( phoneme->m_bSelected )
+ {
+ if ( !parity )
+ {
+ parity = true;
+ runcount++;
+ }
+ }
+ else
+ {
+ if ( parity )
+ {
+ parity = false;
+ }
+ }
+ }
+ }
+
+ if ( runcount == 1 )
+ return true;
+
+ return false;
+
+}
+
+void PhonemeEditor::SortWords( bool prepareundo )
+{
+ if ( prepareundo )
+ {
+ SetDirty( true );
+ PushUndo();
+ }
+
+ // Just bubble sort by start time
+ int c = m_Tags.m_Words.Count();
+
+ int i;
+
+ // check for start > end
+ for ( i = 0; i < c; i++ )
+ {
+ CWordTag *p1 = m_Tags.m_Words[ i ];
+ if (p1->m_flStartTime > p1->m_flEndTime )
+ {
+ float swap = p1->m_flStartTime;
+ p1->m_flStartTime = p1->m_flEndTime;
+ p1->m_flEndTime = swap;
+ }
+ }
+
+ for ( i = 0; i < c; i++ )
+ {
+ for ( int j = i + 1; j < c; j++ )
+ {
+ CWordTag *p1 = m_Tags.m_Words[ i ];
+ CWordTag *p2 = m_Tags.m_Words[ j ];
+
+ if ( p1->m_flStartTime < p2->m_flStartTime )
+ continue;
+
+ // Swap them
+ m_Tags.m_Words[ i ] = p2;
+ m_Tags.m_Words[ j ] = p1;
+ }
+ }
+
+ if ( prepareundo )
+ {
+ PushRedo();
+ }
+}
+
+void PhonemeEditor::SortPhonemes( bool prepareundo )
+{
+ if ( prepareundo )
+ {
+ SetDirty( true );
+ PushUndo();
+ }
+
+ // Just bubble sort by start time
+ int wc = m_Tags.m_Words.Count();
+ for ( int w = 0; w < wc; w++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ w ];
+ Assert( word );
+
+ int c = word->m_Phonemes.Count();
+ int i;
+
+ // check for start > end
+ for ( i = 0; i < c; i++ )
+ {
+ CPhonemeTag *p1 = word->m_Phonemes[ i ];
+
+ if (p1->GetStartTime() > p1->GetEndTime() )
+ {
+ float swap = p1->GetStartTime();
+ p1->SetStartTime( p1->GetEndTime() );
+ p1->SetEndTime( swap );
+ }
+ }
+
+ for ( i = 0; i < c; i++ )
+ {
+ for ( int j = i + 1; j < c; j++ )
+ {
+ CPhonemeTag *p1 = word->m_Phonemes[ i ];
+ CPhonemeTag *p2 = word->m_Phonemes[ j ];
+
+ if ( p1->GetStartTime() < p2->GetStartTime() )
+ continue;
+
+ // Swap them
+ word->m_Phonemes[ i ] = p2;
+ word->m_Phonemes[ j ] = p1;
+ }
+ }
+ }
+
+ if ( prepareundo )
+ {
+ PushRedo();
+ }
+}
+
+void PhonemeEditor::CleanupWordsAndPhonemes( bool prepareundo )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ // 2 pixel gap
+ float snap_epsilon = 2.49f / GetPixelsPerSecond();
+
+ if ( prepareundo )
+ {
+ SetDirty( true );
+ PushUndo();
+ }
+
+ SortWords( false );
+ SortPhonemes( false );
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ CWordTag *next = NULL;
+ if ( i < m_Tags.m_Words.Size() - 1 )
+ {
+ next = m_Tags.m_Words[ i + 1 ];
+ }
+
+ if ( word && next )
+ {
+ // Check for words close enough
+ float eps = next->m_flStartTime - word->m_flEndTime;
+ if ( eps && eps <= snap_epsilon )
+ {
+ float t = (word->m_flEndTime + next->m_flStartTime) * 0.5;
+ word->m_flEndTime = t;
+ next->m_flStartTime = t;
+ }
+ }
+
+ for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ CPhonemeTag *next = NULL;
+ if ( j < word->m_Phonemes.Size() - 1 )
+ {
+ next = word->m_Phonemes[ j + 1 ];
+ }
+
+ if ( phoneme && next )
+ {
+ float eps = next->GetStartTime() - phoneme->GetEndTime();
+ if ( eps && eps <= snap_epsilon )
+ {
+ float t = (phoneme->GetEndTime() + next->GetStartTime() ) * 0.5;
+ phoneme->SetEndTime( t );
+ next->SetStartTime( t );
+ }
+ }
+ }
+ }
+
+ if ( prepareundo )
+ {
+ PushRedo();
+ }
+
+ // NOTE: Caller must call "redraw()" to get screen to update
+}
+
+
+
+void PhonemeEditor::RealignPhonemesToWords( bool prepareundo )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( prepareundo )
+ {
+ SetDirty( true );
+ PushUndo();
+ }
+
+ SortWords( false );
+ SortPhonemes( false );
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ CWordTag *next = NULL;
+ if ( i < m_Tags.m_Words.Size() - 1 )
+ {
+ next = m_Tags.m_Words[ i + 1 ];
+ }
+
+ float word_dt = word->m_flEndTime - word->m_flStartTime;
+
+ CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
+ if ( !FirstPhoneme )
+ continue;
+
+ CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
+ if ( !LastPhoneme )
+ continue;
+
+ float phoneme_dt = LastPhoneme->GetEndTime() - FirstPhoneme->GetStartTime();
+
+ float phoneme_shift = FirstPhoneme->GetStartTime();
+
+ for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ CPhonemeTag *next = NULL;
+ if ( j < word->m_Phonemes.Size() - 1 )
+ {
+ next = word->m_Phonemes[ j + 1 ];
+ }
+
+ if (j == 0)
+ {
+ float t = (phoneme->GetStartTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
+ phoneme->SetStartTime( t );
+ }
+
+ float t = (phoneme->GetEndTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
+ phoneme->SetEndTime( t );
+ if (next)
+ {
+ next->SetStartTime( t );
+ }
+ }
+ }
+
+ if ( prepareundo )
+ {
+ PushRedo();
+ }
+
+ // NOTE: Caller must call "redraw()" to get screen to update
+}
+
+
+void PhonemeEditor::RealignWordsToPhonemes( bool prepareundo )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ if ( prepareundo )
+ {
+ SetDirty( true );
+ PushUndo();
+ }
+
+ SortWords( false );
+ SortPhonemes( false );
+
+ for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
+ if ( !FirstPhoneme )
+ continue;
+
+ CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
+ if ( !LastPhoneme )
+ continue;
+
+ word->m_flStartTime = FirstPhoneme->GetStartTime();
+ word->m_flEndTime = LastPhoneme->GetEndTime();
+ }
+
+ if ( prepareundo )
+ {
+ PushRedo();
+ }
+
+ // NOTE: Caller must call "redraw()" to get screen to update
+}
+
+
+
+float PhonemeEditor::ComputeMaxWordShift( bool forward, bool allowcrop )
+{
+ // skipping selected words, figure out max time shift of words before they selection touches any
+ // unselected words
+ // if allowcrop is true, then the maximum extends up to end of next word
+ float maxshift = PLENTY_OF_TIME;
+
+ if ( forward )
+ {
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *w1 = m_Tags.m_Words[ i ];
+ if ( !w1 || !w1->m_bSelected )
+ continue;
+
+ CWordTag *w2 = NULL;
+ for ( int search = i + 1; search < m_Tags.m_Words.Size() ; search++ )
+ {
+ CWordTag *check = m_Tags.m_Words[ search ];
+ if ( !check || check->m_bSelected )
+ continue;
+
+ w2 = check;
+ break;
+ }
+
+ if ( w2 )
+ {
+ float shift;
+ if ( allowcrop )
+ {
+ shift = w2->m_flEndTime - w1->m_flEndTime;
+ }
+ else
+ {
+ shift = w2->m_flStartTime - w1->m_flEndTime;
+ }
+
+ if ( shift < maxshift )
+ {
+ maxshift = shift;
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i = m_Tags.m_Words.Size() -1; i >= 0; i-- )
+ {
+ CWordTag *w1 = m_Tags.m_Words[ i ];
+ if ( !w1 || !w1->m_bSelected )
+ continue;
+
+ CWordTag *w2 = NULL;
+ for ( int search = i - 1; search >= 0 ; search-- )
+ {
+ CWordTag *check = m_Tags.m_Words[ search ];
+ if ( !check || check->m_bSelected )
+ continue;
+
+ w2 = check;
+ break;
+ }
+
+ if ( w2 )
+ {
+ float shift;
+ if ( allowcrop )
+ {
+ shift = w1->m_flStartTime - w2->m_flStartTime;
+ }
+ else
+ {
+ shift = w1->m_flStartTime - w2->m_flEndTime;
+ }
+
+ if ( shift < maxshift )
+ {
+ maxshift = shift;
+ }
+ }
+ }
+ }
+
+ return maxshift;
+}
+
+float PhonemeEditor::ComputeMaxPhonemeShift( bool forward, bool allowcrop )
+{
+ // skipping selected phonemes, figure out max time shift of phonemes before they selection touches any
+ // unselected words
+ // if allowcrop is true, then the maximum extends up to end of next word
+ float maxshift = PLENTY_OF_TIME;
+
+ if ( forward )
+ {
+ for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *p1 = word->m_Phonemes[ j ];
+ if ( !p1 || !p1->m_bSelected )
+ continue;
+
+ // Find next unselected phoneme
+ CPhonemeTag *p2 = NULL;
+
+ CPhonemeTag *start = p1;
+ do
+ {
+ CPhonemeTag *test = NULL;
+ GetTimeGapToNextPhoneme( forward, start, NULL, &test );
+ if ( !test )
+ break;
+
+ if ( test->m_bSelected )
+ {
+ start = test;
+ continue;
+ }
+
+ p2 = test;
+ break;
+ } while ( 1 );
+
+ if ( p2 )
+ {
+ float shift;
+ if ( allowcrop )
+ {
+ shift = p2->GetEndTime() - p1->GetEndTime();
+ }
+ else
+ {
+ shift = p2->GetStartTime() - p1->GetEndTime();
+ }
+
+ if ( shift < maxshift )
+ {
+ maxshift = shift;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i = m_Tags.m_Words.Size() -1; i >= 0; i-- )
+ {
+ CWordTag *word = m_Tags.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ for ( int j = word->m_Phonemes.Size() - 1; j >= 0; j-- )
+ {
+ CPhonemeTag *p1 = word->m_Phonemes[ j ];
+ if ( !p1 || !p1->m_bSelected )
+ continue;
+
+ // Find previous unselected phoneme
+ CPhonemeTag *p2 = NULL;
+
+ CPhonemeTag *start = p1;
+ do
+ {
+ CPhonemeTag *test = NULL;
+ GetTimeGapToNextPhoneme( forward, start, NULL, &test );
+ if ( !test )
+ break;
+
+ if ( test->m_bSelected )
+ {
+ start = test;
+ continue;
+ }
+
+ p2 = test;
+ break;
+ } while ( 1 );
+
+ if ( p2 )
+ {
+ float shift;
+ if ( allowcrop )
+ {
+ shift = p1->GetStartTime() - p2->GetStartTime();
+ }
+ else
+ {
+ shift = p1->GetStartTime() - p2->GetEndTime();
+ }
+
+ if ( shift < maxshift )
+ {
+ maxshift = shift;
+ }
+ }
+ }
+ }
+ }
+
+ return maxshift;
+}
+
+int PhonemeEditor::PixelsForDeltaTime( float dt )
+{
+ if ( !dt )
+ return 0;
+
+ RECT rc;
+ GetWorkspaceRect( rc );
+
+ float starttime = m_nLeftOffset / GetPixelsPerSecond();
+ float endtime = w2() / GetPixelsPerSecond() + starttime;
+
+ float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
+
+ float pixels = dt / timeperpixel;
+
+ return abs( (int)pixels );
+}
+
+void PhonemeEditor::ClearDragLimit( void )
+{
+ m_bLimitDrag = false;
+ m_nLeftLimit = -1;
+ m_nRightLimit = -1;
+}
+
+void PhonemeEditor::SetDragLimit( int dragtype )
+{
+ ClearDragLimit();
+
+ float nextW, nextP;
+ float prevW, prevP;
+
+ nextW = ComputeMaxWordShift( true, false );
+ prevW = ComputeMaxWordShift( false, false );
+ nextP = ComputeMaxPhonemeShift( true, false );
+ prevP = ComputeMaxPhonemeShift( false, false );
+
+ /*
+ Con_Printf( "+w %f -w %f +p %f -p %f\n",
+ 1000.0f * nextW,
+ 1000.0f * prevW,
+ 1000.0f * nextP,
+ 1000.0f * prevP );
+ */
+
+ switch ( dragtype )
+ {
+ case DRAGTYPE_MOVEWORD:
+ m_bLimitDrag = true;
+ m_nLeftLimit = PixelsForDeltaTime( prevW );
+ m_nRightLimit = PixelsForDeltaTime( nextW );
+ break;
+ case DRAGTYPE_MOVEPHONEME:
+ m_bLimitDrag = true;
+ m_nLeftLimit = PixelsForDeltaTime( prevP );
+ m_nRightLimit = PixelsForDeltaTime( nextP );
+ break;
+ default:
+ ClearDragLimit();
+ break;
+ }
+}
+
+void PhonemeEditor::LimitDrag( int& mousex )
+{
+ if ( m_nDragType == DRAGTYPE_NONE )
+ return;
+
+ if ( !m_bLimitDrag )
+ return;
+
+ int delta = mousex - m_nStartX;
+ if ( delta > 0 )
+ {
+ if ( m_nRightLimit >= 0 )
+ {
+ if ( delta > m_nRightLimit )
+ {
+ mousex = m_nStartX + m_nRightLimit;
+ }
+ }
+ }
+ else if ( delta < 0 )
+ {
+ if ( m_nLeftLimit >= 0 )
+ {
+ if ( abs( delta ) > abs( m_nLeftLimit ) )
+ {
+ mousex = m_nStartX - m_nLeftLimit;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wipe undo/redo data
+//-----------------------------------------------------------------------------
+void PhonemeEditor::ClearUndo( void )
+{
+ WipeUndo();
+ WipeRedo();
+
+ SetDirty( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *tag -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::SelectExpression( CPhonemeTag *tag )
+{
+ if ( !models->GetActiveStudioModel() )
+ return;
+
+ CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
+ if ( !hdr )
+ return;
+
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ CExpClass *cl = expressions->FindClass( "phonemes", true );
+ if ( !cl )
+ {
+ Con_Printf( "Couldn't load expressions/phonemes.txt!\n" );
+ return;
+ }
+
+ if ( expressions->GetActiveClass() != cl )
+ {
+ expressions->ActivateExpressionClass( cl );
+ }
+
+ CExpression *exp = cl->FindExpression( ConvertPhoneme( tag->GetPhonemeCode() ) );
+ if ( !exp )
+ {
+ Con_Printf( "Couldn't find phoneme '%s'\n", ConvertPhoneme( tag->GetPhonemeCode() ) );
+ return;
+ }
+
+ float *settings = exp->GetSettings();
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ models->GetActiveStudioModel()->SetFlexController( i, settings[j] );
+ }
+}
+
+void PhonemeEditor::OnSAPI( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ g_viewerSettings.speechapiindex = SPEECH_API_SAPI;
+
+ m_pPhonemeExtractor = NULL;
+
+ CheckSpeechAPI();
+
+ redraw();
+}
+
+void PhonemeEditor::OnLipSinc( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ return;
+
+ g_viewerSettings.speechapiindex = SPEECH_API_LIPSINC;
+
+ m_pPhonemeExtractor = NULL;
+
+ CheckSpeechAPI();
+
+ redraw();
+}
+
+void PhonemeEditor::LoadPhonemeConverters()
+{
+ m_pPhonemeExtractor = NULL;
+
+ // Enumerate modules under bin folder of exe
+ FileFindHandle_t findHandle;
+ const char *pFilename = filesystem->FindFirstEx( "phonemeextractors/*.dll", "EXECUTABLE_PATH", &findHandle );
+ while( pFilename )
+ {
+ char fullpath[ 512 ];
+ Q_snprintf( fullpath, sizeof( fullpath ), "phonemeextractors/%s", pFilename );
+
+ Con_Printf( "Loading extractor from %s\n", fullpath );
+
+ Extractor e;
+ e.module = Sys_LoadModule( fullpath );
+ if ( !e.module )
+ {
+ pFilename = filesystem->FindNext( findHandle );
+ continue;
+ }
+
+ CreateInterfaceFn factory = Sys_GetFactory( e.module );
+ if ( !factory )
+ {
+ pFilename = filesystem->FindNext( findHandle );
+ continue;
+ }
+
+ e.extractor = ( IPhonemeExtractor * )factory( VPHONEME_EXTRACTOR_INTERFACE, NULL );
+ if ( !e.extractor )
+ {
+ Warning( "Unable to get IPhonemeExtractor interface version %s from %s\n", VPHONEME_EXTRACTOR_INTERFACE, fullpath );
+ pFilename = filesystem->FindNext( findHandle );
+ continue;
+ }
+
+ e.apitype = e.extractor->GetAPIType();
+
+ g_Extractors.AddToTail( e );
+ pFilename = filesystem->FindNext( findHandle );
+ }
+
+ filesystem->FindClose( findHandle );
+}
+
+void PhonemeEditor::ValidateSpeechAPIIndex()
+{
+ if ( !DoesExtractorExistFor( (PE_APITYPE)g_viewerSettings.speechapiindex ) )
+ {
+ if ( g_Extractors.Count() > 0 )
+ g_viewerSettings.speechapiindex = g_Extractors[0].apitype;
+ }
+}
+
+void PhonemeEditor::UnloadPhonemeConverters()
+{
+ int c = g_Extractors.Count();
+ for ( int i = c - 1; i >= 0; i-- )
+ {
+ Extractor *e = &g_Extractors[ i ];
+ Sys_UnloadModule( e->module );
+ }
+
+ g_Extractors.RemoveAll();
+
+ m_pPhonemeExtractor = NULL;
+}
+
+bool PhonemeEditor::CheckSpeechAPI( void )
+{
+ if ( GetMode() != MODE_PHONEMES )
+ {
+ return false;
+ }
+
+ if ( !m_pPhonemeExtractor )
+ {
+ int c = g_Extractors.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ Extractor *e = &g_Extractors[ i ];
+ if ( e->apitype == (PE_APITYPE)g_viewerSettings.speechapiindex )
+ {
+ m_pPhonemeExtractor = e->extractor;
+ break;
+ }
+ }
+
+ if ( !m_pPhonemeExtractor )
+ {
+ Con_ErrorPrintf( "Couldn't find phoneme extractor %i\n",
+ g_viewerSettings.speechapiindex );
+ }
+ }
+
+ return m_pPhonemeExtractor != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *PhonemeEditor::GetSpeechAPIName( void )
+{
+ CheckSpeechAPI();
+
+ if ( m_pPhonemeExtractor )
+ {
+ return m_pPhonemeExtractor->GetName();
+ }
+
+ return "Unknown Speech API";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::PaintBackground( void )
+{
+ redraw();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : PhonemeEditor::EditorMode
+//-----------------------------------------------------------------------------
+PhonemeEditor::EditorMode PhonemeEditor::GetMode( void ) const
+{
+ return m_CurrentMode;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcWorkSpace -
+// rcEmphasis -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_GetRect( RECT const & rcWorkSpace, RECT& rcEmphasis )
+{
+ rcEmphasis = rcWorkSpace;
+
+ int ybottom = rcWorkSpace.bottom - 2 * m_nTickHeight - 2;
+ int workspaceheight = rcWorkSpace.bottom - rcWorkSpace.top;
+
+ // Just past midpoint
+ rcEmphasis.top = rcWorkSpace.top + workspaceheight / 2 + 2;
+ // 60 units or
+ rcEmphasis.bottom = clamp( rcEmphasis.top + 60, rcEmphasis.top + 20, ybottom );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::OnModeChanged( void )
+{
+ // Show/hide controls as necessary
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_Init( void )
+{
+ m_nNumSelected = 0;
+}
+
+CEmphasisSample *PhonemeEditor::Emphasis_GetSampleUnderMouse( mxEvent *event )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return NULL;
+
+ if ( !m_pWaveFile )
+ return NULL;
+
+ if ( w2() <= 0 )
+ return NULL;
+
+ if ( GetPixelsPerSecond() <= 0 )
+ return NULL;
+
+ float timeperpixel = 1.0f / GetPixelsPerSecond();
+ float closest_dist = 999999.0f;
+ CEmphasisSample *bestsample = NULL;
+
+ int samples = m_Tags.GetNumSamples();
+
+ float clickTime = GetTimeForPixel( (short)event->x );
+
+ for ( int i = 0; i < samples; i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+
+ float dist = fabs( sample->time - clickTime );
+ if ( dist < closest_dist )
+ {
+ bestsample = sample;
+ closest_dist = dist;
+ }
+
+ }
+
+ // Not close to any of them!!!
+ if ( closest_dist > ( 5.0f * timeperpixel ) )
+ {
+ return NULL;
+ }
+
+ return bestsample;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_DeselectAll( void )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+ sample->selected = false;
+ }
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_SelectAll( void )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+ sample->selected = true;
+ }
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_Delete( void )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ for ( int i = m_Tags.GetNumSamples() - 1; i >= 0 ; i-- )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+ if ( !sample->selected )
+ continue;
+
+ m_Tags.m_EmphasisSamples.Remove( i );
+
+ SetDirty( true );
+ }
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : sample -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_AddSample( CEmphasisSample const& sample )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ SetDirty( true );
+
+ PushUndo();
+
+ m_Tags.m_EmphasisSamples.AddToTail( sample );
+ m_Tags.Resort();
+
+ PushRedo();
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_CountSelected( void )
+{
+ m_nNumSelected = 0;
+
+ for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+ if ( !sample || !sample->selected )
+ continue;
+
+ m_nNumSelected++;
+ }
+}
+
+void PhonemeEditor::Emphasis_ShowContextMenu( mxEvent *event )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ CountSelected();
+
+ // Construct main menu
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( m_nNumSelected > 0 )
+ {
+ pop->add( va( "Delete" ), IDC_EMPHASIS_DELETE );
+ pop->add( "Deselect all", IDC_EMPHASIS_DESELECT );
+ }
+ pop->add( "Select all", IDC_EMPHASIS_SELECTALL );
+
+ pop->popup( this, (short)event->x, (short)event->y );
+}
+
+void PhonemeEditor::Emphasis_MouseDrag( int x, int y )
+{
+ if ( m_nDragType != DRAGTYPE_EMPHASIS_MOVE )
+ return;
+
+ RECT rcWork;
+ GetWorkspaceRect( rcWork );
+
+ RECT rc;
+ Emphasis_GetRect( rcWork, rc );
+
+ int height = rc.bottom - rc.top;
+
+ int dx = x - m_nLastX;
+ int dy = y - m_nLastY;
+
+ float dfdx = (float)dx * GetTimePerPixel();
+ float dfdy = (float)dy / (float)height;
+
+ for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+ if ( !sample || !sample->selected )
+ continue;
+
+ sample->time += dfdx;
+ //sample->time = clamp( sample->time, 0.0f, 1.0f );
+
+ sample->value -= dfdy;
+ sample->value = clamp( sample->value, 0.0f, 1.0f );
+ }
+}
+
+void PhonemeEditor::Emphasis_Redraw( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace )
+{
+ if ( GetMode() != MODE_EMPHASIS &&
+ GetMode() != MODE_PHONEMES )
+ return;
+
+ bool fullmode = GetMode() == MODE_EMPHASIS;
+ RECT rcClient;
+
+ Emphasis_GetRect( rcWorkSpace, rcClient );
+
+ RECT rcText;
+ rcText = rcClient;
+
+ InflateRect( &rcText, -15, 0 );
+
+ OffsetRect( &rcText, 0, -20 );
+ rcText.bottom = rcText.top + 20;
+
+ if ( fullmode )
+ {
+ drawHelper.DrawColoredText( "Arial", 15, FW_BOLD, PEColor( COLOR_PHONEME_EMPHASIS_TEXT ), rcText, "Emphasis..." );
+ }
+
+ {
+ int h = rcClient.bottom - rcClient.top;
+ int offset = h/3;
+ RECT rcSpot = rcClient;
+ rcSpot.bottom = rcSpot.top + offset;
+
+ drawHelper.DrawGradientFilledRect(
+ rcSpot,
+ PEColor( COLOR_PHONEME_EMPHASIS_BG_STRONG ),
+ PEColor( COLOR_PHONEME_EMPHASIS_BG ),
+ true );
+
+ OffsetRect( &rcSpot, 0, offset );
+
+ drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_EMPHASIS_BG ), rcSpot );
+
+ OffsetRect( &rcSpot, 0, offset );
+
+ drawHelper.DrawGradientFilledRect(
+ rcSpot,
+ PEColor( COLOR_PHONEME_EMPHASIS_BG ),
+ PEColor( COLOR_PHONEME_EMPHASIS_BG_WEAK ),
+ true );
+ }
+
+ COLORREF gray = PEColor( COLOR_PHONEME_EMPHASIS_MIDLINE );
+
+ drawHelper.DrawOutlinedRect( PEColor( COLOR_PHONEME_EMPHASIS_BORDER ), PS_SOLID, 1, rcClient );
+
+ COLORREF lineColor = PEColor( COLOR_PHONEME_EMPHASIS_LINECOLOR );
+ COLORREF dotColor = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR );
+ COLORREF dotColorSelected = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED );
+
+ int midy = ( rcClient.bottom + rcClient.top ) / 2;
+
+ drawHelper.DrawColoredLine( gray, PS_SOLID, 1, rcClient.left, midy,
+ rcClient.right, midy );
+ int height = rcClient.bottom - rcClient.top;
+ int bottom = rcClient.bottom - 1;
+
+ if ( !m_pWaveFile )
+ return;
+
+ float running_length = m_pWaveFile->GetRunningLength();
+
+ // FIXME: adjust this based on framerate....
+ float timeperpixel = GetTimePerPixel();
+
+ float starttime, endtime;
+ GetScreenStartAndEndTime( starttime, endtime );
+
+ int prevx = 0;
+ float prev_t = starttime;
+ float prev_value = m_Tags.GetIntensity( prev_t, running_length );
+
+ int dx = 5;
+
+ for ( int x = 0; x < ( w2() + dx ); x += dx )
+ {
+ float t = GetTimeForPixel( x );
+
+ float value = m_Tags.GetIntensity( t, running_length );
+
+ // Draw segment
+ drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ prevx,
+ bottom - prev_value * height,
+ x,
+ bottom - value * height );
+
+ prev_t = t;
+ prev_value = value;
+ prevx = x;
+
+ }
+
+ int numsamples = m_Tags.GetNumSamples();
+
+ for ( int sample = 0; sample < numsamples; sample++ )
+ {
+ CEmphasisSample *start = m_Tags.GetSample( sample );
+
+ int x = ( start->time - starttime ) / timeperpixel;
+
+ float value = m_Tags.GetIntensity( start->time, running_length );
+ int y = bottom - value * height;
+
+ int dotsize = 4;
+ int dotSizeSelected = 5;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool PhonemeEditor::Emphasis_IsValid( void )
+{
+ if ( m_Tags.GetNumSamples() > 0 )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void PhonemeEditor::Emphasis_SelectPoints( void )
+{
+ if ( GetMode() != MODE_EMPHASIS )
+ return;
+
+ RECT rcWork, rcEmphasis;
+ GetWorkspaceRect( rcWork );
+
+ Emphasis_GetRect( rcWork, rcEmphasis );
+
+ RECT rcSelection;
+
+ rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ rcSelection.top = max( rcSelection.top, rcEmphasis.top );
+ rcSelection.bottom = min( rcSelection.bottom, rcEmphasis.bottom );
+
+ int eh, ew;
+
+ eh = rcEmphasis.bottom - rcEmphasis.top;
+ ew = rcEmphasis.right - rcEmphasis.left;
+
+ InflateRect( &rcSelection, 5, 5 );
+
+ if ( !w2() || !h2() )
+ return;
+
+ float fleft = GetTimeForPixel( rcSelection.left );
+ float fright = GetTimeForPixel( rcSelection.right );
+
+ float ftop = (float)( rcSelection.top - rcEmphasis.top ) / (float)eh;
+ float fbottom = (float)( rcSelection.bottom- rcEmphasis.top ) / (float)eh;
+
+ //fleft = clamp( fleft, 0.0f, 1.0f );
+ //fright = clamp( fright, 0.0f, 1.0f );
+ ftop = clamp( ftop, 0.0f, 1.0f );
+ fbottom = clamp( fbottom, 0.0f, 1.0f );
+
+ float eps = 0.005;
+
+ for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
+ {
+ CEmphasisSample *sample = m_Tags.GetSample( i );
+
+ if ( sample->time + eps < fleft )
+ continue;
+
+ if ( sample->time - eps > fright )
+ continue;
+
+ if ( (1.0f - sample->value ) + eps < ftop )
+ continue;
+
+ if ( (1.0f - sample->value ) - eps > fbottom )
+ continue;
+
+ sample->selected = true;
+ }
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcHandle -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::GetScrubHandleRect( RECT& rcHandle, bool clipped )
+{
+ float pixel = 0.0f;
+
+ if ( m_pWaveFile )
+ {
+ float currenttime = m_flScrub;
+ float starttime, endtime;
+ GetScreenStartAndEndTime( starttime, endtime );
+
+ float screenfrac = ( currenttime - starttime ) / ( endtime - starttime );
+
+ pixel = screenfrac * w2();
+
+ 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() + 12;
+ rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rcArea -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::GetScrubAreaRect( RECT& rcArea )
+{
+ rcArea.left = 0;
+ rcArea.right = w2();
+ rcArea.top = 2 + GetCaptionHeight() + 12;
+ rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4;
+}
+
+void PhonemeEditor::DrawScrubHandle()
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+ rcHandle.left = 0;
+ rcHandle.right = w2();
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcHandle );
+
+ DrawScrubHandle( drawHelper );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcHandle -
+//-----------------------------------------------------------------------------
+void PhonemeEditor::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper )
+{
+ RECT rcHandle;
+ GetScrubHandleRect( rcHandle, true );
+
+ HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
+
+ COLORREF areaBorder = RGB( 230, 230, 220 );
+
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
+ drawHelper.DrawColoredLine( areaBorder,
+ PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
+
+ 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 PhonemeEditor::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 PhonemeEditor::IsMouseOverScrubArea( mxEvent *event )
+{
+ RECT rcArea;
+
+ rcArea.left = 0;
+ rcArea.right = w2();
+ rcArea.top = 2 + GetCaptionHeight() + 12;
+ rcArea.bottom = rcArea.top + 10;
+
+ InflateRect( &rcArea, 2, 2 );
+
+ POINT pt;
+ pt.x = (short)event->x;
+ pt.y = (short)event->y;
+ if ( PtInRect( &rcArea, pt ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+float PhonemeEditor::GetTimeForSample( int sample )
+{
+ if ( !m_pWaveFile )
+ {
+ return 0.0f;
+ }
+
+ float duration = m_pWaveFile->GetRunningLength();
+ int sampleCount = m_pWaveFile->SampleCount();
+ if ( sampleCount <= 0 )
+ return 0.0f;
+
+ float frac = (float)sample / (float)sampleCount;
+
+ return frac * duration;
+}
+
+void PhonemeEditor::ClampTimeToSelectionInterval( float& timeval )
+{
+ if ( !m_pWaveFile )
+ {
+ return;
+ }
+ if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
+ {
+ return;
+ }
+
+ if ( !m_bSelectionActive )
+ return;
+
+ float starttime = GetTimeForSample( m_nSelection[ 0 ] );
+ float endtime = GetTimeForSample( m_nSelection[ 1 ] );
+
+ Assert( starttime <= endtime );
+
+ timeval = clamp( timeval, starttime, endtime );
+}
+
+void PhonemeEditor::ScrubThink( float dt, bool scrubbing )
+{
+ ClampTimeToSelectionInterval( m_flScrub );
+ ClampTimeToSelectionInterval( m_flScrubTarget );
+
+ if ( m_flScrubTarget == m_flScrub && !scrubbing )
+ {
+ if ( sound->IsSoundPlaying( m_pMixer ) )
+ {
+ sound->StopSound( m_pMixer );
+ }
+ return;
+ }
+
+ if ( !m_pWaveFile )
+ return;
+
+ bool newmixer = false;
+ if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
+ {
+ m_pMixer = NULL;
+ SaveLinguisticData();
+
+ StudioModel *model = NULL;//models->GetActiveStudioModel();
+
+ sound->PlaySound( model, VOL_NORM, m_WorkFile.m_szWorkingFile, &m_pMixer );
+ newmixer = true;
+ }
+
+ if ( !m_pMixer )
+ {
+ return;
+ }
+
+ if ( m_flScrub > m_flScrubTarget )
+ {
+ m_pMixer->SetDirection( false );
+ }
+ else
+ {
+ m_pMixer->SetDirection( true );
+ }
+
+ float duration = m_pWaveFile->GetRunningLength();
+ if ( !duration )
+ return;
+
+ float d = m_flScrubTarget - m_flScrub;
+ int sign = d > 0.0f ? 1 : -1;
+
+ float maxmove = dt * m_flPlaybackRate;
+
+ 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;
+ }
+ }
+
+ int sampleCount = m_pMixer->GetSource()->SampleCount();
+
+ int cursample = sampleCount * ( m_flScrub / duration );
+
+ int realsample = m_pMixer->GetSamplePosition();
+
+ int dsample = cursample - realsample;
+
+ int onehundredth = m_pMixer->GetSource()->SampleRate() * 0.01f;
+
+ if ( abs( dsample ) > onehundredth )
+ {
+ m_pMixer->SetSamplePosition( cursample, true );
+ }
+ m_pMixer->SetActive( true );
+
+ RECT rcArea;
+ GetScrubAreaRect( rcArea );
+
+ CChoreoWidgetDrawHelper drawHelper( this, rcArea );
+ DrawScrubHandle( drawHelper );
+
+ if ( scrubbing )
+ {
+ g_pMatSysWindow->Frame();
+ }
+}
+
+void PhonemeEditor::SetScrubTime( float t )
+{
+ m_flScrub = t;
+ ClampTimeToSelectionInterval( m_flScrub );
+}
+
+void PhonemeEditor::SetScrubTargetTime( float t )
+{
+ m_flScrubTarget = t;
+ ClampTimeToSelectionInterval( m_flScrubTarget );
+}
+
+
+void PhonemeEditor::OnToggleVoiceDuck()
+{
+ SetDirty( true );
+ PushUndo();
+ m_Tags.SetVoiceDuck( !m_Tags.GetVoiceDuck() );
+ PushRedo();
+ redraw();
+}
+
+void PhonemeEditor::Play()
+{
+ PlayEditedWave( m_bSelectionActive );
+}
+
+
+
+
+
diff --git a/utils/hlfaceposer/phonemeeditor.h b/utils/hlfaceposer/phonemeeditor.h
new file mode 100644
index 0000000..98972c7
--- /dev/null
+++ b/utils/hlfaceposer/phonemeeditor.h
@@ -0,0 +1,591 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PHONEEDITOR_H
+#define PHONEEDITOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+class CAudioSource;
+class CAudioMixer;
+class mxBitmapButton;
+class mxButton;
+
+#include "utlvector.h"
+#include "faceposertoolwindow.h"
+
+#define IDC_PHONEME_SCROLL 1001
+#define IDC_PHONEME_PLAY_ORIG 1002
+#define IDC_EDIT_PHONEME 1004
+#define IDC_EDIT_INSERTPHONEMEBEFORE 1005
+#define IDC_EDIT_INSERTPHONEMEAFTER 1006
+#define IDC_EDIT_DELETEPHONEME 1007
+
+#define IDC_PLAY_EDITED_SELECTION 1008
+
+#define IDC_REDO_PHONEMEEXTRACTION 1009
+#define IDC_REDO_PHONEMEEXTRACTION_SELECTION 1010
+#define IDC_DESELECT 1011
+#define IDC_PLAY_EDITED 1012
+#define IDC_SAVE_LINGUISTIC 1013
+#define IDC_CANCELPLAYBACK 1014
+
+#define IDC_EDITWORDLIST 1015
+#define IDC_SNAPWORDS 1016
+#define IDC_SEPARATEWORDS 1017
+#define IDC_LOADWAVEFILE 1018
+#define IDC_SNAPPHONEMES 1019
+#define IDC_SEPARATEPHONEMES 1020
+
+#define IDC_COMMITEXTRACTED 1021
+#define IDC_CLEAREXTRACTED 1022
+
+#define IDC_ADDTAG 1023
+#define IDC_DELETETAG 1024
+
+#define IDC_CVUNDO 1025
+#define IDC_CVREDO 1026
+
+#define IDC_EDIT_DELETEWORD 1027
+#define IDC_EDIT_WORD 1028
+#define IDC_EDIT_INSERTWORDBEFORE 1029
+#define IDC_EDIT_INSERTWORDAFTER 1030
+#define IDC_EDIT_INSERTFIRSTPHONEMEOFWORD 1031
+
+#define IDC_SELECT_WORDSRIGHT 1032
+#define IDC_SELECT_WORDSLEFT 1033
+#define IDC_SELECT_PHONEMESRIGHT 1034
+#define IDC_SELECT_PHONEMESLEFT 1035
+
+#define IDC_DESELECT_PHONEMESANDWORDS 1036
+#define IDC_CLEANUP 1037
+#define IDC_CLEARUNDO 1038
+
+#define IDC_PLAYBUTTON 1039
+
+#define IDC_MODE_TAB 1040
+
+#define IDC_EMPHASIS_DELETE 1041
+#define IDC_EMPHASIS_DESELECT 1042
+#define IDC_EMPHASIS_SELECTALL 1043
+
+#define IDC_PLAYBACKRATE 1044
+
+#define IDC_REALIGNPHONEMES 1045
+#define IDC_REALIGNWORDS 1046
+
+// Support for multiple speech api's
+#define IDC_API_SAPI 1050
+#define IDC_API_LIPSINC 1051
+
+#define IDC_EXPORT_SENTENCE 1075
+#define IDC_IMPORT_SENTENCE 1076
+#define IDC_TOGGLE_VOICEDUCK 1077
+
+#define IDC_PE_LANGUAGESTART 1100
+// #define IDC_PE_LANGUAGEEND 1106 or so
+
+class IterateOutputRIFF;
+class IterateRIFF;
+class CChoreoWidgetDrawHelper;
+class CChoreoEvent;
+class CEventRelativeTag;
+class CChoreoView;
+class IPhonemeExtractor;
+class CPhonemeModeTab;
+class mxPopupMenu;
+
+#include "sentence.h"
+
+enum
+{
+ COLOR_PHONEME_BACKGROUND = 0,
+ COLOR_PHONEME_TEXT,
+ COLOR_PHONEME_LIGHTTEXT,
+ COLOR_PHONEME_PLAYBACKTICK,
+ COLOR_PHONEME_WAVDATA,
+ COLOR_PHONEME_TIMELINE,
+ COLOR_PHONEME_TIMELINE_MAJORTICK,
+ COLOR_PHONEME_TIMELINE_MINORTICK,
+ COLOR_PHONEME_EXTRACTION_RESULT_FAIL,
+ COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS,
+ COLOR_PHONEME_EXTRACTION_RESULT_ERROR,
+ COLOR_PHONEME_EXTRACTION_RESULT_OTHER,
+ COLOR_PHONEME_TAG_BORDER,
+ COLOR_PHONEME_TAG_BORDER_SELECTED,
+ COLOR_PHONEME_TAG_FILLER_NORMAL,
+ COLOR_PHONEME_TAG_SELECTED,
+ COLOR_PHONEME_TAG_TEXT,
+ COLOR_PHONEME_TAG_TEXT_SELECTED,
+ COLOR_PHONEME_WAV_ENDPOINT,
+ COLOR_PHONEME_AB,
+ COLOR_PHONEME_AB_LINE,
+ COLOR_PHONEME_AB_TEXT,
+
+ COLOR_PHONEME_ACTIVE_BORDER,
+ COLOR_PHONEME_SELECTED_BORDER,
+ COLOR_PHONEME_TIMING_TAG,
+
+ COLOR_PHONEME_EMPHASIS_BG,
+ COLOR_PHONEME_EMPHASIS_BG_STRONG,
+ COLOR_PHONEME_EMPHASIS_BG_WEAK,
+
+ COLOR_PHONEME_EMPHASIS_BORDER,
+ COLOR_PHONEME_EMPHASIS_LINECOLOR,
+ COLOR_PHONEME_EMPHASIS_DOTCOLOR,
+ COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED,
+ COLOR_PHONEME_EMPHASIS_TEXT,
+ COLOR_PHONEME_EMPHASIS_MIDLINE,
+
+ NUM_COLORS,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Shows WAV data and allows blanking it out and tweaking phoneme tags
+//-----------------------------------------------------------------------------
+class PhonemeEditor : public mxWindow, public IFacePoserToolWindow
+{
+public:
+ enum
+ {
+ BOUNDARY_NONE = 0,
+ BOUNDARY_PHONEME,
+ BOUNDARY_WORD,
+ };
+
+ typedef enum
+ {
+ MODE_PHONEMES = 0,
+ MODE_EMPHASIS
+ } EditorMode;
+
+ // Construction
+ PhonemeEditor( mxWindow *parent );
+ ~PhonemeEditor( void );
+
+ virtual void Think( float dt );
+
+ virtual void OnDelete();
+ virtual bool CanClose();
+
+ void ValidateSpeechAPIIndex();
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void redraw( void );
+ virtual bool PaintBackground( void );
+
+ EditorMode GetMode( void ) const;
+ void SetupPhonemeEditorColors( void );
+ COLORREF PEColor( int colornum );
+ void OnModeChanged( void );
+
+ // Change wave file being edited
+ void SetCurrentWaveFile( const char *wavefile, bool force = false, CChoreoEvent *event = NULL );
+
+ // called when scene is unloaded in choreview or when event/channel/actor gets deleted
+ // so we don't have dangling pointers to tags, events, scene
+ void ClearEvent( void );
+
+ void Play();
+
+private:
+ void DrawWords( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence &sentence, int type, bool showactive = true );
+ void DrawPhonemes( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence &sentence, int type, bool showactive = true );
+ void DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc );
+
+ void Emphasis_Redraw( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace );
+ void Emphasis_GetRect( RECT const & rcWorkSpace, RECT& rcEmphasis );
+
+ void Emphasis_Init( void );
+ CEmphasisSample *Emphasis_GetSampleUnderMouse( mxEvent *event );
+ void Emphasis_DeselectAll( void );
+ void Emphasis_SelectAll( void );
+ void Emphasis_Delete( void );
+ void Emphasis_AddSample( CEmphasisSample const& sample );
+ void Emphasis_CountSelected( void );
+ void Emphasis_ShowContextMenu( mxEvent *event );
+ void Emphasis_MouseDrag( int x, int y );
+ bool Emphasis_IsValid( void );
+ void Emphasis_SelectPoints( void );
+
+ // Data
+ int m_nNumSelected;
+
+ // Readjust slider
+ void MoveTimeSliderToPos( int x );
+
+ // Handle scrollbar
+ void SetTimeZoomScale( int scale );
+ float GetTimeZoomScale( void );
+ float GetPixelsPerSecond( void );
+ // Adjust scroll bars
+ void RepositionHSlider( void );
+
+ // Edit commands
+ void EditPhoneme( CPhonemeTag *pPhoneme, bool positionDialog = false );
+ void EditPhoneme( void );
+ void EditInsertPhonemeBefore( void );
+ void EditInsertPhonemeAfter( void );
+ void EditDeletePhoneme( void );
+
+ void SelectPhonemes( bool forward );
+
+ void EditInsertFirstPhonemeOfWord( void );
+
+ void EditWord( CWordTag *pWord, bool positionDialog = false );
+ void EditWord( void );
+ void EditInsertWordBefore( void );
+ void EditInsertWordAfter( void );
+ void EditDeleteWord( void );
+
+ void SelectWords( bool forward );
+
+ // Edit word list
+ void EditWordList( void );
+ void SentenceFromString( CSentence& sentence, char const *str );
+
+
+ // Wav processing commands
+ void RedoPhonemeExtraction( void );
+ // Redo extraction of selected words only
+ void RedoPhonemeExtractionSelected( void );
+ void Deselect( void );
+
+ void PlayEditedWave( bool selection = false );
+ void CommitChanges( void );
+
+ // Context menu
+ void ShowPhonemeMenu( CPhonemeTag *pho, int mx, int my );
+ void ShowWordMenu( CWordTag *word, int mx, int my );
+
+ void ShowContextMenu( int mx, int my );
+ void ShowContextMenu_Phonemes( int mx, int my );
+ void ShowContextMenu_Emphasis( int mx, int my );
+
+ // UI helpers
+ void GetWorkspaceRect( RECT &rc );
+
+
+ bool IsMouseOverWordRow( int my );
+ bool IsMouseOverPhonemeRow( int my );
+
+ int IsMouseOverBoundary( mxEvent *event );
+ int GetWordUnderMouse( int mx, int my );
+ int ComputeHPixelsNeeded( void );
+ void DrawFocusRect( char *reason );
+ void StartDragging( int dragtype, int startx, int starty, HCURSOR cursor );
+
+ void FinishPhonemeMove( int startx, int endx );
+ void FinishPhonemeDrag( int startx, int endx );
+ void FinishWordMove( int startx, int endx );
+ void FinishWordDrag( int startx, int endx );
+
+ float GetTimeForPixel( int mx );
+ void GetScreenStartAndEndTime( float &starttime, float& endtime );
+ float GetTimePerPixel( void );
+ int GetSampleForMouse( int mx );
+ int GetPixelForSample( int sample );
+
+ bool FindSpanningPhonemes( float time, CPhonemeTag **pp1, CPhonemeTag **pp2 );
+ bool FindSpanningWords( float time, CWordTag **pp1, CWordTag **pp2 );
+ int FindWordForTime( float time );
+ CPhonemeTag *FindPhonemeForTime( float time );
+ void DeselectWords( void );
+ void SnapWords( void );
+ void SeparateWords( void );
+
+ void DeselectPhonemes( void );
+ void SnapPhonemes( void );
+ void SeparatePhonemes( void );
+
+ void CreateEvenWordDistribution( const char *wordlist );
+
+ // Dirty flag
+ void SetDirty( bool dirty, bool clearundo = true );
+ bool GetDirty( void );
+
+ // FIXME: Do something else here
+ void ResampleChunk( IterateOutputRIFF& store, void *format, int chunkname, char *buffer, int buffersize, int start_silence = 0, int end_silence = 0 );
+
+ // Mouse control over selected samples
+ void SelectSamples( int start, int end );
+ void FinishSelect( int startx, int mx );
+ void FinishMoveSelection( int startx, int mx );
+ void FinishMoveSelectionStart( int startx, int mx );
+ void FinishMoveSelectionEnd( int startx, int mx );
+
+ bool IsMouseOverSamples( int mx, int my );
+ bool IsMouseOverSelection( int mx, int my );
+ bool IsMouseOverSelectionStartEdge( mxEvent *event );
+ bool IsMouseOverSelectionEndEdge( mxEvent *event );
+
+ bool IsMouseOverTag( int mx, int my );
+ void FinishEventTagDrag( int startx, int endx );
+ CEventRelativeTag *GetTagUnderMouse( int mx );
+ bool IsMouseOverTagRow( int my );
+ void ShowTagMenu( int mx, int my );
+ void AddTag( void );
+ void DeleteTag( void );
+
+ // After running liset/sapi, retrieve phoneme tag data from stream
+ void RetrieveLinguisticData( void );
+
+ // Copy current phoneme chunk over existing data chunk of .wav file
+ void SaveLinguisticData( void );
+ void StoreValveDataChunk( IterateOutputRIFF& store );
+
+ void ExportValveDataChunk( char const *tempfile );
+ void ImportValveDataChunk( char const *tempfile );
+
+ void OnImport();
+ void OnExport();
+
+ // Playback (returns true if sound had been playing)
+ bool StopPlayback( void );
+
+ CPhonemeTag *GetPhonemeTagUnderMouse( int mx, int my );
+ CWordTag *GetWordTagUnderMouse( int mx, int my );
+
+ void ReadLinguisticTags( void );
+
+ void LoadWaveFile( void );
+
+ void GetPhonemeTrayTopBottom( RECT& rc );
+ void GetWordTrayTopBottom( RECT& rc );
+
+ void GetWordRect( const CWordTag *tag, RECT& rc );
+ void GetPhonemeRect( const CPhonemeTag *tag, RECT& rc );
+ int GetMouseForTime( float time );
+
+ void CommitExtracted( void );
+ void ClearExtracted( void );
+
+ const char * GetExtractionResultString( int resultCode );
+
+ void AddFocusRect( RECT& rc );
+
+ void CountSelected( void );
+
+ typedef void (PhonemeEditor::*PEWORDITERFUNC)( CWordTag *word, float fparam );
+ typedef void (PhonemeEditor::*PEPHONEMEITERFUNC)( CPhonemeTag *phoneme, CWordTag *word, float fparam );
+
+ void TraverseWords( PEWORDITERFUNC pfn, float fparam );
+ void TraversePhonemes( PEPHONEMEITERFUNC pfn, float fparam );
+
+ // Iteration functions
+ void ITER_MoveSelectedWords( CWordTag *word, float amount );
+ void ITER_MoveSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount );
+
+ void ITER_ExtendSelectedPhonemeEndTimes( CPhonemeTag *phoneme, CWordTag *word, float amount );
+ void ITER_ExtendSelectedWordEndTimes( CWordTag *word, float amount );
+
+ void ITER_AddFocusRectSelectedWords( CWordTag *word, float amount );
+ void ITER_AddFocusRectSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount );
+
+ void ITER_CountSelectedWords( CWordTag *word, float amount );
+ void ITER_CountSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount );
+
+ void ITER_SelectSpanningWords( CWordTag *word, float amount );
+
+// Undo/Redo
+ void Undo( void );
+ void Redo( void );
+ void ClearUndo( void );
+
+ // Do push before changes
+ void PushUndo( void );
+ // Do this push after changes, must match pushundo 1for1
+ void PushRedo( void );
+
+ void WipeUndo( void );
+ void WipeRedo( void );
+
+ CPhonemeTag *GetClickedPhoneme( void );
+ CWordTag *GetClickedWord( void );
+ void SetClickedPhoneme( int word, int phoneme );
+
+ void ShiftSelectedPhoneme( int direction );
+ void ExtendSelectedPhonemeEndTime( int direction );
+ void SelectNextPhoneme( int direction );
+ void SelectNextWord( int direction );
+ bool IsPhonemeSelected( CWordTag *word );
+ void ShiftSelectedWord( int direction );
+ void ExtendSelectedWordEndTime( int direction );
+
+ float GetTimeGapToNextWord( bool forward, CWordTag *currentWord, CWordTag **ppNextWord = NULL );
+ float GetTimeGapToNextPhoneme( bool forward, CPhonemeTag *currentPhoneme, CWordTag **ppword = NULL, CPhonemeTag **phoneme = NULL );
+ int IndexOfWord( CWordTag *word );
+ CPhonemeTag *GetSelectedPhoneme( void );
+ CWordTag *GetSelectedWord( void );
+
+ void OnMouseMove( mxEvent *event );
+
+ bool AreSelectedWordsContiguous( void );
+ bool AreSelectedPhonemesContiguous( void );
+
+ bool CreateCroppedWave( char const *filename, int startsample, int endsample );
+ void CleanupWordsAndPhonemes( bool prepareundo );
+ void RealignPhonemesToWords( bool prepareundo );
+ void RealignWordsToPhonemes( bool prepareundo );
+ void SortWords( bool prepareundo );
+ void SortPhonemes( bool prepareundo );
+
+ float ComputeMaxWordShift( bool forward, bool allowcrop );
+ float ComputeMaxPhonemeShift( bool forward, bool allowcrop );
+
+ int PixelsForDeltaTime( float dt );
+
+ void ClearDragLimit( void );
+ void SetDragLimit( int dragtype );
+ void LimitDrag( int& mousex );
+
+ void SelectExpression( CPhonemeTag *tag );
+
+ void OnSAPI( void );
+ void OnLipSinc( void );
+
+ bool CheckSpeechAPI( void );
+ char const *GetSpeechAPIName( void );
+
+ void LoadPhonemeConverters();
+ void UnloadPhonemeConverters();
+
+ bool IsMouseOverScrubHandle( mxEvent *event );
+ bool IsMouseOverScrubArea( mxEvent *event );
+ void GetScrubHandleRect( RECT& rcHandle, bool clipped = false );
+ void GetScrubAreaRect( RECT& rcArea );
+ void DrawScrubHandle();
+
+ void DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper );
+ void ScrubThink( float dt, bool scrubbing );
+
+ void SetScrubTime( float t );
+ void SetScrubTargetTime( float t );
+
+ float GetTimeForSample( int sample );
+ void ClampTimeToSelectionInterval( float& timeval );
+ void OnToggleVoiceDuck();
+
+ // Data
+private:
+ // Type of mouse movement
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_PHONEME ,
+ DRAGTYPE_WORD,
+ DRAGTYPE_SELECTSAMPLES,
+ DRAGTYPE_MOVESELECTION,
+ DRAGTYPE_MOVESELECTIONSTART,
+ DRAGTYPE_MOVESELECTIONEND,
+ DRAGTYPE_MOVEWORD,
+ DRAGTYPE_MOVEPHONEME,
+ DRAGTYPE_EVENTTAG_MOVE,
+ DRAGTYPE_EMPHASIS_SELECT,
+ DRAGTYPE_EMPHASIS_MOVE,
+ DRAGTYPE_SCRUBBER
+ };
+
+ float m_flScrub;
+ float m_flScrubTarget;
+
+ EditorMode m_CurrentMode;
+ // Graph scale
+ float m_flPixelsPerSecond;
+ // Graph scale
+ int m_nTimeZoom;
+ int m_nTimeZoomStep;
+
+ int m_nTickHeight;
+
+ // Current wave file
+ CAudioSource *m_pWaveFile;
+ CAudioMixer *m_pMixer;
+ CChoreoEvent *m_pEvent;
+ int m_nClickX;
+
+ struct CWorkFile
+ {
+ public:
+ char m_szWaveFile[ 256 ];
+ char m_szWorkingFile[ 256 ];
+ char m_szBasePath[ 256 ];
+ bool m_bDirty;
+ };
+ CWorkFile m_WorkFile;
+
+ mxScrollbar *m_pHorzScrollBar;
+ // Current sb value
+ int m_nLeftOffset;
+
+ CPhonemeModeTab *m_pModeTab;
+
+ mxSlider *m_pPlaybackRate;
+ float m_flPlaybackRate;
+
+ mxButton *m_btnRedoPhonemeExtraction;
+ mxButton *m_btnSave;
+ mxButton *m_btnLoad;
+
+ mxButton *m_btnPlay; // selection or full depending
+
+ // Mouse dragging
+ HCURSOR m_hPrevCursor;
+
+ int m_nStartX;
+ int m_nStartY;
+ int m_nLastX;
+ int m_nLastY;
+ int m_nDragType;
+ struct CFocusRect
+ {
+ RECT m_rcOrig;
+ RECT m_rcFocus;
+ };
+ CUtlVector < CFocusRect > m_FocusRects;
+
+ int m_nClickedPhoneme;
+ int m_nClickedWord;
+
+ // Current set of tags
+ CSentence m_Tags;
+
+ CSentence m_TagsExt;
+
+ int m_nSelection[ 2 ];
+ bool m_bSelectionActive;
+
+ int m_nLastExtractionResult;
+
+ int m_nSelectedPhonemeCount;
+ int m_nSelectedWordCount;
+
+ bool m_bWordsActive;
+
+ struct PEUndo
+ {
+ CSentence *undo;
+ CSentence *redo;
+ };
+
+ CUtlVector< PEUndo * > m_UndoStack;
+ int m_nUndoLevel;
+ bool m_bRedoPending;
+
+ bool m_bLimitDrag;
+ int m_nLeftLimit;
+ int m_nRightLimit;
+
+ IPhonemeExtractor *m_pPhonemeExtractor;
+ float m_flScrubberTimeOffset;
+};
+
+extern PhonemeEditor *g_pPhonemeEditor;
+
+#endif // PHONEEDITOR_H
diff --git a/utils/hlfaceposer/phonemeeditorcolors.h b/utils/hlfaceposer/phonemeeditorcolors.h
new file mode 100644
index 0000000..cb68c06
--- /dev/null
+++ b/utils/hlfaceposer/phonemeeditorcolors.h
@@ -0,0 +1,14 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PHONEMEEDITORCOLORS_H
+#define PHONEMEEDITORCOLORS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#endif // PHONEMEEDITORCOLORS_H
diff --git a/utils/hlfaceposer/phonemeproperties.cpp b/utils/hlfaceposer/phonemeproperties.cpp
new file mode 100644
index 0000000..c1e2da2
--- /dev/null
+++ b/utils/hlfaceposer/phonemeproperties.cpp
@@ -0,0 +1,434 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <mxtk/mx.h>
+#include "resource.h"
+#include "PhonemeProperties.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "mdlviewer.h"
+
+static CPhonemeParams g_Params;
+
+static int g_nPhonemeCount = 0;
+static HWND *g_rgButtons = NULL;
+
+#define IDC_PHONEME 2000
+
+#define PHONEME_WIDTH 50
+#define PHONEME_HEIGHT 18
+#define PHONEME_GAP 10
+#define PHONEME_VGAP 5
+
+typedef long (__stdcall *WINPROCTYPE)( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+static WINPROCTYPE lpfnOldButtonProc;
+
+static BOOL CALLBACK PhonemeBtnProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch ( uMsg )
+ {
+ case WM_MOUSEMOVE:
+ {
+ HWND dialog = GetParent( hwnd );
+ if ( dialog )
+ {
+ // Get the hint text item
+ HWND helpText = GetDlgItem( dialog, IDC_STATIC_HELPTEXT );
+ if ( helpText )
+ {
+ CExpression *exp = ( CExpression * )GetWindowLong( (HWND)hwnd, GWL_USERDATA );
+ if ( exp )
+ {
+ SendMessage( helpText, WM_SETTEXT, 0, (LPARAM)exp->description );
+ }
+ }
+ }
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ return CallWindowProc( lpfnOldButtonProc, hwnd, uMsg, wParam, lParam );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : phoneme -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void ClickedPhoneme( HWND hwndDlg, int phoneme )
+{
+ HWND ctrl = GetDlgItem( hwndDlg, IDC_EDIT_PHONEME );
+ if ( !ctrl )
+ return;
+
+ if ( !g_Params.m_bMultiplePhoneme )
+ {
+ g_Params.m_szName[ 0 ] = 0;
+ }
+ else
+ {
+ SendMessage( ctrl, WM_GETTEXT, (WPARAM)sizeof( g_Params.m_szName ), (LPARAM)g_Params.m_szName );
+ }
+ if ( phoneme >= g_nPhonemeCount || phoneme < 0 )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ HWND button = g_rgButtons[ phoneme ];
+ CExpression *exp = ( CExpression * )GetWindowLong( (HWND)button, GWL_USERDATA );
+ if ( exp )
+ {
+ if ( strlen( g_Params.m_szName ) > 0 )
+ {
+ strcat( g_Params.m_szName, " " );
+ }
+ strcat( g_Params.m_szName, exp->name );
+
+ if ( g_Params.m_bMultiplePhoneme )
+ {
+ SetFocus( ctrl );
+ SendMessage( ctrl, WM_SETTEXT, 0, (LPARAM)g_Params.m_szName );
+ SendMessage( ctrl, EM_SETSEL, 0, MAKELONG(0, 0xffff) );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void CreateAndLayoutControls( HWND hwndDlg, CPhonemeParams* params )
+{
+ g_nPhonemeCount = 0;
+ // Find phomemes
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ CExpClass *cl = expressions->FindClass( "phonemes", true );
+ if ( !cl )
+ return;
+
+ g_nPhonemeCount = cl->GetNumExpressions();
+ if ( g_nPhonemeCount == 0 )
+ return;
+
+ g_rgButtons = new HWND[ g_nPhonemeCount ];
+ Assert( g_rgButtons );
+
+ int columns = 7;
+ int rows = ( g_nPhonemeCount / columns ) + 1;
+
+ int dialogW = columns * ( PHONEME_WIDTH + PHONEME_GAP ) + 2 * PHONEME_GAP;
+ int dialogH = rows * ( PHONEME_HEIGHT + PHONEME_VGAP ) + 40 + 55 + 30;
+
+ int startx = PHONEME_GAP;
+ int starty = 40;
+
+ if ( params->m_bPositionDialog )
+ {
+ int top = params->m_nTop - dialogH - 5;
+ int left = params->m_nLeft;
+
+ MoveWindow( hwndDlg,
+ left,
+ top,
+ dialogW,
+ dialogH,
+ TRUE );
+ }
+ else
+ {
+ MoveWindow( hwndDlg,
+ ( GetSystemMetrics( SM_CXFULLSCREEN ) - dialogW ) / 2,
+ ( GetSystemMetrics( SM_CYFULLSCREEN ) - dialogH ) / 2,
+ dialogW,
+ dialogH,
+ TRUE );
+ }
+
+ HWND ctrl = GetDlgItem( hwndDlg, IDOK );
+ if ( ctrl )
+ {
+ MoveWindow( ctrl, dialogW - 220, dialogH - 58, 100, 20, TRUE );
+ }
+ ctrl = GetDlgItem( hwndDlg, IDCANCEL );
+ if ( ctrl )
+ {
+ MoveWindow( ctrl, dialogW - 110, dialogH - 58, 100, 20, TRUE );
+ }
+ ctrl = GetDlgItem( hwndDlg, IDC_PHONEMETEXTPROMPT );
+ if ( ctrl )
+ {
+ MoveWindow( ctrl, startx, dialogH - 55, 50, 20, TRUE );
+ }
+ ctrl = GetDlgItem( hwndDlg, IDC_EDIT_PHONEME );
+ if ( ctrl )
+ {
+ MoveWindow( ctrl, startx + 50, dialogH - 58, 100, 20, TRUE );
+ }
+ ctrl = GetDlgItem( hwndDlg, IDC_STATIC_HELPTEXT );
+ if ( ctrl )
+ {
+ MoveWindow( ctrl, startx, dialogH - 85, dialogW - startx - 20, 20, TRUE );
+ }
+
+ int r = 0;
+ int c = 0;
+ for ( int i = 0; i < g_nPhonemeCount; i++ )
+ {
+ CExpression *exp = cl->GetExpression( i );
+ if ( !exp )
+ continue;
+
+ HWND button = CreateWindowEx(
+ 0,
+ "BUTTON",
+ va( "%s", exp->name ),
+ WS_CHILD | WS_VISIBLE | BS_LEFT,
+ startx + c * ( PHONEME_WIDTH + PHONEME_GAP ),
+ starty + r * ( PHONEME_HEIGHT + PHONEME_VGAP ),
+ PHONEME_WIDTH,
+ PHONEME_HEIGHT,
+ hwndDlg,
+ (HMENU)( IDC_PHONEME + i ),
+ (HINSTANCE)GetModuleHandle( 0 ),
+ NULL );
+ Assert( button );
+ SetWindowLong( (HWND)button, GWL_USERDATA, (LONG)exp );
+
+ // Subclass it
+ lpfnOldButtonProc = (WINPROCTYPE)SetWindowLong( (HWND)button, GWL_WNDPROC, (LONG)PhonemeBtnProc );
+
+ SendMessage ((HWND)button, WM_SETFONT, (WPARAM) (HFONT) GetStockObject (ANSI_VAR_FONT), MAKELPARAM (TRUE, 0));
+
+ g_rgButtons[ i ] = button;
+
+ c++;
+ if ( c >= columns )
+ {
+ r++;
+ c = 0;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void DestroyControls( HWND hwndDlg )
+{
+ for ( int i = 0 ; i < g_nPhonemeCount; i++ )
+ {
+ if ( g_rgButtons[ i ] )
+ {
+ DestroyWindow( g_rgButtons[ i ] );
+ g_rgButtons[ i ] = NULL;
+ }
+ }
+
+ delete[] g_rgButtons;
+ g_nPhonemeCount = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// Output : static void
+//-----------------------------------------------------------------------------
+static void PhonemePropertiesDialogExit( HWND hwndDlg, int exitCode )
+{
+ DestroyControls( hwndDlg );
+
+ EndDialog( hwndDlg, exitCode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : allowmultiple -
+// *input -
+// *output -
+// Output : static bool
+//-----------------------------------------------------------------------------
+static bool ValidatePhonemeString( bool allowmultiple, char const *input, char *output )
+{
+ // Make sure phonemes are loaded
+ FacePoser_EnsurePhonemesLoaded();
+
+ CExpClass *cl = expressions->FindClass( "phonemes", true );
+ if ( !cl )
+ return false;
+
+ if ( !input || !input[ 0 ] )
+ return false;
+
+ // Go one by one
+ int count = 1;
+ char phoneme[ 128 ];
+ char *in, *out;
+
+ *output = 0;
+
+ in = (char *)input;
+ do
+ {
+ out = phoneme;
+
+ while ( *in > 32 )
+ {
+ *out++ = *in++;
+ }
+ *out = 0;
+
+ // Validate phoneme entered
+ for ( int i = 0; i < g_nPhonemeCount; i++ )
+ {
+ CExpression *exp = cl->GetExpression( i );
+ if ( !exp )
+ continue;
+
+ if ( !stricmp( exp->name, phoneme ) )
+ {
+ // Found it
+ if ( count != 1 )
+ {
+ strcat( output, " " );
+ }
+ strcat( output, phoneme );
+ break;
+ }
+ }
+
+ if ( !*in )
+ break;
+
+ // Skip whitespace
+ in++;
+ count++;
+
+ // Only keep first one
+ if ( !allowmultiple )
+ break;
+
+ } while ( 1 );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK PhonemePropertiesDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ CreateAndLayoutControls( hwndDlg, &g_Params );
+
+ HWND control = GetDlgItem( hwndDlg, IDC_PHONEMENAME );
+ if ( control )
+ {
+ SendMessage( control, WM_SETTEXT , 0,
+ ( LPARAM )(
+ g_Params.m_bMultiplePhoneme ?
+ "Click or enter one or more phonemes from list below"
+ :
+ va( "Phoneme/Viseme: %s", g_Params.m_szName ) ) );
+ }
+
+ control = GetDlgItem( hwndDlg, IDC_EDIT_PHONEME );
+ if ( control )
+ {
+ SetFocus( control );
+ SendMessage( control, WM_SETTEXT , 0, ( LPARAM )g_Params.m_szName );
+ SendMessage( control, EM_SETSEL, 0, MAKELONG(0, 0xffff) );
+ return FALSE;
+ }
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ {
+ int cmd = LOWORD( wParam );
+
+ if ( ( cmd >= IDC_PHONEME ) &&
+ ( cmd < ( IDC_PHONEME + g_nPhonemeCount ) ) )
+ {
+ ClickedPhoneme( hwndDlg, cmd - IDC_PHONEME );
+ if ( !g_Params.m_bMultiplePhoneme )
+ {
+ PhonemePropertiesDialogExit( hwndDlg, 1 );
+ }
+ }
+ else if ( cmd != IDC_EDIT_PHONEME )
+ {
+ switch ( cmd )
+ {
+ case IDOK:
+ {
+ // Retrieve text
+ char szPhoneme[ 256 ];
+ HWND ctrl = GetDlgItem( hwndDlg, IDC_EDIT_PHONEME );
+ if ( ctrl )
+ {
+ SendMessage( ctrl, WM_GETTEXT, (WPARAM)sizeof( szPhoneme ), (LPARAM)szPhoneme );
+
+ ValidatePhonemeString( g_Params.m_bMultiplePhoneme, szPhoneme, g_Params.m_szName );
+ }
+ PhonemePropertiesDialogExit( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ PhonemePropertiesDialogExit( hwndDlg, 0 );
+ break;
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int PhonemeProperties( CPhonemeParams *params )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_PHONEMEPROPERTIES ),
+ (HWND)g_MDLViewer->getHandle(),
+ (DLGPROC)PhonemePropertiesDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/phonemeproperties.h b/utils/hlfaceposer/phonemeproperties.h
new file mode 100644
index 0000000..401343d
--- /dev/null
+++ b/utils/hlfaceposer/phonemeproperties.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PHONEMEPROPERTIES_H
+#define PHONEMEPROPERTIES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CPhonemeParams : public CBaseDialogParams
+{
+ // i/o phoneme name
+ char m_szName[ 256 ];
+
+ // Can enter multiple phonemes, and clicking buttons just appends phonemes to string
+ bool m_bMultiplePhoneme;
+};
+
+// Display/create actor info
+int PhonemeProperties( CPhonemeParams *params );
+
+#endif // PHONEMEPROPERTIES_H
diff --git a/utils/hlfaceposer/project.ico b/utils/hlfaceposer/project.ico
new file mode 100644
index 0000000..c7d9da9
--- /dev/null
+++ b/utils/hlfaceposer/project.ico
Binary files differ
diff --git a/utils/hlfaceposer/project1.ico b/utils/hlfaceposer/project1.ico
new file mode 100644
index 0000000..3e2b5c5
--- /dev/null
+++ b/utils/hlfaceposer/project1.ico
Binary files differ
diff --git a/utils/hlfaceposer/resource.h b/utils/hlfaceposer/resource.h
new file mode 100644
index 0000000..84c410c
--- /dev/null
+++ b/utils/hlfaceposer/resource.h
@@ -0,0 +1,149 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by hlfaceposer.rc
+//
+#define IDI_ICON1 101
+#define IDD_ACTORPROPERTIES 102
+#define IDD_INPUTDIALOG 103
+#define IDD_EXPRESSIONPROPERTIES 104
+#define IDI_WORKSPACE 104
+#define IDD_CHANNELPROPERTIES 105
+#define IDC_MOVETAG 105
+#define IDI_PROJECT 105
+#define IDD_EVENTPROPERTIES 106
+#define IDI_SCENE 106
+#define IDD_PHONEMEPROPERTIES 107
+#define IDI_VCD 107
+#define IDD_GLOBALEVENTPROPERTIES 108
+#define IDI_WAV 108
+#define IDD_FLEXSLIDERS 109
+#define IDI_SPEAK 109
+#define IDI_GRAY 112
+#define IDI_UNCHECKED 113
+#define IDI_CHECKED 114
+#define IDD_CHOICEDIALOG 115
+#define IDI_VCD_CHECKEDOUT 115
+#define IDD_EDITPHRASE 116
+#define IDI_PROJECT_CHECKEDOUT 116
+#define IDI_SPEAK_CHECKEDOUT 117
+#define IDD_WAVELOOKUP 118
+#define IDI_WAV_CHECKEDOUT 118
+#define IDD_ADDSOUNDENTRY 119
+#define IDI_WORKSPACE_CHECKEDOUT 119
+#define IDD_CCLOOKUP 120
+#define IDC_CURSOR1 120
+#define IDD_EVENTPROPERTIES_EXPRESSION 121
+#define IDD_EVENTPROPERTIES_LOOKAT 122
+#define IDD_EVENTPROPERTIES_MOVETO 123
+#define IDD_EVENTPROPERTIES_SPEAK 124
+#define IDD_EVENTPROPERTIES_GESTURE 125
+#define IDD_EVENTPROPERTIES_SEQUENCE 126
+#define IDD_EVENTPROPERTIES_FACE 127
+#define IDD_EVENTPROPERTIES_FIRETRIGGER 128
+#define IDD_EVENTPROPERTIES_FLEXANIMATION 129
+#define IDD_EVENTPROPERTIES_SUBSCENE 130
+#define IDD_EVENTPROPERTIES_INTERRUPT 131
+#define IDD_EVENTPROPERTIES_STOPPOINT 132
+#define IDD_EDGEPROPERTIES 132
+#define IDD_EVENTPROPERTIES_PERMITRESPONSES 133
+#define IDD_EVENTPROPERTIES_GENERIC 134
+#define IDD_PROGRESS 135
+#define IDC_ACTORNAME 1000
+#define IDC_EXPRESSIONDESC 1001
+#define IDC_STATIC_PROMPT 1003
+#define IDC_EXPRESSIONNAME 1004
+#define IDC_CHANNELNAME 1005
+#define IDC_EVENTCHOICES 1007
+#define IDC_TYPENAME 1008
+#define IDC_SELECTWAV 1009
+#define IDC_EVENTCHOICES2 1010
+#define IDC_FILENAME 1011
+#define IDC_TYPENAME2 1011
+#define IDC_EVENTNAME 1012
+#define IDC_ACTORCHOICE 1013
+#define IDC_CHOICES2PROMPT 1013
+#define IDC_STATIC_ACTOR 1014
+#define IDC_CHOICES3PROMPT 1014
+#define IDC_STARTTIME 1015
+#define IDC_ENDTIME 1016
+#define IDC_LOOPTIME 1016
+#define IDC_CHECK_ENDTIME 1017
+#define IDC_CHECK_RESUMECONDITION 1018
+#define IDC_CHECK_AUTOCHECK 1019
+#define IDC_CHECK_LOCKBODYFACING 1019
+#define IDC_CB_AUTOACTION 1020
+#define IDC_DURATION 1021
+#define IDC_INPUTSTRING 1022
+#define IDC_LOOPCOUNT 1023
+#define IDC_PHONEMENAME 1024
+#define IDC_ABSOLUTESTART 1025
+#define IDC_RELATIVESTART 1026
+#define IDC_TAGS 1027
+#define IDC_EDIT_PHONEME 1028
+#define IDC_PHONEMETEXTPROMPT 1029
+#define IDC_STATIC_HELPTEXT 1030
+#define IDC_STATIC_SPLINE 1038
+#define IDC_SLIDERS 1040
+#define IDC_CHOICE 1043
+#define IDC_EVENTCHOICES3 1044
+#define IDC_STATIC_PITCH 1046
+#define IDC_STATIC_YAW 1047
+#define IDC_SLIDER_PITCH 1048
+#define IDC_SLIDER_YAW 1049
+#define IDC_STATIC_PITCHVAL 1050
+#define IDC_STATIC_YAWVAL 1051
+#define IDC_CHECK_LOOKAT 1052
+#define IDC_STATIC_LOOPCOUNT 1054
+#define IDC_STATIC_LOOPTIME 1055
+#define IDC_STATIC_AFTER 1056
+#define IDC_STATIC_SECONDS 1057
+#define IDC_SHOW_ALL_SOUNDS 1058
+#define IDC_SOUNDENTRYLIST 1059
+#define IDC_CAPTION_ATTENUATION 1059
+#define IDC_ADDENTRY 1061
+#define IDC_SOUNDNAME 1061
+#define IDC_SOUNDSCRIPT 1062
+#define IDC_CCTOKEN 1065
+#define IDC_CCTOKENLIST 1066
+#define IDC_SOUNDLIST 1067
+#define IDC_FILTER 1068
+#define IDC_STATIC_WAVEFILENAME 1069
+#define IDC_STATIC_SCRIPTFILE 1070
+#define IDC_PLAY_SOUND 1071
+#define IDC_OPENSOURCE 1072
+#define IDC_STATIC_DISTANCE 1074
+#define IDC_SLIDER_DISTANCE 1075
+#define IDC_STATIC_DISTANCEVAL 1076
+#define IDC_LEFT_CURVETYPE 1077
+#define IDC_RIGHT_CURVETYPE 1078
+#define IDC_LEFT_ACTIVE 1079
+#define IDC_RIGHT_ACTIVE 1080
+#define IDC_LEFT_ZEROVALUE 1081
+#define IDC_RIGHT_ZEROVALUE 1082
+#define IDC_LEFT_RESET 1083
+#define IDC_RIGHT_RESET 1084
+#define IDC_CHECK1 1086
+#define IDC_HOLD_OUT 1086
+#define IDC_CHECK_FORCESHORTMOVEMENT 1087
+#define IDC_CHECK_SYNCTOFOLLOWINGGESTURE 1088
+#define IDC_CHECK_DISABLED 1090
+#define IDC_CHECK_PLAYOVERSCRIPT 1091
+#define IDC_FP_PROGRESS 1092
+#define IDC_FP_PROGRESS_TITLE 1093
+#define IDC_FP_PROGRESS_TEXT 1094
+#define IDC_FP_PROGRESS_CANCEL 1095
+#define IDC_FP_PROGRESS_PERCENT 1095
+#define IDC_FP_PROGRESS_PERCENT2 1096
+#define IDC_FP_PROGRESS_ETA 1096
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 133
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1096
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/utils/hlfaceposer/scene.ico b/utils/hlfaceposer/scene.ico
new file mode 100644
index 0000000..ad48cbf
--- /dev/null
+++ b/utils/hlfaceposer/scene.ico
Binary files differ
diff --git a/utils/hlfaceposer/snd_audio_source.cpp b/utils/hlfaceposer/snd_audio_source.cpp
new file mode 100644
index 0000000..58fd6fa
--- /dev/null
+++ b/utils/hlfaceposer/snd_audio_source.cpp
@@ -0,0 +1,63 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "snd_audio_source.h"
+
+
+
+extern CAudioSource *Audio_CreateMemoryWave( const char *pName );
+
+//-----------------------------------------------------------------------------
+// Purpose: Simple wrapper to crack naming convention and create the proper wave source
+// Input : *pName - WAVE filename
+// Output : CAudioSource
+//-----------------------------------------------------------------------------
+CAudioSource *AudioSource_Create( const char *pName )
+{
+ if ( !pName )
+ return NULL;
+
+// if ( pName[0] == '!' ) // sentence
+ ;
+
+ // Names that begin with "*" are streaming.
+ // Skip over the * and create a streamed source
+ if ( pName[0] == '*' )
+ {
+
+ return NULL;
+ }
+
+ // These are loaded into memory directly
+ return Audio_CreateMemoryWave( pName );
+}
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "hlfaceposer.h"
+#include "ifaceposersound.h"
+
+CAudioSource::~CAudioSource( void )
+{
+ CAudioMixer *mixer;
+
+ while ( 1 )
+ {
+ mixer = sound->FindMixer( this );
+ if ( !mixer )
+ break;
+
+ sound->StopSound( mixer );
+ }
+
+ sound->EnsureNoModelReferences( this );
+}
+
+CAudioSource::CAudioSource( void )
+{
+}
diff --git a/utils/hlfaceposer/snd_audio_source.h b/utils/hlfaceposer/snd_audio_source.h
new file mode 100644
index 0000000..a60f14b
--- /dev/null
+++ b/utils/hlfaceposer/snd_audio_source.h
@@ -0,0 +1,124 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SND_AUDIO_SOURCE_H
+#define SND_AUDIO_SOURCE_H
+#pragma once
+
+
+// fixed point stuff for real-time resampling
+#define FIX_BITS 28
+#define FIX_SCALE (1 << FIX_BITS)
+#define FIX_MASK ((1 << FIX_BITS)-1)
+#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE))
+#define FIX(a) (((int)(a)) << FIX_BITS)
+#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS)
+#define FIX_FRACTION(a,b) (FIX(a)/(b))
+#define FIX_FRACPART(a) ((a) & FIX_MASK)
+
+typedef unsigned int fixedint;
+
+typedef struct channel_s channel_t;
+
+class CAudioSource;
+
+class IAudioDevice
+{
+public:
+ // Add a virtual destructor to silence the clang warning.
+ // This is harmless but not important since the only derived class
+ // doesn't have a destructor.
+ virtual ~IAudioDevice() {}
+
+ virtual void MixBegin( void ) = 0;
+ virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0;
+ virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0;
+ virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0;
+ virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0;
+ virtual int MaxSampleCount( void ) = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: This is an instance of an audio source.
+// Mixers are attached to channels and reference an audio source.
+// Mixers are specific to the sample format and source format.
+// Mixers are never re-used, so they can track instance data like
+// sample position, fractional sample, stream cache, faders, etc.
+//-----------------------------------------------------------------------------
+class CAudioMixer
+{
+public:
+ virtual ~CAudioMixer( void ) {}
+
+ // UNDONE: time compress
+ virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true ) = 0;
+ virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true ) = 0;
+ virtual bool SkipSamples( channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true ) = 0;
+
+ virtual CAudioSource *GetSource( void ) = 0;
+
+ virtual int GetSamplePosition( void ) = 0;
+ virtual int GetScrubPosition( void ) = 0;
+
+ virtual bool SetSamplePosition( int position, bool scrubbing = false ) = 0;
+ virtual void SetLoopPosition( int position ) = 0;
+ virtual int GetStartPosition( void ) = 0;
+
+ virtual bool GetActive( void ) = 0;
+ virtual void SetActive( bool active ) = 0;
+
+ virtual void SetModelIndex( int index ) = 0;
+ virtual int GetModelIndex( void ) const = 0;
+
+ virtual void SetDirection( bool forward ) = 0;
+ virtual bool GetDirection( void ) const = 0;
+
+ virtual void SetAutoDelete( bool autodelete ) = 0;
+ virtual bool GetAutoDelete( void ) const = 0;
+
+ virtual void SetVolume( float volume ) = 0;
+ virtual channel_t *GetChannel() = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A source is an abstraction for a stream, cached file, or procedural
+// source of audio.
+//-----------------------------------------------------------------------------
+class CSentence;
+
+class CAudioSource
+{
+public:
+ CAudioSource( void );
+ virtual ~CAudioSource( void );
+
+ // Create an instance (mixer) of this audio source
+ virtual CAudioMixer *CreateMixer( void ) = 0;
+ virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true ) = 0;
+ virtual int SampleRate( void ) = 0;
+ virtual int SampleSize( void ) = 0;
+ virtual int SampleCount( void ) = 0;
+ virtual float TrueSampleSize( void ) = 0;
+ virtual bool IsLooped( void ) = 0;
+ virtual bool IsStreaming( void ) = 0;
+ virtual float GetRunningLength( void ) = 0;
+ virtual int GetNumChannels() = 0;
+ virtual bool IsStereoWav( void ) = 0;
+
+ virtual CSentence *GetSentence( void ) { return NULL; };
+};
+
+
+extern CAudioSource *AudioSource_Create( const char *pName );
+
+#endif // SND_AUDIO_SOURCE_H
diff --git a/utils/hlfaceposer/snd_wave_mixer.cpp b/utils/hlfaceposer/snd_wave_mixer.cpp
new file mode 100644
index 0000000..e90d5cd
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_mixer.cpp
@@ -0,0 +1,537 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//===========================================================================//
+#include <stdio.h>
+#include <windows.h>
+#include "snd_audio_source.h"
+#include "snd_wave_source.h"
+#include "snd_wave_mixer_private.h"
+#include "snd_wave_mixer_adpcm.h"
+#include "ifaceposersound.h"
+#include "AudioWaveOutput.h"
+#include "tier2/riff.h"
+
+typedef struct channel_s
+{
+ int leftvol;
+ int rightvol;
+ int rleftvol;
+ int rrightvol;
+ float pitch;
+} channel_t;
+
+//-----------------------------------------------------------------------------
+// These mixers provide an abstraction layer between the audio device and
+// mixing/decoding code. They allow data to be decoded and mixed using
+// optimized, format sensitive code by calling back into the device that
+// controls them.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Purpose: maps mixing to 8-bit mono mixer
+//-----------------------------------------------------------------------------
+class CAudioMixerWave8Mono : public CAudioMixerWave
+{
+public:
+ CAudioMixerWave8Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
+ virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
+ {
+ pDevice->Mix8Mono( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: maps mixing to 8-bit stereo mixer
+//-----------------------------------------------------------------------------
+class CAudioMixerWave8Stereo : public CAudioMixerWave
+{
+public:
+ CAudioMixerWave8Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
+ virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
+ {
+ pDevice->Mix8Stereo( pChannel, (char *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: maps mixing to 16-bit mono mixer
+//-----------------------------------------------------------------------------
+class CAudioMixerWave16Mono : public CAudioMixerWave
+{
+public:
+ CAudioMixerWave16Mono( CWaveData *data ) : CAudioMixerWave( data ) {}
+ virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
+ {
+ pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: maps mixing to 16-bit stereo mixer
+//-----------------------------------------------------------------------------
+class CAudioMixerWave16Stereo : public CAudioMixerWave
+{
+public:
+ CAudioMixerWave16Stereo( CWaveData *data ) : CAudioMixerWave( data ) {}
+ virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true )
+ {
+ pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Create an approprite mixer type given the data format
+// Input : *data - data access abstraction
+// format - pcm or adpcm (1 or 2 -- RIFF format)
+// channels - number of audio channels (1 = mono, 2 = stereo)
+// bits - bits per sample
+// Output : CAudioMixer * abstract mixer type that maps mixing to appropriate code
+//-----------------------------------------------------------------------------
+CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits )
+{
+ if ( format == WAVE_FORMAT_PCM )
+ {
+ if ( channels > 1 )
+ {
+ if ( bits == 8 )
+ return new CAudioMixerWave8Stereo( data );
+ else
+ return new CAudioMixerWave16Stereo( data );
+ }
+ else
+ {
+ if ( bits == 8 )
+ return new CAudioMixerWave8Mono( data );
+ else
+ return new CAudioMixerWave16Mono( data );
+ }
+ }
+ else if ( format == WAVE_FORMAT_ADPCM )
+ {
+ return CreateADPCMMixer( data );
+ }
+ return NULL;
+}
+
+#include "hlfaceposer.h"
+//-----------------------------------------------------------------------------
+// Purpose: Init the base WAVE mixer.
+// Input : *data - data access object
+//-----------------------------------------------------------------------------
+CAudioMixerWave::CAudioMixerWave( CWaveData *data ) : m_pData(data), m_pChannel(NULL)
+{
+ m_loop = 0;
+ m_sample = 0;
+ m_absoluteSample = 0;
+ m_scrubSample = -1;
+ m_fracOffset = 0;
+ m_bActive = false;
+ m_nModelIndex = -1;
+ m_bForward = true;
+ m_bAutoDelete = true;
+ m_pChannel = new channel_t;
+ m_pChannel->leftvol = 127;
+ m_pChannel->rightvol = 127;
+ m_pChannel->pitch = 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Frees the data access object (we own it after construction)
+//-----------------------------------------------------------------------------
+CAudioMixerWave::~CAudioMixerWave( void )
+{
+ delete m_pData;
+ delete m_pChannel;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decode and read the data
+// by default we just pass the request on to the data access object
+// other mixers may need to buffer or decode the data for some reason
+//
+// Input : **pData - dest pointer
+// sampleCount - number of samples needed
+// Output : number of samples available in this batch
+//-----------------------------------------------------------------------------
+int CAudioMixerWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
+{
+ if ( samplePosition != m_sample )
+ {
+ // Seek
+ m_sample = samplePosition;
+ m_absoluteSample = samplePosition;
+ }
+
+ return m_pData->ReadSourceData( pData, m_sample, sampleCount, forward );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: calls through the wavedata to get the audio source
+// Output : CAudioSource
+//-----------------------------------------------------------------------------
+CAudioSource *CAudioMixerWave::GetSource( void )
+{
+ if ( m_pData )
+ return &m_pData->Source();
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the current sample location in playback
+// Output : int (samples from start of wave)
+//-----------------------------------------------------------------------------
+int CAudioMixerWave::GetSamplePosition( void )
+{
+ return m_sample;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the current sample location in playback
+// Output : int (samples from start of wave)
+//-----------------------------------------------------------------------------
+int CAudioMixerWave::GetScrubPosition( void )
+{
+ if (m_scrubSample != -1)
+ {
+ return m_scrubSample;
+ }
+ return m_sample;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : position -
+//-----------------------------------------------------------------------------
+bool CAudioMixerWave::SetSamplePosition( int position, bool scrubbing )
+{
+ position = max( 0, position );
+
+ m_sample = position;
+ m_absoluteSample = position;
+ m_startpos = m_sample;
+ if (scrubbing)
+ {
+ m_scrubSample = position;
+ }
+ else
+ {
+ m_scrubSample = -1;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : position -
+//-----------------------------------------------------------------------------
+void CAudioMixerWave::SetLoopPosition( int position )
+{
+ m_loop = position;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CAudioMixerWave::GetStartPosition( void )
+{
+ return m_startpos;
+}
+
+bool CAudioMixerWave::GetActive( void )
+{
+ return m_bActive;
+}
+
+void CAudioMixerWave::SetActive( bool active )
+{
+ m_bActive = active;
+}
+
+void CAudioMixerWave::SetModelIndex( int index )
+{
+ m_nModelIndex = index;
+}
+
+int CAudioMixerWave::GetModelIndex( void ) const
+{
+ return m_nModelIndex;
+}
+
+void CAudioMixerWave::SetDirection( bool forward )
+{
+ m_bForward = forward;
+}
+
+bool CAudioMixerWave::GetDirection( void ) const
+{
+ return m_bForward;
+}
+
+void CAudioMixerWave::SetAutoDelete( bool autodelete )
+{
+ m_bAutoDelete = autodelete;
+}
+
+bool CAudioMixerWave::GetAutoDelete( void ) const
+{
+ return m_bAutoDelete;
+}
+
+void CAudioMixerWave::SetVolume( float volume )
+{
+ int ivolume = (int)( clamp( volume, 0.0f, 1.0f ) * 127.0f );
+
+ m_pChannel->leftvol = ivolume;
+ m_pChannel->rightvol = ivolume;
+}
+
+channel_t *CAudioMixerWave::GetChannel()
+{
+ Assert( m_pChannel );
+ return m_pChannel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pChannel -
+// sampleCount -
+// outputRate -
+//-----------------------------------------------------------------------------
+void CAudioMixerWave::IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward /*= true*/ )
+{
+ int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
+ float rate = (float)inputSampleRate / outputRate;
+
+ int startpos = startSample;
+
+ if ( !forward )
+ {
+ int requestedstart = startSample - (int)( sampleCount * rate );
+ if ( requestedstart < 0 )
+ return;
+
+ startpos = max( 0, requestedstart );
+ SetSamplePosition( startpos );
+ }
+
+ while ( sampleCount > 0 )
+ {
+ int inputSampleCount;
+ int outputSampleCount = sampleCount;
+
+ if ( outputRate != inputSampleRate )
+ {
+ inputSampleCount = (int)(sampleCount * rate);
+ }
+ else
+ {
+ inputSampleCount = sampleCount;
+ }
+
+ sampleCount -= outputSampleCount;
+ if ( forward )
+ {
+ m_sample += inputSampleCount;
+ m_absoluteSample += inputSampleCount;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The device calls this to request data. The mixer must provide the
+// full amount of samples or have silence in its output stream.
+// Input : *pDevice - requesting device
+// sampleCount - number of samples at the output rate
+// outputRate - sampling rate of the request
+// Output : Returns true to keep mixing, false to delete this mixer
+//-----------------------------------------------------------------------------
+bool CAudioMixerWave::SkipSamples( channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
+{
+ int offset = 0;
+
+ int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
+ float rate = (float)inputSampleRate / outputRate;
+
+ sampleCount = min( sampleCount, PAINTBUFFER_SIZE );
+
+ int startpos = startSample;
+
+ if ( !forward )
+ {
+ int requestedstart = startSample - (int)( sampleCount * rate );
+ if ( requestedstart < 0 )
+ return false;
+
+ startpos = max( 0, requestedstart );
+ SetSamplePosition( startpos );
+ }
+
+ while ( sampleCount > 0 )
+ {
+ int inputSampleCount;
+ char *pData = NULL;
+ int outputSampleCount = sampleCount;
+
+ if ( outputRate != inputSampleRate )
+ {
+ inputSampleCount = (int)(sampleCount * rate);
+ if ( !forward )
+ {
+ startSample = max( 0, startSample - inputSampleCount );
+ }
+ int availableSamples = GetOutputData( (void **)&pData, startSample, inputSampleCount, forward );
+ if ( !availableSamples )
+ break;
+
+ if ( availableSamples < inputSampleCount )
+ {
+ outputSampleCount = (int)(availableSamples / rate);
+ inputSampleCount = availableSamples;
+ }
+
+ // compute new fraction part of sample index
+ float flOffset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
+ flOffset = flOffset - (float)((int)flOffset);
+ m_fracOffset = FIX_FLOAT( flOffset );
+ }
+ else
+ {
+ if ( !forward )
+ {
+ startSample = max( 0, startSample - sampleCount );
+ }
+ int availableSamples = GetOutputData( (void **)&pData, startSample, sampleCount, forward );
+ if ( !availableSamples )
+ break;
+ outputSampleCount = availableSamples;
+ inputSampleCount = availableSamples;
+
+ }
+ offset += outputSampleCount;
+ sampleCount -= outputSampleCount;
+ if ( forward )
+ {
+ m_sample += inputSampleCount;
+ m_absoluteSample += inputSampleCount;
+ }
+
+ if ( m_loop != 0 && m_sample >= m_loop )
+ {
+ SetSamplePosition( m_startpos );
+ }
+
+ }
+
+ if ( sampleCount > 0 )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The device calls this to request data. The mixer must provide the
+// full amount of samples or have silence in its output stream.
+// Input : *pDevice - requesting device
+// sampleCount - number of samples at the output rate
+// outputRate - sampling rate of the request
+// Output : Returns true to keep mixing, false to delete this mixer
+//-----------------------------------------------------------------------------
+bool CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ )
+{
+ int offset = 0;
+
+ int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate());
+ float rate = (float)inputSampleRate / outputRate;
+ fixedint fracstep = FIX_FLOAT( rate );
+
+ sampleCount = min( sampleCount, PAINTBUFFER_SIZE );
+
+ int startpos = startSample;
+
+ if ( !forward )
+ {
+ int requestedstart = startSample - (int)( sampleCount * rate );
+ if ( requestedstart < 0 )
+ return false;
+
+ startpos = max( 0, requestedstart );
+ SetSamplePosition( startpos );
+ }
+
+ while ( sampleCount > 0 )
+ {
+ int inputSampleCount;
+ char *pData = NULL;
+ int outputSampleCount = sampleCount;
+
+
+ if ( outputRate != inputSampleRate )
+ {
+ inputSampleCount = (int)(sampleCount * rate);
+
+ int availableSamples = GetOutputData( (void **)&pData, startpos, inputSampleCount, forward );
+ if ( !availableSamples )
+ break;
+
+ if ( availableSamples < inputSampleCount )
+ {
+ outputSampleCount = (int)(availableSamples / rate);
+ inputSampleCount = availableSamples;
+ }
+
+ Mix( pDevice, pChannel, pData, offset, m_fracOffset, fracstep, outputSampleCount, 0, forward );
+
+ // compute new fraction part of sample index
+ float flOffset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount);
+ flOffset = flOffset - (float)((int)flOffset);
+ m_fracOffset = FIX_FLOAT( flOffset );
+ }
+ else
+ {
+ int availableSamples = GetOutputData( (void **)&pData, startpos, sampleCount, forward );
+ if ( !availableSamples )
+ break;
+
+ outputSampleCount = availableSamples;
+ inputSampleCount = availableSamples;
+
+ Mix( pDevice, pChannel, pData, offset, m_fracOffset, FIX(1), outputSampleCount, 0, forward );
+ }
+ offset += outputSampleCount;
+ sampleCount -= outputSampleCount;
+
+ if ( forward )
+ {
+ m_sample += inputSampleCount;
+ m_absoluteSample += inputSampleCount;
+ }
+
+ if ( m_loop != 0 && m_sample >= m_loop )
+ {
+ SetSamplePosition( m_startpos );
+ }
+
+ }
+
+ if ( sampleCount > 0 )
+ return false;
+
+ return true;
+}
diff --git a/utils/hlfaceposer/snd_wave_mixer.h b/utils/hlfaceposer/snd_wave_mixer.h
new file mode 100644
index 0000000..07e019b
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_mixer.h
@@ -0,0 +1,24 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SND_WAVE_MIXER_H
+#define SND_WAVE_MIXER_H
+#pragma once
+
+class CWaveData;
+class CAudioMixer;
+
+CAudioMixer *CreateWaveMixer( CWaveData *data, int format, int channels, int bits );
+
+
+#endif // SND_WAVE_MIXER_H
diff --git a/utils/hlfaceposer/snd_wave_mixer_adpcm.cpp b/utils/hlfaceposer/snd_wave_mixer_adpcm.cpp
new file mode 100644
index 0000000..a76b66b
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_mixer_adpcm.cpp
@@ -0,0 +1,516 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#pragma warning( disable: 4201 )
+#include <mmsystem.h>
+#pragma warning( default: 4201 )
+
+#include <mmreg.h>
+#include "snd_wave_source.h"
+#include "snd_wave_mixer_adpcm.h"
+#include "snd_wave_mixer_private.h"
+#include "hlfaceposer.h"
+
+// max size of ADPCM block in bytes
+#define MAX_BLOCK_SIZE 4096
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Mixer for ADPCM encoded audio
+//-----------------------------------------------------------------------------
+class CAudioMixerWaveADPCM : public CAudioMixerWave
+{
+public:
+ CAudioMixerWaveADPCM( CWaveData *data );
+ ~CAudioMixerWaveADPCM( void );
+
+ virtual void Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward = true );
+ virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
+
+ virtual bool SetSamplePosition( int position, bool scrubbing = false );
+
+private:
+ bool DecodeBlock( void );
+ int NumChannels( void );
+ void DecompressBlockMono( short *pOut, const char *pIn, int count );
+ void DecompressBlockStereo( short *pOut, const char *pIn, int count );
+
+ void SetCurrentBlock( int block );
+ int GetCurrentBlock( void ) const;
+ int GetBlockNumberForSample( int samplePosition );
+ bool IsSampleInCurrentBlock( int samplePosition );
+ int GetFirstSampleForBlock( int blocknum ) const;
+
+ const ADPCMWAVEFORMAT *m_pFormat;
+ const ADPCMCOEFSET *m_pCoefficients;
+
+ short *m_pSamples;
+ int m_sampleCount;
+ int m_samplePosition;
+
+ int m_blockSize;
+ int m_offset;
+
+ int m_currentBlock;
+};
+
+
+CAudioMixerWaveADPCM::CAudioMixerWaveADPCM( CWaveData *data ) : CAudioMixerWave( data )
+{
+ m_currentBlock = -1;
+ m_pSamples = NULL;
+ m_sampleCount = 0;
+ m_samplePosition = 0;
+ m_offset = 0;
+
+ m_pFormat = (const ADPCMWAVEFORMAT *)m_pData->Source().GetHeader();
+ if ( m_pFormat )
+ {
+ m_pCoefficients = (ADPCMCOEFSET *)((char *)m_pFormat + sizeof(WAVEFORMATEX) + 4);
+
+ // create the decode buffer
+ m_pSamples = new short[m_pFormat->wSamplesPerBlock * m_pFormat->wfx.nChannels];
+
+ // number of bytes for samples
+ m_blockSize = ((m_pFormat->wSamplesPerBlock - 2) * m_pFormat->wfx.nChannels ) / 2;
+ // size of channel header
+ m_blockSize += 7 * m_pFormat->wfx.nChannels;
+// Assert(m_blockSize < MAX_BLOCK_SIZE);
+ }
+}
+
+
+CAudioMixerWaveADPCM::~CAudioMixerWaveADPCM( void )
+{
+ delete[] m_pSamples;
+}
+
+
+int CAudioMixerWaveADPCM::NumChannels( void )
+{
+ if ( m_pFormat )
+ {
+ return m_pFormat->wfx.nChannels;
+ }
+ return 0;
+}
+
+void CAudioMixerWaveADPCM::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress, bool forward /*= true*/ )
+{
+ if ( NumChannels() == 1 )
+ pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+ else
+ pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress, forward );
+}
+
+
+static int error_sign_lut[] = { 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 };
+static int error_coefficients_lut[] = { 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230 };
+
+//-----------------------------------------------------------------------------
+// Purpose: ADPCM decompress a single block of 1-channel audio
+// Input : *pOut - output buffer 16-bit
+// *pIn - input block
+// count - number of samples to decode (to support partial blocks)
+//-----------------------------------------------------------------------------
+void CAudioMixerWaveADPCM::DecompressBlockMono( short *pOut, const char *pIn, int count )
+{
+
+ int pred = *pIn++;
+ int co1 = m_pCoefficients[pred].iCoef1;
+ int co2 = m_pCoefficients[pred].iCoef2;
+
+ // read initial delta
+ int delta = *((short *)pIn);
+ pIn += 2;
+
+ // read initial samples for prediction
+ int samp1 = *((short *)pIn);
+ pIn += 2;
+
+ int samp2 = *((short *)pIn);
+ pIn += 2;
+
+ // write out the initial samples (stored in reverse order)
+ *pOut++ = (short)samp2;
+ *pOut++ = (short)samp1;
+
+ // subtract the 2 samples in the header
+ count -= 2;
+
+ // this is a toggle to read nibbles, first nibble is high
+ int high = 1;
+
+ int error = 0, sample = 0;
+
+ // now process the block
+ while ( count )
+ {
+ // read the error nibble from the input stream
+ if ( high )
+ {
+ sample = (unsigned char) (*pIn++);
+ // high nibble
+ error = sample >> 4;
+ // cache low nibble for next read
+ sample = sample & 0xf;
+ // Next read is from cache, not stream
+ high = 0;
+ }
+ else
+ {
+ // stored in previous read (low nibble)
+ error = sample;
+ // next read is from stream
+ high = 1;
+ }
+ // convert to signed with LUT
+ int errorSign = error_sign_lut[error];
+
+ // interpolate the new sample
+ int predSample = (samp1 * co1) + (samp2 * co2);
+ // coefficients are fixed point 8-bit, so shift back to 16-bit integer
+ predSample >>= 8;
+
+ // Add in current error estimate
+ predSample += (errorSign * delta);
+
+ // Correct error estimate
+ delta = (delta * error_coefficients_lut[error]) >> 8;
+ // Clamp error estimate
+ if ( delta < 16 )
+ delta = 16;
+
+ // clamp
+ if ( predSample > 32767L )
+ predSample = 32767L;
+ else if ( predSample < -32768L )
+ predSample = -32768L;
+
+ // output
+ *pOut++ = (short)predSample;
+ // move samples over
+ samp2 = samp1;
+ samp1 = predSample;
+
+ count--;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decode a single block of stereo ADPCM audio
+// Input : *pOut - 16-bit output buffer
+// *pIn - ADPCM encoded block data
+// count - number of sample pairs to decode
+//-----------------------------------------------------------------------------
+void CAudioMixerWaveADPCM::DecompressBlockStereo( short *pOut, const char *pIn, int count )
+{
+ int pred[2], co1[2], co2[2];
+ int i;
+
+ for ( i = 0; i < 2; i++ )
+ {
+ pred[i] = *pIn++;
+ co1[i] = m_pCoefficients[pred[i]].iCoef1;
+ co2[i] = m_pCoefficients[pred[i]].iCoef2;
+ }
+
+ int delta[2], samp1[2], samp2[2];
+
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ // read initial delta
+ delta[i] = *((short *)pIn);
+ }
+
+ // read initial samples for prediction
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ samp1[i] = *((short *)pIn);
+ }
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ samp2[i] = *((short *)pIn);
+ }
+
+ // write out the initial samples (stored in reverse order)
+ *pOut++ = (short)samp2[0]; // left
+ *pOut++ = (short)samp2[1]; // right
+ *pOut++ = (short)samp1[0]; // left
+ *pOut++ = (short)samp1[1]; // right
+
+ // subtract the 2 samples in the header
+ count -= 2;
+
+ // this is a toggle to read nibbles, first nibble is high
+ int high = 1;
+
+ int error, sample = 0;
+
+ // now process the block
+ while ( count )
+ {
+ for ( i = 0; i < 2; i++ )
+ {
+ // read the error nibble from the input stream
+ if ( high )
+ {
+ sample = (unsigned char) (*pIn++);
+ // high nibble
+ error = sample >> 4;
+ // cache low nibble for next read
+ sample = sample & 0xf;
+ // Next read is from cache, not stream
+ high = 0;
+ }
+ else
+ {
+ // stored in previous read (low nibble)
+ error = sample;
+ // next read is from stream
+ high = 1;
+ }
+ // convert to signed with LUT
+ int errorSign = error_sign_lut[error];
+
+ // interpolate the new sample
+ int predSample = (samp1[i] * co1[i]) + (samp2[i] * co2[i]);
+ // coefficients are fixed point 8-bit, so shift back to 16-bit integer
+ predSample >>= 8;
+
+ // Add in current error estimate
+ predSample += (errorSign * delta[i]);
+
+ // Correct error estimate
+ delta[i] = (delta[i] * error_coefficients_lut[error]) >> 8;
+ // Clamp error estimate
+ if ( delta[i] < 16 )
+ delta[i] = 16;
+
+ // clamp
+ if ( predSample > 32767L )
+ predSample = 32767L;
+ else if ( predSample < -32768L )
+ predSample = -32768L;
+
+ // output
+ *pOut++ = (short)predSample;
+ // move samples over
+ samp2[i] = samp1[i];
+ samp1[i] = predSample;
+ }
+ count--;
+ }
+}
+
+
+bool CAudioMixerWaveADPCM::DecodeBlock( void )
+{
+ char tmpBlock[MAX_BLOCK_SIZE];
+ char *pData;
+
+ int available = m_pData->ReadSourceData( (void **) (&pData), m_offset, m_blockSize );
+ if ( available < m_blockSize )
+ {
+ int total = 0;
+ while ( available && total < m_blockSize )
+ {
+ memcpy( tmpBlock + total, pData, available );
+ total += available;
+ available = m_pData->ReadSourceData( (void **) (&pData), m_offset + total, m_blockSize - total );
+ }
+ pData = tmpBlock;
+ available = total;
+ }
+
+ Assert( m_blockSize > 0 );
+
+ // Current block number is based on starting offset
+ int blockNumber = m_offset / m_blockSize;
+ SetCurrentBlock( blockNumber );
+
+ if ( !available )
+ {
+ return false;
+ }
+
+ // advance the file pointer
+ m_offset += available;
+
+ int channelCount = NumChannels();
+
+ // this is sample pairs for stereo, samples for mono
+ m_sampleCount = m_pFormat->wSamplesPerBlock;
+
+ // short block?, fixup sample count (2 samples per byte, divided by number of channels per sample set)
+ m_sampleCount -= ((m_blockSize - available) * 2) / channelCount;
+
+ // new block, start at the first sample
+ m_samplePosition = 0;
+
+ // no need to subclass for different channel counts...
+ if ( channelCount == 1 )
+ {
+ DecompressBlockMono( m_pSamples, pData, m_sampleCount );
+ }
+ else
+ {
+ DecompressBlockStereo( m_pSamples, pData, m_sampleCount );
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : block -
+//-----------------------------------------------------------------------------
+void CAudioMixerWaveADPCM::SetCurrentBlock( int block )
+{
+ m_currentBlock = block;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CAudioMixerWaveADPCM::GetCurrentBlock( void ) const
+{
+ return m_currentBlock;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : samplePosition -
+// Output : int
+//-----------------------------------------------------------------------------
+int CAudioMixerWaveADPCM::GetBlockNumberForSample( int samplePosition )
+{
+ int blockNum = samplePosition / m_pFormat->wSamplesPerBlock;
+ return blockNum;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : samplePosition -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAudioMixerWaveADPCM::IsSampleInCurrentBlock( int samplePosition )
+{
+ int currentBlock = GetCurrentBlock();
+
+ int startSample = currentBlock * m_pFormat->wSamplesPerBlock;
+ int endSample = startSample + m_pFormat->wSamplesPerBlock - 1;
+
+ if ( samplePosition >= startSample &&
+ samplePosition <= endSample )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : blocknum -
+// Output : int
+//-----------------------------------------------------------------------------
+int CAudioMixerWaveADPCM::GetFirstSampleForBlock( int blocknum ) const
+{
+ return m_pFormat->wSamplesPerBlock * blocknum;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Read existing buffer or decompress a new block when necessary
+// Input : **pData - output data pointer
+// sampleCount - number of samples (or pairs)
+// Output : int - available samples (zero to stop decoding)
+//-----------------------------------------------------------------------------
+int CAudioMixerWaveADPCM::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
+{
+ int requestedBlock = GetBlockNumberForSample( samplePosition );
+ if ( requestedBlock != GetCurrentBlock() )
+ {
+ // Ran out of data!!!
+ if ( !SetSamplePosition( samplePosition ) )
+ return 0;
+ }
+
+ Assert( requestedBlock == GetCurrentBlock() );
+
+ if ( m_samplePosition >= m_sampleCount )
+ {
+ if ( !DecodeBlock() )
+ return 0;
+ }
+
+ if ( m_samplePosition < m_sampleCount )
+ {
+ *pData = (void *)(m_pSamples + m_samplePosition * NumChannels());
+ int available = m_sampleCount - m_samplePosition;
+ if ( available > sampleCount )
+ available = sampleCount;
+
+ m_samplePosition += available;
+ return available;
+ }
+
+ return 0;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : position -
+//-----------------------------------------------------------------------------
+bool CAudioMixerWaveADPCM::SetSamplePosition( int position, bool scrubbing )
+{
+ position = max( 0, position );
+
+ CAudioMixerWave::SetSamplePosition( position, scrubbing );
+
+ int requestedBlock = GetBlockNumberForSample( position );
+ int firstSample = GetFirstSampleForBlock( requestedBlock );
+
+ if ( firstSample >= m_pData->Source().SampleCount() )
+ {
+ // Read past end of file!!!
+ return false;
+ }
+
+ int currentSample = ( position - firstSample );
+
+ if ( requestedBlock != GetCurrentBlock() )
+ {
+ // Rewind file to beginning of block
+ m_offset = requestedBlock * m_blockSize;
+ if ( !DecodeBlock() )
+ {
+ return false;
+ }
+ }
+
+ m_samplePosition = currentSample;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Abstract factory function for ADPCM mixers
+// Input : *data - wave data access object
+// channels -
+// Output : CAudioMixer
+//-----------------------------------------------------------------------------
+CAudioMixer *CreateADPCMMixer( CWaveData *data )
+{
+ return new CAudioMixerWaveADPCM( data );
+}
diff --git a/utils/hlfaceposer/snd_wave_mixer_adpcm.h b/utils/hlfaceposer/snd_wave_mixer_adpcm.h
new file mode 100644
index 0000000..467ab5f
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_mixer_adpcm.h
@@ -0,0 +1,24 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SND_WAVE_MIXER_ADPCM_H
+#define SND_WAVE_MIXER_ADPCM_H
+#pragma once
+
+
+class CAudioMixer;
+class CWaveData;
+
+CAudioMixer *CreateADPCMMixer( CWaveData *data );
+
+#endif // SND_WAVE_MIXER_ADPCM_H
diff --git a/utils/hlfaceposer/snd_wave_mixer_private.h b/utils/hlfaceposer/snd_wave_mixer_private.h
new file mode 100644
index 0000000..2155b87
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_mixer_private.h
@@ -0,0 +1,102 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SND_WAVE_MIXER_PRIVATE_H
+#define SND_WAVE_MIXER_PRIVATE_H
+#pragma once
+
+#include "snd_audio_source.h"
+#include "snd_wave_mixer.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Linear iterator over source data.
+// Keeps track of position in source, and maintains necessary buffers
+//-----------------------------------------------------------------------------
+class CWaveData
+{
+public:
+ virtual ~CWaveData( void ) {}
+ virtual CAudioSourceWave &Source( void ) = 0;
+ virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward = true ) = 0;
+};
+
+class CAudioMixerWave : public CAudioMixer
+{
+public:
+ CAudioMixerWave( CWaveData *data );
+ virtual ~CAudioMixerWave( void );
+
+ virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true );
+ virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
+ virtual bool SkipSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true );
+ virtual void Mix( IAudioDevice *pDevice,
+ channel_t *pChannel,
+ void *pData,
+ int outputOffset,
+ int inputOffset,
+ fixedint fracRate,
+ int outCount,
+ int timecompress,
+ bool forward = true ) = 0;
+
+ virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
+
+ virtual CAudioSource *GetSource( void );
+
+ virtual int GetSamplePosition( void );
+ virtual int GetScrubPosition( void );
+
+ virtual bool SetSamplePosition( int position, bool scrubbing = false );
+ virtual void SetLoopPosition( int position );
+ virtual int GetStartPosition( void );
+
+ virtual bool GetActive( void );
+ virtual void SetActive( bool active );
+
+ virtual void SetModelIndex( int index );
+ virtual int GetModelIndex( void ) const;
+
+ virtual void SetDirection( bool forward );
+ virtual bool GetDirection( void ) const;
+
+ virtual void SetAutoDelete( bool autodelete );
+ virtual bool GetAutoDelete( void ) const;
+
+ virtual void SetVolume( float volume );
+ virtual channel_t *GetChannel();
+
+protected:
+ int m_sample;
+ int m_absoluteSample;
+ int m_scrubSample;
+ int m_startpos;
+ int m_loop;
+ int m_fracOffset;
+ CWaveData *m_pData;
+
+ int m_absoluteStartPos;
+
+ bool m_bActive;
+ // Associated playback model in faceposer
+ int m_nModelIndex;
+
+ bool m_bForward;
+
+ bool m_bAutoDelete;
+
+ channel_t *m_pChannel;
+};
+
+
+#endif // SND_WAVE_MIXER_PRIVATE_H
diff --git a/utils/hlfaceposer/snd_wave_source.cpp b/utils/hlfaceposer/snd_wave_source.cpp
new file mode 100644
index 0000000..61f4b60
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_source.cpp
@@ -0,0 +1,605 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <windows.h>
+#include "tier2/riff.h"
+#include "snd_wave_source.h"
+#include "snd_wave_mixer_private.h"
+#include "snd_audio_source.h"
+#include <mmsystem.h> // wave format
+#include <mmreg.h> // adpcm format
+#include "hlfaceposer.h"
+#include "filesystem.h"
+#include "utlbuffer.h"
+#include "phonemeconverter.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the RIFF i/o interface on stdio
+//-----------------------------------------------------------------------------
+class StdIOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName )
+ {
+ return (int)filesystem->Open( pFileName, "rb" );
+ }
+
+ int read( void *pOutput, int size, int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Read( pOutput, size, (FileHandle_t)file );
+ }
+
+ void seek( int file, int pos )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+ }
+
+ unsigned int tell( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Tell( (FileHandle_t)file );
+ }
+
+ unsigned int size( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Size( (FileHandle_t)file );
+ }
+
+ void close( int file )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Close( (FileHandle_t)file );
+ }
+};
+
+static StdIOReadBinary io;
+
+#define RIFF_WAVE MAKEID('W','A','V','E')
+#define WAVE_FMT MAKEID('f','m','t',' ')
+#define WAVE_DATA MAKEID('d','a','t','a')
+#define WAVE_FACT MAKEID('f','a','c','t')
+#define WAVE_CUE MAKEID('c','u','e',' ')
+
+void ChunkError( unsigned int id )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Init to empty wave
+//-----------------------------------------------------------------------------
+CAudioSourceWave::CAudioSourceWave( void )
+{
+ m_format = 0;
+ m_pHeader = NULL;
+ // no looping
+ m_loopStart = -1;
+ m_sampleSize = 1;
+ m_sampleCount = 0;
+}
+
+
+CAudioSourceWave::~CAudioSourceWave( void )
+{
+ // for non-standard waves, we store a copy of the header in RAM
+ delete[] m_pHeader;
+ // m_pWords points into m_pWordBuffer, no need to delete
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Init the wave data.
+// Input : *pHeaderBuffer - the RIFF fmt chunk
+// headerSize - size of that chunk
+//-----------------------------------------------------------------------------
+void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
+{
+ const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;
+
+ // copy the relevant header data
+ m_format = pHeader->wFormatTag;
+ m_bits = pHeader->wBitsPerSample;
+ m_rate = pHeader->nSamplesPerSec;
+ m_channels = pHeader->nChannels;
+
+ m_sampleSize = (m_bits * m_channels) / 8;
+
+ // this can never be zero -- other functions divide by this.
+ // This should never happen, but avoid crashing
+ if ( m_sampleSize <= 0 )
+ m_sampleSize = 1;
+
+ // For non-standard waves (like ADPCM) store the header, it has some useful data
+ if ( m_format != WAVE_FORMAT_PCM )
+ {
+ m_pHeader = new char[headerSize];
+ memcpy( m_pHeader, pHeader, headerSize );
+ if ( m_format == WAVE_FORMAT_ADPCM )
+ {
+ // treat ADPCM sources as a file of bytes. They are decoded by the mixer
+ m_sampleSize = 1;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CAudioSourceWave::TrueSampleSize( void )
+{
+ if ( m_format == WAVE_FORMAT_ADPCM )
+ {
+ return 0.5f;
+ }
+ return (float)m_sampleSize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Total number of samples in this source
+// Output : int
+//-----------------------------------------------------------------------------
+int CAudioSourceWave::SampleCount( void )
+{
+ if ( m_format == WAVE_FORMAT_ADPCM )
+ {
+ ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)m_pHeader;
+ int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
+ blockSize += 7 * pFormat->wfx.nChannels;
+
+ int blockCount = m_sampleCount / blockSize;
+ int blockRem = m_sampleCount % blockSize;
+
+ // total samples in complete blocks
+ int sampleCount = blockCount * pFormat->wSamplesPerBlock;
+
+ // add remaining in a short block
+ if ( blockRem )
+ {
+ sampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / m_channels);
+ }
+ return sampleCount;
+ }
+ return m_sampleCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Do any sample conversion
+// For 8 bit PCM, convert to signed because the mixing routine assumes this
+// Input : *pData - pointer to sample data
+// sampleCount - number of samples
+//-----------------------------------------------------------------------------
+void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
+{
+ if ( m_format == WAVE_FORMAT_PCM )
+ {
+ if ( m_bits == 8 )
+ {
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ for ( int j = 0; j < m_channels; j++ )
+ {
+ *pData = (unsigned char)((int)((unsigned)*pData) - 128);
+ pData++;
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &walk -
+//-----------------------------------------------------------------------------
+void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
+{
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.EnsureCapacity( walk.ChunkSize() );
+ walk.ChunkRead( buf.Base() );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
+
+ m_Sentence.InitFromDataChunk( buf.Base(), buf.TellPut() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse base chunks
+// Input : &walk - riff file to parse
+// : chunkName - name of the chunk to parse
+//-----------------------------------------------------------------------------
+// UNDONE: Move parsing loop here and drop each chunk into a virtual function
+// instead of this being virtual.
+void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
+{
+ switch( chunkName )
+ {
+ case WAVE_CUE:
+ {
+ m_loopStart = ParseCueChunk( walk );
+ }
+ break;
+ case WAVE_VALVEDATA:
+ {
+ ParseSentence( walk );
+ }
+ break;
+ // unknown/don't care
+ default:
+ {
+ ChunkError( walk.ChunkName() );
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CSentence
+//-----------------------------------------------------------------------------
+CSentence *CAudioSourceWave::GetSentence( void )
+{
+ return &m_Sentence;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Bastardized construction routine. This is just to avoid complex
+// constructor functions so code can be shared more easily by sub-classes
+// Input : *pFormatBuffer - RIFF header
+// formatSize - header size
+// &walk - RIFF file
+//-----------------------------------------------------------------------------
+void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
+{
+ Init( pFormatBuffer, formatSize );
+
+ while ( walk.ChunkAvailable() )
+ {
+ ParseChunk( walk, walk.ChunkName() );
+ walk.ChunkNext();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wave file that is completely in memory
+// UNDONE: Implement Lock/Unlock and caching
+//-----------------------------------------------------------------------------
+class CAudioSourceMemWave : public CAudioSourceWave
+{
+public:
+ CAudioSourceMemWave( void );
+ ~CAudioSourceMemWave( void );
+
+ // Create an instance (mixer) of this audio source
+ virtual CAudioMixer *CreateMixer( void );
+
+ virtual void ParseChunk( IterateRIFF &walk, int chunkName );
+ void ParseDataChunk( IterateRIFF &walk );
+
+ virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true );
+ virtual float GetRunningLength( void ) { return CAudioSourceWave::GetRunningLength(); };
+
+ virtual int GetNumChannels();
+
+private:
+ char *m_pData; // wave data
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterator for wave data (this is to abstract streaming/buffering)
+//-----------------------------------------------------------------------------
+class CWaveDataMemory : public CWaveData
+{
+public:
+ CWaveDataMemory( CAudioSourceWave &source ) : m_source(source) {}
+ ~CWaveDataMemory( void ) {}
+ CAudioSourceWave &Source( void ) { return m_source; }
+
+ // this file is in memory, simply pass along the data request to the source
+ virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, bool forward /*= true*/ )
+ {
+ return m_source.GetOutputData( pData, sampleIndex, sampleCount, forward );
+ }
+private:
+ CAudioSourceWave &m_source; // pointer to source
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: NULL the wave data pointer (we haven't loaded yet)
+//-----------------------------------------------------------------------------
+CAudioSourceMemWave::CAudioSourceMemWave( void )
+{
+ m_pData = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Free any wave data we've allocated
+//-----------------------------------------------------------------------------
+CAudioSourceMemWave::~CAudioSourceMemWave( void )
+{
+ delete[] m_pData;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates a mixer and initializes it with an appropriate mixer
+//-----------------------------------------------------------------------------
+CAudioMixer *CAudioSourceMemWave::CreateMixer( void )
+{
+ return CreateWaveMixer( new CWaveDataMemory(*this), m_format, m_channels, m_bits );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: parse chunks with unique processing to in-memory waves
+// Input : &walk - RIFF file
+//-----------------------------------------------------------------------------
+void CAudioSourceMemWave::ParseChunk( IterateRIFF &walk, int chunkName )
+{
+ switch( chunkName )
+ {
+ // this is the audio data
+ case WAVE_DATA:
+ {
+ ParseDataChunk( walk );
+ }
+ return;
+ }
+
+ CAudioSourceWave::ParseChunk( walk, chunkName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: reads the actual sample data and parses it
+// Input : &walk - RIFF file
+//-----------------------------------------------------------------------------
+void CAudioSourceMemWave::ParseDataChunk( IterateRIFF &walk )
+{
+ int size = walk.ChunkSize();
+
+ // create a buffer for the samples
+ m_pData = new char[size];
+
+ // load them into memory
+ walk.ChunkRead( m_pData );
+
+ if ( m_format == WAVE_FORMAT_PCM )
+ {
+ // number of samples loaded
+ m_sampleCount = size / m_sampleSize;
+
+ // some samples need to be converted
+ ConvertSamples( m_pData, m_sampleCount );
+ }
+ else if ( m_format == WAVE_FORMAT_ADPCM )
+ {
+ // The ADPCM mixers treat the wave source as a flat file of bytes.
+ m_sampleSize = 1;
+ // Since each "sample" is a byte (this is a flat file), the number of samples is the file size
+ m_sampleCount = size;
+
+ // file says 4, output is 16
+ m_bits = 16;
+ }
+}
+
+int CAudioSourceMemWave::GetNumChannels()
+{
+ return m_channels;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: parses loop information from a cue chunk
+// Input : &walk - RIFF iterator
+// Output : int loop start position
+//-----------------------------------------------------------------------------
+int CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
+{
+ // Cue chunk as specified by RIFF format
+ // see $/research/jay/sound/riffnew.htm
+ struct
+ {
+ unsigned int dwName;
+ unsigned int dwPosition;
+ unsigned int fccChunk;
+ unsigned int dwChunkStart;
+ unsigned int dwBlockStart;
+ unsigned int dwSampleOffset;
+ } cue_chunk;
+
+ int cueCount;
+
+ // assume that the cue chunk stored in the wave is the start of the loop
+ // assume only one cue chunk, UNDONE: Test this assumption here?
+ cueCount = walk.ChunkReadInt();
+
+ walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
+ return cue_chunk.dwSampleOffset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: get the wave header
+//-----------------------------------------------------------------------------
+void *CAudioSourceWave::GetHeader( void )
+{
+ return m_pHeader;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: wrap the position wrt looping
+// Input : samplePosition - absolute position
+// Output : int - looped position
+//-----------------------------------------------------------------------------
+int CAudioSourceWave::ConvertLoopedPosition( int samplePosition )
+{
+ // if the wave is looping and we're past the end of the sample
+ // convert to a position within the loop
+ // At the end of the loop, we return a short buffer, and subsequent call
+ // will loop back and get the rest of the buffer
+ if ( m_loopStart >= 0 )
+ {
+ if ( samplePosition >= m_sampleCount )
+ {
+ // size of loop
+ int loopSize = m_sampleCount - m_loopStart;
+ // subtract off starting bit of the wave
+ samplePosition -= m_loopStart;
+
+ if ( loopSize )
+ {
+ // "real" position in memory (mod off extra loops)
+ samplePosition = m_loopStart + (samplePosition % loopSize);
+ }
+ // ERROR? if no loopSize
+ }
+ }
+
+ return samplePosition;
+}
+
+bool CAudioSourceWave::IsStereoWav( void )
+{
+ return (m_channels == 2) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : **pData - output pointer to samples
+// samplePosition - position (in samples not bytes)
+// sampleCount - number of samples (not bytes)
+// Output : int - number of samples available
+//-----------------------------------------------------------------------------
+int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward /*= true*/ )
+{
+ // handle position looping
+ samplePosition = ConvertLoopedPosition( samplePosition );
+
+ // how many samples are available (linearly not counting looping)
+ int availableSampleCount = m_sampleCount - samplePosition;
+ if ( !forward )
+ {
+ if ( samplePosition >= m_sampleCount )
+ {
+ availableSampleCount = 0;
+ }
+ else
+ {
+ availableSampleCount = samplePosition;
+ }
+ }
+
+ // may be asking for a sample out of range, clip at zero
+ if ( availableSampleCount < 0 )
+ availableSampleCount = 0;
+
+ // clip max output samples to max available
+ if ( sampleCount > availableSampleCount )
+ sampleCount = availableSampleCount;
+
+ // byte offset in sample database
+ samplePosition *= m_sampleSize;
+
+ // if we are returning some samples, store the pointer
+ if ( sampleCount )
+ {
+ *pData = m_pData + samplePosition;
+ }
+
+ return sampleCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a wave audio source (streaming or in memory)
+// Input : *pName - file name
+// streaming - if true, don't load, stream each instance
+// Output : CAudioSource * - a new source
+//-----------------------------------------------------------------------------
+// UNDONE : Pool these and check for duplicates?
+CAudioSource *CreateWave( const char *pName )
+{
+ char formatBuffer[1024];
+ InFileRIFF riff( pName, io );
+
+ // UNDONE: Don't use printf to handle errors
+ if ( riff.RIFFName() != RIFF_WAVE )
+ {
+ printf("Bad RIFF file type %s\n", pName );
+ return NULL;
+ }
+
+ // set up the iterator for the whole file (root RIFF is a chunk)
+ IterateRIFF walk( riff, riff.RIFFSize() );
+
+ int format = 0;
+ int formatSize = 0;
+
+ // This chunk must be first as it contains the wave's format
+ // break out when we've parsed it
+ while ( walk.ChunkAvailable() && format == 0 )
+ {
+ switch( walk.ChunkName() )
+ {
+ case WAVE_FMT:
+ {
+ if ( walk.ChunkSize() <= 1024 )
+ {
+ walk.ChunkRead( formatBuffer );
+ formatSize = walk.ChunkSize();
+ format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
+ }
+ }
+ break;
+ default:
+ {
+ ChunkError( walk.ChunkName() );
+ }
+ break;
+ }
+ walk.ChunkNext();
+ }
+
+ // Not really a WAVE file or no format chunk, bail
+ if ( !format )
+ return NULL;
+
+ CAudioSourceWave *pWave;
+
+ // create the source from this file
+ pWave = new CAudioSourceMemWave();
+
+ // init the wave source
+ pWave->Setup( formatBuffer, formatSize, walk );
+
+ return pWave;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper for CreateWave()
+//-----------------------------------------------------------------------------
+CAudioSource *Audio_CreateMemoryWave( const char *pName )
+{
+ return CreateWave( pName );
+}
diff --git a/utils/hlfaceposer/snd_wave_source.h b/utils/hlfaceposer/snd_wave_source.h
new file mode 100644
index 0000000..f403203
--- /dev/null
+++ b/utils/hlfaceposer/snd_wave_source.h
@@ -0,0 +1,78 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SND_WAVE_SOURCE_H
+#define SND_WAVE_SOURCE_H
+#pragma once
+
+#include "snd_audio_source.h"
+#include "sentence.h"
+
+class IterateRIFF;
+
+class CAudioSourceWave : public CAudioSource
+{
+public:
+ CAudioSourceWave( void );
+ ~CAudioSourceWave( void );
+
+ void Setup( const char *pFormat, int formatSize, IterateRIFF &walk );
+
+ virtual int SampleRate( void ) { return m_rate; }
+ inline int SampleSize( void ) { return m_sampleSize; }
+ virtual float TrueSampleSize( void );
+
+ void *GetHeader( void );
+
+ // Legacy
+ virtual void ParseChunk( IterateRIFF &walk, int chunkName );
+
+ virtual void ParseSentence( IterateRIFF &walk );
+
+ void ConvertSamples( char *pData, int sampleCount );
+ bool IsLooped( void ) { return (m_loopStart >= 0) ? true : false; }
+ bool IsStreaming( void ) { return false; }
+ bool IsStereoWav( void );
+ int ConvertLoopedPosition( int samplePosition );
+
+ int SampleCount( void );
+
+ virtual float GetRunningLength( void )
+ {
+ if ( m_rate > 0.0 )
+ {
+ return (float)SampleCount() / m_rate;
+ }
+ return 0.0f; }
+
+ CSentence *GetSentence( void );
+
+protected:
+ // returns the loop start from a cue chunk
+ int ParseCueChunk( IterateRIFF &walk );
+ void Init( const char *pHeaderBuffer, int headerSize );
+
+ int m_bits;
+ int m_rate;
+ int m_channels;
+ int m_format;
+ int m_sampleSize;
+ int m_loopStart;
+ int m_sampleCount;
+
+private:
+ char *m_pHeader;
+ CSentence m_Sentence;
+};
+
+#endif // SND_WAVE_SOURCE_H
diff --git a/utils/hlfaceposer/sound.cpp b/utils/hlfaceposer/sound.cpp
new file mode 100644
index 0000000..3d9f6e5
--- /dev/null
+++ b/utils/hlfaceposer/sound.cpp
@@ -0,0 +1,1800 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#pragma warning( disable : 4201 )
+#include <mmsystem.h>
+#include <stdio.h>
+#include <math.h>
+#include "snd_audio_source.h"
+#include "AudioWaveOutput.h"
+#include "ifaceposersound.h"
+#include "StudioModel.h"
+#include "hlfaceposer.h"
+#include "expressions.h"
+#include "expclass.h"
+#include "PhonemeConverter.h"
+#include "utlvector.h"
+#include "filesystem.h"
+#include "sentence.h"
+#include "faceposer_models.h"
+#include "iclosecaptionmanager.h"
+#include "phonemeeditor.h"
+#include "wavebrowser.h"
+#include "choreoscene.h"
+#include "choreoview.h"
+#include "KeyValues.h"
+
+extern ISoundEmitterSystemBase *soundemitter;
+
+typedef struct channel_s
+{
+ int leftvol;
+ int rightvol;
+ int rleftvol;
+ int rrightvol;
+ float pitch;
+} channel_t;
+
+#define INPUT_BUFFER_COUNT 32
+
+class CAudioWaveInput : public CAudioInput
+{
+public:
+ CAudioWaveInput( void );
+ ~CAudioWaveInput( void );
+
+ // Returns the current count of available samples
+ int SampleCount( void );
+
+ // returns the size of each sample in bytes
+ int SampleSize( void ) { return m_sampleSize; }
+
+ // returns the sampling rate of the data
+ int SampleRate( void ) { return m_sampleRate; }
+
+ // returns a pointer to the actual data
+ void *SampleData( void );
+
+ // release the available data (mark as done)
+ void SampleRelease( void );
+
+ // returns the mono/stereo status of this device (true if stereo)
+ bool IsStereo( void ) { return m_isStereo; }
+
+ // begin sampling
+ void Start( void );
+
+ // stop sampling
+ void Stop( void );
+
+ void WaveMessage( HWAVEIN hdevice, UINT uMsg, DWORD dwParam1, DWORD dwParam2 );
+
+private:
+ void OpenDevice( void );
+ bool ValidDevice( void ) { return m_deviceId != 0xFFFFFFFF; }
+ void ClearDevice( void ) { m_deviceId = 0xFFFFFFFF; }
+
+ // returns true if the new format is better
+ bool BetterFormat( DWORD dwNewFormat, DWORD dwOldFormat );
+
+ void InitReadyList( void );
+ void AddToReadyList( WAVEHDR *pBuffer );
+ void PopReadyList( void );
+
+ WAVEHDR *m_pReadyList;
+
+ int m_sampleSize;
+ int m_sampleRate;
+ bool m_isStereo;
+
+ UINT m_deviceId;
+ HWAVEIN m_deviceHandle;
+
+ WAVEHDR *m_buffers[ INPUT_BUFFER_COUNT ];
+};
+
+extern "C" void CALLBACK WaveData( HWAVEIN hwi, UINT uMsg, CAudioWaveInput *pAudio, DWORD dwParam1, DWORD dwParam2 );
+
+CAudioWaveInput::CAudioWaveInput( void )
+{
+ memset( m_buffers, 0, sizeof( m_buffers ) );
+ int deviceCount = (int)waveInGetNumDevs();
+ UINT deviceId = 0;
+ DWORD deviceFormat = 0;
+
+ int i;
+ for ( i = 0; i < deviceCount; i++ )
+ {
+ WAVEINCAPS waveCaps;
+ MMRESULT errorCode = waveInGetDevCaps( (UINT)i, &waveCaps, sizeof(waveCaps) );
+ if ( errorCode == MMSYSERR_NOERROR )
+ {
+ // valid device
+ if ( BetterFormat( waveCaps.dwFormats, deviceFormat ) )
+ {
+ deviceId = i;
+ deviceFormat = waveCaps.dwFormats;
+ }
+ }
+ }
+
+ if ( !deviceFormat )
+ {
+ m_deviceId = 0xFFFFFFFF;
+ m_sampleSize = 0;
+ m_sampleRate = 0;
+ m_isStereo = false;
+ }
+ else
+ {
+ m_deviceId = deviceId;
+ m_sampleRate = 44100;
+ m_isStereo = false;
+ if ( deviceFormat & WAVE_FORMAT_4M16 )
+ {
+ m_sampleSize = 2;
+ }
+ else if ( deviceFormat & WAVE_FORMAT_4M08 )
+ {
+ m_sampleSize = 1;
+ }
+ else
+ {
+ // ERROR!
+ }
+
+ OpenDevice();
+ }
+
+ InitReadyList();
+}
+
+CAudioWaveInput::~CAudioWaveInput( void )
+{
+ if ( ValidDevice() )
+ {
+ Stop();
+ waveInReset( m_deviceHandle );
+ waveInClose( m_deviceHandle );
+ for ( int i = 0; i < INPUT_BUFFER_COUNT; i++ )
+ {
+ if ( m_buffers[i] )
+ {
+ waveInUnprepareHeader( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) );
+ delete[] m_buffers[i]->lpData;
+ delete m_buffers[i];
+ }
+ m_buffers[i] = NULL;
+ }
+ ClearDevice();
+ }
+}
+
+void CALLBACK WaveData( HWAVEIN hwi, UINT uMsg, CAudioWaveInput *pAudio, DWORD dwParam1, DWORD dwParam2 )
+{
+ if ( pAudio )
+ {
+ pAudio->WaveMessage( hwi, uMsg, dwParam1, dwParam2 );
+ }
+}
+
+void CAudioWaveInput::WaveMessage( HWAVEIN hdevice, UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
+{
+ if ( hdevice != m_deviceHandle )
+ return;
+ switch( uMsg )
+ {
+ case WIM_DATA:
+ WAVEHDR *pHeader = (WAVEHDR *)dwParam1;
+ AddToReadyList( pHeader );
+ break;
+ }
+}
+
+void CAudioWaveInput::OpenDevice( void )
+{
+ if ( !ValidDevice() )
+ return;
+
+ WAVEFORMATEX format;
+
+ memset( &format, 0, sizeof(format) );
+ format.nAvgBytesPerSec = m_sampleRate * m_sampleSize;
+ format.nChannels = 1;
+ format.wBitsPerSample = m_sampleSize * 8;
+ format.nSamplesPerSec = m_sampleRate;
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nBlockAlign = m_sampleSize;
+
+ MMRESULT errorCode = waveInOpen( &m_deviceHandle, m_deviceId, &format, (DWORD)WaveData, (DWORD)this, CALLBACK_FUNCTION );
+ if ( errorCode == MMSYSERR_NOERROR )
+ {
+ // valid device opened
+ int bufferSize = m_sampleSize * m_sampleRate / INPUT_BUFFER_COUNT; // total of one second of data
+
+ // allocate buffers
+ for ( int i = 0; i < INPUT_BUFFER_COUNT; i++ )
+ {
+ m_buffers[i] = new WAVEHDR;
+ m_buffers[i]->lpData = new char[ bufferSize ];
+ m_buffers[i]->dwBufferLength = bufferSize;
+ m_buffers[i]->dwUser = 0;
+ m_buffers[i]->dwFlags = 0;
+
+ waveInPrepareHeader( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) );
+ waveInAddBuffer( m_deviceHandle, m_buffers[i], sizeof( *m_buffers[i] ) );
+ }
+ }
+ else
+ {
+ ClearDevice();
+ }
+}
+
+void CAudioWaveInput::Start( void )
+{
+ if ( !ValidDevice() )
+ return;
+
+ waveInStart( m_deviceHandle );
+}
+
+void CAudioWaveInput::Stop( void )
+{
+ if ( !ValidDevice() )
+ return;
+
+ waveInStop( m_deviceHandle );
+}
+
+void CAudioWaveInput::InitReadyList( void )
+{
+ m_pReadyList = NULL;
+}
+
+void CAudioWaveInput::AddToReadyList( WAVEHDR *pBuffer )
+{
+ WAVEHDR **pList = &m_pReadyList;
+
+ waveInUnprepareHeader( m_deviceHandle, pBuffer, sizeof(*pBuffer) );
+ // insert at the tail of the list
+ while ( *pList )
+ {
+ pList = reinterpret_cast<WAVEHDR **>(&((*pList)->dwUser));
+ }
+ pBuffer->dwUser = NULL;
+ *pList = pBuffer;
+}
+
+
+void CAudioWaveInput::PopReadyList( void )
+{
+ if ( m_pReadyList )
+ {
+ WAVEHDR *pBuffer = m_pReadyList;
+ m_pReadyList = reinterpret_cast<WAVEHDR *>(m_pReadyList->dwUser);
+ waveInPrepareHeader( m_deviceHandle, pBuffer, sizeof(*pBuffer) );
+ waveInAddBuffer( m_deviceHandle, pBuffer, sizeof(*pBuffer) );
+ }
+}
+
+
+
+#define WAVE_FORMAT_STEREO (WAVE_FORMAT_1S08|WAVE_FORMAT_1S16|WAVE_FORMAT_2S08|WAVE_FORMAT_2S16|WAVE_FORMAT_4S08|WAVE_FORMAT_4S16)
+#define WAVE_FORMATS_UNDERSTOOD (0xFFF)
+#define WAVE_FORMAT_11K (WAVE_FORMAT_1M08|WAVE_FORMAT_1M16)
+#define WAVE_FORMAT_22K (WAVE_FORMAT_2M08|WAVE_FORMAT_2M16)
+#define WAVE_FORMAT_44K (WAVE_FORMAT_4M08|WAVE_FORMAT_4M16)
+
+static int HighestBit( DWORD dwFlags )
+{
+ int i = 31;
+ while ( i )
+ {
+ if ( dwFlags & (1<<i) )
+ return i;
+ i--;
+ }
+
+ return 0;
+}
+
+bool CAudioWaveInput::BetterFormat( DWORD dwNewFormat, DWORD dwOldFormat )
+{
+ dwNewFormat &= WAVE_FORMATS_UNDERSTOOD & (~WAVE_FORMAT_STEREO);
+ dwOldFormat &= WAVE_FORMATS_UNDERSTOOD & (~WAVE_FORMAT_STEREO);
+
+ // our target format is 44.1KHz, mono, 16-bit
+ if ( HighestBit(dwOldFormat) >= HighestBit(dwNewFormat) )
+ return false;
+
+ return true;
+}
+
+
+int CAudioWaveInput::SampleCount( void )
+{
+ if ( !ValidDevice() )
+ return 0;
+
+ if ( m_pReadyList )
+ {
+ switch( SampleSize() )
+ {
+ case 2:
+ return m_pReadyList->dwBytesRecorded >> 1;
+ case 1:
+ return m_pReadyList->dwBytesRecorded;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+void *CAudioWaveInput::SampleData( void )
+{
+ if ( !ValidDevice() )
+ return NULL;
+
+ if ( m_pReadyList )
+ {
+ return m_pReadyList->lpData;
+ }
+
+ return NULL;
+}
+
+
+// release the available data (mark as done)
+void CAudioWaveInput::SampleRelease( void )
+{
+ PopReadyList();
+}
+
+
+// factory to create a suitable audio input for this system
+CAudioInput *CAudioInput::Create( void )
+{
+ // sound source is a singleton for now
+ static CAudioInput *pSource = NULL;
+
+ if ( !pSource )
+ {
+ pSource = new CAudioWaveInput;
+ }
+
+ return pSource;
+}
+
+void CAudioDeviceSWMix::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward )
+{
+ int sampleIndex = 0;
+ fixedint sampleFrac = inputOffset;
+
+ int fixup = 0;
+ int fixupstep = 1;
+
+ if ( !forward )
+ {
+ fixup = outCount - 1;
+ fixupstep = -1;
+ }
+
+ for ( int i = 0; i < outCount; i++, fixup += fixupstep )
+ {
+ int dest = max( outputOffset + fixup, 0 );
+
+ m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
+ m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex];
+ sampleFrac += rateScaleFix;
+ sampleIndex += FIX_INTPART(sampleFrac);
+ sampleFrac = FIX_FRACPART(sampleFrac);
+ }
+}
+
+
+void CAudioDeviceSWMix::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward )
+{
+ int sampleIndex = 0;
+ fixedint sampleFrac = inputOffset;
+
+ int fixup = 0;
+ int fixupstep = 1;
+
+ if ( !forward )
+ {
+ fixup = outCount - 1;
+ fixupstep = -1;
+ }
+
+ for ( int i = 0; i < outCount; i++, fixup += fixupstep )
+ {
+ int dest = max( outputOffset + fixup, 0 );
+
+ m_paintbuffer[ dest ].left += pChannel->leftvol * pData[sampleIndex];
+ m_paintbuffer[ dest ].right += pChannel->rightvol * pData[sampleIndex+1];
+ sampleFrac += rateScaleFix;
+ sampleIndex += FIX_INTPART(sampleFrac)<<1;
+ sampleFrac = FIX_FRACPART(sampleFrac);
+ }
+}
+
+
+void CAudioDeviceSWMix::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward )
+{
+ int sampleIndex = 0;
+ fixedint sampleFrac = inputOffset;
+
+ int fixup = 0;
+ int fixupstep = 1;
+
+ if ( !forward )
+ {
+ fixup = outCount - 1;
+ fixupstep = -1;
+ }
+
+ for ( int i = 0; i < outCount; i++, fixup += fixupstep )
+ {
+ int dest = max( outputOffset + fixup, 0 );
+
+ m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
+ m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex])>>8;
+ sampleFrac += rateScaleFix;
+ sampleIndex += FIX_INTPART(sampleFrac);
+ sampleFrac = FIX_FRACPART(sampleFrac);
+ }
+}
+
+
+void CAudioDeviceSWMix::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, int rateScaleFix, int outCount, int timecompress, bool forward )
+{
+ int sampleIndex = 0;
+ fixedint sampleFrac = inputOffset;
+
+ int fixup = 0;
+ int fixupstep = 1;
+
+ if ( !forward )
+ {
+ fixup = outCount - 1;
+ fixupstep = -1;
+ }
+
+ for ( int i = 0; i < outCount; i++, fixup += fixupstep )
+ {
+ int dest = max( outputOffset + fixup, 0 );
+
+ m_paintbuffer[ dest ].left += (pChannel->leftvol * pData[sampleIndex])>>8;
+ m_paintbuffer[ dest ].right += (pChannel->rightvol * pData[sampleIndex+1])>>8;
+
+ sampleFrac += rateScaleFix;
+ sampleIndex += FIX_INTPART(sampleFrac)<<1;
+ sampleFrac = FIX_FRACPART(sampleFrac);
+ }
+}
+
+
+int CAudioDeviceSWMix::MaxSampleCount( void )
+{
+ return PAINTBUFFER_SIZE;
+}
+
+void CAudioDeviceSWMix::MixBegin( void )
+{
+ memset( m_paintbuffer, 0, sizeof(m_paintbuffer) );
+}
+
+void CAudioDeviceSWMix::TransferBufferStereo16( short *pOutput, int sampleCount )
+{
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ if ( m_paintbuffer[i].left > 32767 )
+ m_paintbuffer[i].left = 32767;
+ else if ( m_paintbuffer[i].left < -32768 )
+ m_paintbuffer[i].left = -32768;
+
+ if ( m_paintbuffer[i].right > 32767 )
+ m_paintbuffer[i].right = 32767;
+ else if ( m_paintbuffer[i].right < -32768 )
+ m_paintbuffer[i].right = -32768;
+
+ *pOutput++ = (short)m_paintbuffer[i].left;
+ *pOutput++ = (short)m_paintbuffer[i].right;
+ }
+}
+
+CAudioWaveOutput::CAudioWaveOutput( void )
+{
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ CAudioBuffer *buffer = &m_buffers[ i ];
+ Assert( buffer );
+ buffer->hdr = NULL;
+ buffer->submitted = false;
+ buffer->submit_sample_count = false;
+ }
+
+ ClearDevice();
+ OpenDevice();
+
+ m_mixTime = -1;
+ m_sampleIndex = 0;
+ memset( m_sourceList, 0, sizeof(m_sourceList) );
+
+ m_nEstimatedSamplesAhead = (int)( ( float ) OUTPUT_SAMPLE_RATE / 10.0f );
+}
+
+void CAudioWaveOutput::RemoveMixerChannelReferences( CAudioMixer *mixer )
+{
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ RemoveFromReferencedList( mixer, &m_buffers[ i ] );
+ }
+}
+
+void CAudioWaveOutput::AddToReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
+{
+ // Already in list
+ for ( int i = 0; i < buffer->m_Referenced.Size(); i++ )
+ {
+ if ( buffer->m_Referenced[ i ].mixer == mixer )
+ {
+ return;
+ }
+ }
+
+ // Just remove it
+ int idx = buffer->m_Referenced.AddToTail();
+
+ CAudioMixerState *state = &buffer->m_Referenced[ idx ];
+ state->mixer = mixer;
+ state->submit_mixer_sample = mixer->GetSamplePosition();
+
+}
+
+void CAudioWaveOutput::RemoveFromReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
+{
+ for ( int i = 0; i < buffer->m_Referenced.Size(); i++ )
+ {
+ if ( buffer->m_Referenced[ i ].mixer == mixer )
+ {
+ buffer->m_Referenced.Remove( i );
+ break;
+ }
+ }
+}
+
+bool CAudioWaveOutput::IsSoundInReferencedList( CAudioMixer *mixer, CAudioBuffer *buffer )
+{
+ for ( int i = 0; i < buffer->m_Referenced.Size(); i++ )
+ {
+ if ( buffer->m_Referenced[ i ].mixer == mixer )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CAudioWaveOutput::IsSourceReferencedByActiveBuffer( CAudioMixer *mixer )
+{
+ if ( !ValidDevice() )
+ return false;
+
+ CAudioBuffer *buffer;
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ buffer = &m_buffers[ i ];
+ if ( !buffer->submitted )
+ continue;
+
+ if ( buffer->hdr->dwFlags & WHDR_DONE )
+ continue;
+
+ // See if it's referenced
+ if ( IsSoundInReferencedList( mixer, buffer ) )
+ return true;
+ }
+
+ return false;
+}
+
+CAudioWaveOutput::~CAudioWaveOutput( void )
+{
+ if ( ValidDevice() )
+ {
+ waveOutReset( m_deviceHandle );
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ if ( m_buffers[i].hdr )
+ {
+ waveOutUnprepareHeader( m_deviceHandle, m_buffers[i].hdr, sizeof(*m_buffers[i].hdr) );
+ delete[] m_buffers[i].hdr->lpData;
+ delete m_buffers[i].hdr;
+ }
+ m_buffers[i].hdr = NULL;
+ m_buffers[i].submitted = false;
+ m_buffers[i].submit_sample_count = 0;
+ m_buffers[i].m_Referenced.Purge();
+ }
+ waveOutClose( m_deviceHandle );
+ ClearDevice();
+ }
+}
+
+
+
+CAudioBuffer *CAudioWaveOutput::GetEmptyBuffer( void )
+{
+ CAudioBuffer *pOutput = NULL;
+ if ( ValidDevice() )
+ {
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ if ( !(m_buffers[ i ].submitted ) ||
+ m_buffers[i].hdr->dwFlags & WHDR_DONE )
+ {
+ pOutput = &m_buffers[i];
+ pOutput->submitted = true;
+ pOutput->m_Referenced.Purge();
+ break;
+ }
+ }
+ }
+
+ return pOutput;
+}
+
+void CAudioWaveOutput::SilenceBuffer( short *pSamples, int sampleCount )
+{
+ int i;
+
+ for ( i = 0; i < sampleCount; i++ )
+ {
+ // left
+ *pSamples++ = 0;
+ // right
+ *pSamples++ = 0;
+ }
+}
+
+void CAudioWaveOutput::Flush( void )
+{
+ waveOutReset( m_deviceHandle );
+}
+
+// mix a buffer up to time (time is absolute)
+void CAudioWaveOutput::Update( float time )
+{
+ if ( !ValidDevice() )
+ return;
+
+ // reset the system
+ if ( m_mixTime < 0 || time < m_baseTime )
+ {
+ m_baseTime = time;
+ m_mixTime = 0;
+ }
+
+ // put time in our coordinate frame
+ time -= m_baseTime;
+
+ if ( time > m_mixTime )
+ {
+ CAudioBuffer *pBuffer = GetEmptyBuffer();
+
+ // no free buffers, mixing is ahead of the playback!
+ if ( !pBuffer || !pBuffer->hdr )
+ {
+ //Con_Printf( "out of buffers\n" );
+ return;
+ }
+
+ // UNDONE: These numbers are constants
+ // calc number of samples (2 channels * 2 bytes per sample)
+ int sampleCount = pBuffer->hdr->dwBufferLength >> 2;
+ m_mixTime += sampleCount * (1.0f / OUTPUT_SAMPLE_RATE);
+
+ short *pSamples = reinterpret_cast<short *>(pBuffer->hdr->lpData);
+
+ SilenceBuffer( pSamples, sampleCount );
+
+ int tempCount = sampleCount;
+
+ while ( tempCount > 0 )
+ {
+ if ( tempCount > m_audioDevice.MaxSampleCount() )
+ sampleCount = m_audioDevice.MaxSampleCount();
+ else
+ sampleCount = tempCount;
+
+ m_audioDevice.MixBegin();
+ for ( int i = 0; i < MAX_CHANNELS; i++ )
+ {
+ CAudioMixer *pSource = m_sourceList[i];
+ if ( !pSource )
+ continue;
+
+ StudioModel *model = NULL;
+
+ int modelindex = pSource->GetModelIndex();
+ if ( modelindex >= 0 )
+ {
+ model = models->GetStudioModel( modelindex );
+ }
+ else
+ {
+ if ( g_pPhonemeEditor->IsActiveTool() || g_pWaveBrowser->IsActiveTool() )
+ {
+ model = models->GetActiveStudioModel();
+
+ }
+ }
+
+ if ( model && !model->m_mouth.IsSourceReferenced( pSource->GetSource() ) )
+ {
+ CChoreoScene *pScene = g_pChoreoView->GetScene();
+ bool bIgnorePhonemes = pScene ? pScene->ShouldIgnorePhonemes() : false;
+ model->m_mouth.AddSource( pSource->GetSource(), bIgnorePhonemes );
+ if ( modelindex < 0 )
+ {
+ pSource->SetModelIndex( models->GetIndexForStudioModel( model ) );
+ }
+ }
+
+ int currentsample = pSource->GetSamplePosition();
+ bool forward = pSource->GetDirection();
+
+ if ( pSource->GetActive() )
+ {
+ if ( !pSource->MixDataToDevice( &m_audioDevice, pSource->GetChannel(), currentsample, sampleCount, SampleRate(), forward ) )
+ {
+ // Source becomes inactive when last submitted sample is finally
+ // submitted. But it lingers until it's no longer referenced
+ pSource->SetActive( false );
+ }
+ else
+ {
+ AddToReferencedList( pSource, pBuffer );
+ }
+ }
+ else
+ {
+ if ( !IsSourceReferencedByActiveBuffer( pSource ) )
+ {
+ if ( !pSource->GetAutoDelete() )
+ {
+ FreeChannel( i );
+ }
+ }
+ else
+ {
+ pSource->IncrementSamples( pSource->GetChannel(), currentsample, sampleCount, SampleRate(), forward );
+ }
+ }
+
+ }
+
+ m_audioDevice.TransferBufferStereo16( pSamples, sampleCount );
+
+ m_sampleIndex += sampleCount;
+ tempCount -= sampleCount;
+ pSamples += sampleCount * 2;
+ }
+ // if the buffers aren't aligned on sample boundaries, this will hard-lock the machine!
+
+ pBuffer->submit_sample_count = GetOutputPosition();
+
+ waveOutWrite( m_deviceHandle, pBuffer->hdr, sizeof(*(pBuffer->hdr)) );
+ }
+}
+
+int CAudioWaveOutput::GetNumberofSamplesAhead( void )
+{
+ ComputeSampleAheadAmount();
+ return m_nEstimatedSamplesAhead;
+}
+
+float CAudioWaveOutput::GetAmountofTimeAhead( void )
+{
+ ComputeSampleAheadAmount();
+ return ( (float)m_nEstimatedSamplesAhead / (float)OUTPUT_SAMPLE_RATE );
+}
+
+// Find the most recent submitted sample that isn't flagged as whdr_done
+void CAudioWaveOutput::ComputeSampleAheadAmount( void )
+{
+ m_nEstimatedSamplesAhead = 0;
+
+ int newest_sample_index = -1;
+ int newest_sample_count = 0;
+
+ CAudioBuffer *buffer;
+
+ if ( ValidDevice() )
+ {
+
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ buffer = &m_buffers[ i ];
+ if ( !buffer->submitted )
+ continue;
+
+ if ( buffer->hdr->dwFlags & WHDR_DONE )
+ continue;
+
+ if ( buffer->submit_sample_count > newest_sample_count )
+ {
+ newest_sample_index = i;
+ newest_sample_count = buffer->submit_sample_count;
+ }
+ }
+ }
+
+ if ( newest_sample_index == -1 )
+ return;
+
+
+ buffer = &m_buffers[ newest_sample_index ];
+ int currentPos = GetOutputPosition() ;
+ m_nEstimatedSamplesAhead = currentPos - buffer->submit_sample_count;
+}
+
+int CAudioWaveOutput::FindSourceIndex( CAudioMixer *pSource )
+{
+ for ( int i = 0; i < MAX_CHANNELS; i++ )
+ {
+ if ( pSource == m_sourceList[i] )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+CAudioMixer *CAudioWaveOutput::GetMixerForSource( CAudioSource *source )
+{
+ for ( int i = 0; i < MAX_CHANNELS; i++ )
+ {
+ if ( !m_sourceList[i] )
+ continue;
+
+ if ( source == m_sourceList[i]->GetSource() )
+ {
+ return m_sourceList[i];
+ }
+ }
+ return NULL;
+}
+
+void CAudioWaveOutput::AddSource( CAudioMixer *pSource )
+{
+ int slot = 0;
+ for ( int i = 0; i < MAX_CHANNELS; i++ )
+ {
+ if ( !m_sourceList[i] )
+ {
+ slot = i;
+ break;
+ }
+ }
+
+ if ( m_sourceList[slot] )
+ {
+ FreeChannel( slot );
+ }
+ SetChannel( slot, pSource );
+
+ pSource->SetActive( true );
+}
+
+
+void CAudioWaveOutput::StopSounds( void )
+{
+ for ( int i = 0; i < MAX_CHANNELS; i++ )
+ {
+ if ( m_sourceList[i] )
+ {
+ FreeChannel( i );
+ }
+ }
+}
+
+
+void CAudioWaveOutput::SetChannel( int channelIndex, CAudioMixer *pSource )
+{
+ if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
+ return;
+
+ m_sourceList[channelIndex] = pSource;
+}
+
+void CAudioWaveOutput::FreeChannel( int channelIndex )
+{
+ if ( channelIndex < 0 || channelIndex >= MAX_CHANNELS )
+ return;
+
+ if ( m_sourceList[channelIndex] )
+ {
+ StudioModel *model = NULL;
+ int modelindex = m_sourceList[channelIndex]->GetModelIndex();
+ if ( modelindex >= 0)
+ {
+ model = models->GetStudioModel( modelindex );
+ }
+
+ if ( model )
+ {
+ model->m_mouth.RemoveSource( m_sourceList[channelIndex]->GetSource() );
+ }
+
+ RemoveMixerChannelReferences( m_sourceList[channelIndex] );
+
+ delete m_sourceList[channelIndex];
+ m_sourceList[channelIndex] = NULL;
+ }
+}
+
+int CAudioWaveOutput::GetOutputPosition( void )
+{
+ if ( !m_deviceHandle )
+ return 0;
+
+ MMTIME mmtime;
+ mmtime.wType = TIME_SAMPLES;
+ waveOutGetPosition( m_deviceHandle, &mmtime, sizeof( MMTIME ) );
+
+ // Convert time to sample count
+ return ( mmtime.u.sample );
+}
+
+void CAudioWaveOutput::OpenDevice( void )
+{
+ WAVEFORMATEX waveFormat;
+
+ memset( &waveFormat, 0, sizeof(waveFormat) );
+ // Select a PCM, 16-bit stereo playback device
+ waveFormat.cbSize = sizeof(waveFormat);
+ waveFormat.nAvgBytesPerSec = OUTPUT_SAMPLE_RATE * 2 * 2;
+ waveFormat.nBlockAlign = 2 * 2; // channels * sample size
+ waveFormat.nChannels = 2; // stereo
+ waveFormat.nSamplesPerSec = OUTPUT_SAMPLE_RATE;
+ waveFormat.wBitsPerSample = 16;
+ waveFormat.wFormatTag = WAVE_FORMAT_PCM;
+
+ MMRESULT errorCode = waveOutOpen( &m_deviceHandle, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL );
+ if ( errorCode == MMSYSERR_NOERROR )
+ {
+ int bufferSize = 4 * ( OUTPUT_SAMPLE_RATE / OUTPUT_BUFFER_COUNT ); // total of 1 second of data
+
+ // Got one!
+ for ( int i = 0; i < OUTPUT_BUFFER_COUNT; i++ )
+ {
+ m_buffers[i].hdr = new WAVEHDR;
+ m_buffers[i].hdr->lpData = new char[ bufferSize ];
+ long align = (long)m_buffers[i].hdr->lpData;
+ if ( align & 3 )
+ {
+ m_buffers[i].hdr->lpData = (char *) ( (align+3) &~3 );
+ }
+ m_buffers[i].hdr->dwBufferLength = bufferSize - (align&3);
+ m_buffers[i].hdr->dwFlags = 0;
+
+ if ( waveOutPrepareHeader( m_deviceHandle, m_buffers[i].hdr, sizeof(*m_buffers[i].hdr) ) != MMSYSERR_NOERROR )
+ {
+ ClearDevice();
+ return;
+ }
+ }
+ }
+ else
+ {
+ ClearDevice();
+ }
+}
+
+// factory to create a suitable audio output for this system
+CAudioOutput *CAudioOutput::Create( void )
+{
+ // sound device is a singleton for now
+ static CAudioOutput *pWaveOut = NULL;
+
+ if ( !pWaveOut )
+ {
+ pWaveOut = new CAudioWaveOutput;
+ }
+
+ return pWaveOut;
+}
+
+struct CSoundFile
+{
+ char filename[ 512 ];
+ CAudioSource *source;
+ long filetime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CFacePoserSound : public IFacePoserSound
+{
+public:
+ ~CFacePoserSound( void );
+
+ void Init( void );
+ void Shutdown( void );
+ void Update( float dt );
+ void Flush( void );
+
+ CAudioSource *LoadSound( const char *wavfile );
+ void PlaySound( StudioModel *source, float volume, const char *wavfile, CAudioMixer **ppMixer );
+ void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer );
+ void PlayPartialSound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample );
+
+ bool IsSoundPlaying( CAudioMixer *pMixer );
+ CAudioMixer *FindMixer( CAudioSource *source );
+
+ void StopAll( void );
+ void StopSound( CAudioMixer *mixer );
+
+ void RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr, float starttime, float endtime,
+ CAudioSource *pWave, bool selected = false, int selectionstart = 0, int selectionend = 0 );
+
+ // void InstallPhonemecallback( IPhonemeTag *pTagInterface );
+ float GetAmountofTimeAhead( void );
+
+ int GetNumberofSamplesAhead( void );
+
+ CAudioOuput *GetAudioOutput( void );
+
+ virtual void EnsureNoModelReferences( CAudioSource *source );
+
+private:
+ void AddViseme( float intensity, StudioModel *model, int phoneme, float scale );
+ void ProcessCloseCaptionData( StudioModel *model, float curtime, CSentence* sentence );
+ void SetupWeights( void );
+
+ CAudioSource *FindOrAddSound( const char *filename );
+
+ CAudioOutput *m_pAudio;
+
+ float m_flElapsedTime;
+
+ CUtlVector < CSoundFile > m_ActiveSounds;
+};
+
+static CFacePoserSound g_FacePoserSound;
+
+IFacePoserSound *sound = ( IFacePoserSound * )&g_FacePoserSound;
+
+CFacePoserSound::~CFacePoserSound( void )
+{
+ OutputDebugString( va( "Removing %i sounds\n", m_ActiveSounds.Size() ) );
+ for ( int i = 0 ; i < m_ActiveSounds.Size(); i++ )
+ {
+ CSoundFile *p = &m_ActiveSounds[ i ];
+ OutputDebugString( va( "Removing sound: %s\n", p->filename ) );
+ delete p->source;
+ }
+
+ m_ActiveSounds.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CAudioOuput *CFacePoserSound::GetAudioOutput( void )
+{
+ return (CAudioOuput *)m_pAudio;
+}
+
+CAudioSource *CFacePoserSound::FindOrAddSound( const char *filename )
+{
+ CSoundFile *s;
+
+ int i;
+ for ( i = 0; i < m_ActiveSounds.Size(); i++ )
+ {
+ s = &m_ActiveSounds[ i ];
+ Assert( s );
+ if ( !stricmp( s->filename, filename ) )
+ {
+ long filetime = filesystem->GetFileTime( filename );
+ if ( filetime != s->filetime )
+ {
+ Con_Printf( "Reloading sound %s\n", filename );
+ delete s->source;
+ s->source = LoadSound( filename );
+ s->filetime = filetime;
+ }
+ return s->source;
+ }
+ }
+
+ i = m_ActiveSounds.AddToTail();
+ s = &m_ActiveSounds[ i ];
+ strcpy( s->filename, filename );
+ s->source = LoadSound( filename );
+ s->filetime = filesystem->GetFileTime( filename );
+
+ return s->source;
+}
+
+void CFacePoserSound::Init( void )
+{
+ m_flElapsedTime = 0.0f;
+ m_pAudio = CAudioOutput::Create();
+
+ // Load SoundOverrides for Faceposer
+
+ KeyValues *manifest = new KeyValues( "scripts/game_sounds_manifest.txt" );
+ if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDEMITTER, "scripts/game_sounds_manifest.txt", "GAME" ) )
+ {
+ for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
+ {
+ if ( !Q_stricmp( sub->GetName(), "faceposer_file" ) )
+ {
+ soundemitter->AddSoundOverrides( sub->GetString() );
+ continue;
+ }
+ }
+ }
+ manifest->deleteThis();
+}
+
+void CFacePoserSound::Shutdown( void )
+{
+}
+
+float CFacePoserSound::GetAmountofTimeAhead( void )
+{
+ if ( !m_pAudio )
+ return 0.0f;
+
+ return m_pAudio->GetAmountofTimeAhead();
+}
+
+int CFacePoserSound::GetNumberofSamplesAhead( void )
+{
+ if ( !m_pAudio )
+ return 0;
+
+ return m_pAudio->GetNumberofSamplesAhead();
+}
+
+
+CAudioSource *CFacePoserSound::LoadSound( const char *wavfile )
+{
+ if ( !m_pAudio )
+ return NULL;
+
+ CAudioSource *wave = AudioSource_Create( wavfile );
+ return wave;
+}
+
+void CFacePoserSound::PlaySound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer )
+{
+ if ( m_pAudio )
+ {
+ CAudioSource *wave = FindOrAddSound( wavfile );
+ if ( !wave )
+ return;
+
+ CAudioMixer *pMixer = wave->CreateMixer();
+ if ( ppMixer )
+ {
+ *ppMixer = pMixer;
+ }
+ pMixer->SetVolume( volume );
+ m_pAudio->AddSource( pMixer );
+ if ( model )
+ {
+ pMixer->SetModelIndex( models->GetIndexForStudioModel( model ) );
+ }
+ }
+}
+
+void CFacePoserSound::PlayPartialSound( StudioModel *model, float volume, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample )
+{
+ if ( !m_pAudio )
+ return;
+
+ StopAll();
+ CAudioSource *wave = FindOrAddSound( wavfile );
+ if ( !wave )
+ return;
+
+ CAudioMixer *mixer = wave->CreateMixer();
+ if ( ppMixer )
+ {
+ *ppMixer = mixer;
+ }
+
+ mixer->SetSamplePosition( startSample );
+ mixer->SetLoopPosition( endSample );
+ mixer->SetVolume( volume );
+ m_pAudio->AddSource( mixer );
+}
+
+void CFacePoserSound::PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer )
+{
+ if ( ppMixer )
+ {
+ *ppMixer = NULL;
+ }
+
+ if ( m_pAudio )
+ {
+ CAudioMixer *mixer = source->CreateMixer();
+ if ( ppMixer )
+ {
+ *ppMixer = mixer;
+ }
+ mixer->SetVolume( volume );
+ m_pAudio->AddSource( mixer );
+ }
+}
+
+enum
+{
+ PHONEME_CLASS_WEAK = 0,
+ PHONEME_CLASS_NORMAL,
+ PHONEME_CLASS_STRONG,
+
+ NUM_PHONEME_CLASSES
+};
+
+struct Emphasized_Phoneme
+{
+ char *classname;
+ bool required;
+ bool valid;
+ CExpClass *cl;
+ CExpression *exp;
+ float *settings;
+ float amount;
+};
+
+static Emphasized_Phoneme g_PhonemeClasses[ NUM_PHONEME_CLASSES ] =
+{
+ { "phonemes_weak", false },
+ { "phonemes", true },
+ { "phonemes_strong", false },
+};
+
+#define STRONG_CROSSFADE_START 0.60f
+#define WEAK_CROSSFADE_START 0.40f
+
+void ComputeBlendedSetting( Emphasized_Phoneme *classes, float emphasis_intensity )
+{
+ // Here's the formula
+ // 0.5 is neutral 100 % of the default setting
+
+ // Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END
+ // If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START
+ // so we don't get huge numbers
+
+ bool has_weak = classes[ PHONEME_CLASS_WEAK ].valid;
+ bool has_strong = classes[ PHONEME_CLASS_STRONG ].valid;
+
+ Assert( classes[ PHONEME_CLASS_NORMAL ].valid );
+
+ if ( emphasis_intensity > STRONG_CROSSFADE_START )
+ {
+ if ( has_strong )
+ {
+ // Blend in some of strong
+ float dist_remaining = 1.0f - emphasis_intensity;
+ float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START );
+
+ classes[ PHONEME_CLASS_NORMAL ].amount = (frac) * 2.0f * STRONG_CROSSFADE_START;
+ classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac;
+ }
+ else
+ {
+ emphasis_intensity = min( emphasis_intensity, STRONG_CROSSFADE_START );
+ classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
+ }
+ }
+ else if ( emphasis_intensity < WEAK_CROSSFADE_START )
+ {
+ if ( has_weak )
+ {
+ // Blend in some weak
+ float dist_remaining = WEAK_CROSSFADE_START - emphasis_intensity;
+ float frac = dist_remaining / ( WEAK_CROSSFADE_START );
+
+ classes[ PHONEME_CLASS_NORMAL ].amount = (1.0f - frac) * 2.0f * WEAK_CROSSFADE_START;
+ classes[ PHONEME_CLASS_WEAK ].amount = frac;
+ }
+ else
+ {
+ emphasis_intensity = max( emphasis_intensity, WEAK_CROSSFADE_START );
+ classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
+ }
+ }
+ else
+ {
+ classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
+ }
+}
+
+void CFacePoserSound::AddViseme( float intensity, StudioModel *model, int phoneme, float scale )
+{
+ int i;
+
+ Assert( model );
+ CStudioHdr *hdr = model->GetStudioHdr();
+ Assert( hdr );
+ if ( !hdr )
+ return;
+
+ for ( i = 0; i < NUM_PHONEME_CLASSES; i++ )
+ {
+ Emphasized_Phoneme *info = &g_PhonemeClasses[ i ];
+
+ info->valid = false;
+ info->exp = NULL;
+ info->settings = NULL;
+ info->amount = 0.0f;
+
+ info->cl = expressions->FindClass( info->classname, true );
+ if ( info->cl )
+ {
+ info->exp = info->cl->FindExpression( ConvertPhoneme( phoneme ) );
+ }
+
+ if ( info->required && ( !info->cl || !info->exp ) )
+ {
+ return;
+ }
+
+ if ( info->exp )
+ {
+ info->valid = true;
+
+ info->settings = info->exp->GetSettings();
+ Assert( info->settings );
+ }
+ }
+
+ ComputeBlendedSetting( g_PhonemeClasses, intensity );
+
+ // Look up the phoneme
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ int j = hdr->pFlexcontroller( i )->localToGlobal;
+
+ float add = 0.0f;
+
+ for ( int k = 0 ; k < NUM_PHONEME_CLASSES; k++ )
+ {
+ Emphasized_Phoneme *info = &g_PhonemeClasses[ k ];
+ if ( !info->valid || !info->amount )
+ continue;
+
+ add += info->amount * info->settings[ j ];
+ }
+
+ if ( add == 0.0f )
+ continue;
+
+ float curvalue = model->GetFlexController( i );
+ curvalue += add * scale;
+ model->SetFlexController( i, curvalue );
+ }
+}
+
+
+#define PHONEME_FILTER 0.08f
+#define PHONEME_DELAY 0.0f
+
+void CFacePoserSound::SetupWeights( void )
+{
+ StudioModel *model;
+ int c = models->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ model = models->GetStudioModel( i );
+ if ( !model )
+ continue;
+
+ // Reset flexes
+ CStudioHdr *hdr = model->GetStudioHdr();
+ if ( !hdr )
+ continue;
+
+ for ( int s = 0; s < model->m_mouth.GetNumVoiceSources(); s++ )
+ {
+ CVoiceData *vd = model->m_mouth.GetVoiceSource( s );
+ if ( !vd || vd->ShouldIgnorePhonemes() )
+ continue;
+
+ CAudioSource *source = vd->GetSource();
+ // check for phoneme flexes
+ if ( !source )
+ continue;
+
+ CAudioMixer *mixer = FindMixer( source );
+ if ( !mixer )
+ continue;
+
+ CSentence *sentence = source->GetSentence();
+ if ( !sentence )
+ continue;
+
+ // Zero faces if needed
+ models->CheckResetFlexes();
+
+ float pos = (float)mixer->GetScrubPosition();
+
+ // Con_Printf( "pos %f for mixer %p\n", pos, mixer );
+
+ float soundtime = pos / source->SampleRate();
+
+ float t = soundtime - PHONEME_DELAY;
+ float dt = PHONEME_FILTER;
+
+ float sentence_duration = source->GetRunningLength();
+ float emphasis_intensity = sentence->GetIntensity( t, sentence_duration );
+
+ if ( t > 0.0f )
+ {
+ for ( int w = 0 ; w < sentence->m_Words.Size(); w++ )
+ {
+ CWordTag *word = sentence->m_Words[ w ];
+ if ( !word )
+ continue;
+
+ for ( int k = 0; k < word->m_Phonemes.Size(); k++)
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ k ];
+ if ( !phoneme )
+ continue;
+
+ // if the filter starts within this phoneme, make sure the filter size is
+ // at least least as long as the current phoneme, or until the end of the next phoneme,
+ // whichever is smaller
+ if (t > phoneme->GetStartTime() && t < phoneme->GetEndTime())
+ {
+ CPhonemeTag *next = NULL;
+ // try next phoneme, or first phoneme of next word
+ if (k < word->m_Phonemes.Size()-1)
+ {
+ next = word->m_Phonemes[ k+1 ];
+ }
+ else if ( w < sentence->m_Words.Size() - 1 && sentence->m_Words[ w+1 ]->m_Phonemes.Size() )
+ {
+ next = sentence->m_Words[ w+1 ]->m_Phonemes[ 0 ];
+ }
+
+ // if I have a neighbor
+ if (next)
+ {
+ // and they're touching
+ if (next->GetStartTime() == phoneme->GetEndTime())
+ {
+ // no gap, so increase the blend length to the end of the next phoneme, as long as it's not longer than the current phoneme
+ dt = max( dt, min( next->GetEndTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) );
+ }
+ else
+ {
+ // dead space, so increase the blend length to the start of the next phoneme, as long as it's not longer than the current phoneme
+ dt = max( dt, min( next->GetStartTime() - t, phoneme->GetEndTime() - phoneme->GetStartTime() ) );
+ }
+ }
+ else
+ {
+ // last phoneme in list, increase the blend length to the length of the current phoneme
+ dt = max( dt, phoneme->GetEndTime() - phoneme->GetStartTime() );
+ }
+ }
+
+ float t1 = ( phoneme->GetStartTime() - t) / dt;
+ float t2 = ( phoneme->GetEndTime() - t) / dt;
+
+ if (t1 < 1.0 && t2 > 0)
+ {
+ float scale;
+
+ // clamp
+ if (t2 > 1)
+ t2 = 1;
+ if (t1 < 0)
+ t1 = 0;
+
+ // FIXME: simple box filter. Should use something fancier
+ scale = (t2 - t1);
+
+ AddViseme( emphasis_intensity, model, phoneme->GetPhonemeCode(), scale );
+ }
+ }
+ }
+ ProcessCloseCaptionData( model, t, sentence );
+ }
+ }
+ }
+}
+
+static int g_nSoundFrameCount = 0;
+
+void CFacePoserSound::ProcessCloseCaptionData( StudioModel *model, float curtime, CSentence* sentence )
+{
+// closecaptionmanager->Process( g_nSoundFrameCount, model, curtime, sentence, GetCloseCaptionLanguageId() );
+}
+
+void CFacePoserSound::Update( float dt )
+{
+// closecaptionmanager->PreProcess( g_nSoundFrameCount );
+
+ if ( m_pAudio )
+ {
+ SetupWeights();
+ m_pAudio->Update( m_flElapsedTime );
+ }
+
+// closecaptionmanager->PostProcess( g_nSoundFrameCount, dt );
+
+ m_flElapsedTime += dt;
+ g_nSoundFrameCount++;
+}
+
+void CFacePoserSound::Flush( void )
+{
+ if ( m_pAudio )
+ {
+ m_pAudio->Flush();
+ }
+}
+
+void CFacePoserSound::StopAll( void )
+{
+ int c = models->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ StudioModel *model = models->GetStudioModel( i );
+ if ( model )
+ {
+ model->m_mouth.ClearVoiceSources();
+ }
+ }
+
+ if ( m_pAudio )
+ {
+ m_pAudio->StopSounds();
+ }
+}
+
+void CFacePoserSound::StopSound( CAudioMixer *mixer )
+{
+ int idx = m_pAudio->FindSourceIndex( mixer );
+ if ( idx != -1 )
+ {
+ m_pAudio->FreeChannel( idx );
+ }
+}
+
+void CFacePoserSound::RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr,
+ float starttime, float endtime, CAudioSource *pWave,
+ bool selected /*= false*/, int selectionstart /*= 0*/, int selectionend /*= 0*/ )
+{
+ channel_t channel;
+
+ channel.leftvol = 127;
+ channel.rightvol = 127;
+ channel.pitch = 1.0;
+
+ if ( !pWave )
+ return;
+
+ CAudioWaveOutput *pWaveOutput = ( CAudioWaveOutput * )m_pAudio;
+
+ CAudioMixer *pMixer = pWave->CreateMixer();
+
+ float timeperpixel = ( endtime - starttime ) / (float)( outrect.right - outrect.left );
+
+ float samplesperpixel = timeperpixel * pWave->SampleRate();
+
+ samplesperpixel = min( samplesperpixel, (float)PAINTBUFFER_SIZE );
+
+ int intsamplesperpixel = (int)samplesperpixel;
+
+ // Determine start/stop positions
+ int totalsamples = (int)( pWave->GetRunningLength() * pWave->SampleRate() );
+
+ if ( totalsamples <= 0 )
+ return;
+
+ float selectionstarttime = pWave->GetRunningLength() * ( float )selectionstart / ( float )totalsamples;
+ float selectionendtime = pWave->GetRunningLength() * ( float )selectionend / ( float )totalsamples;
+
+
+ HPEN oldPen, pen, pen2, pen3, pen4;
+
+ pen = CreatePen( PS_SOLID, 1, RGB( 175, 175, 250 ) );
+ pen2 = CreatePen( PS_SOLID, 1, clr );
+ pen3 = CreatePen( PS_SOLID, 1, RGB( 127, 200, 249 ) );
+ pen4 = CreatePen( PS_SOLID, 2, RGB( 0, 0, 200 ) );
+
+ oldPen = (HPEN)SelectObject( dc, pen );
+
+ MoveToEx( dc, outrect.left, ( outrect.bottom + outrect.top ) / 2, NULL );
+ LineTo( dc, outrect.right, ( outrect.bottom + outrect.top ) / 2 );
+
+ SelectObject( dc, pen2 );
+
+ // Now iterate the samples
+ float currenttime = 0.0f;
+ int pixel = 0;
+ int height = ( outrect.bottom - outrect.top ) / 2;
+ int midy = ( outrect.bottom + outrect.top ) / 2;
+ int bufferlen = ( intsamplesperpixel + 3 ) & ~3;
+ short *samples = new short[ 2 * bufferlen ];
+ bool drawingselection = false;
+ int maxsamples = max( 32, intsamplesperpixel / 16 );
+ int currentsample = 0;
+
+ while ( currenttime < endtime )
+ {
+
+ pWaveOutput->m_audioDevice.MixBegin();
+
+ int samplecount = min( maxsamples, intsamplesperpixel );
+
+ if ( !pMixer->MixDataToDevice( &pWaveOutput->m_audioDevice, &channel, currentsample, samplecount, pWave->SampleRate(), true ) )
+ break;
+
+ currentsample = pMixer->GetSamplePosition();
+
+ // Jump ahead by diff
+ int diff = intsamplesperpixel - samplecount;
+ if ( diff > 0 )
+ {
+ if ( !pMixer->SkipSamples( &channel, currentsample, diff, pWave->SampleRate(), true ) )
+ break;
+ }
+
+ currentsample = pMixer->GetSamplePosition();
+
+ pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, samplecount );
+
+ if ( currenttime >= starttime )
+ {
+ if ( selected )
+ {
+ bool boundary = false;
+ bool inselection = ( currenttime >= selectionstarttime &&
+ currenttime <= selectionendtime );
+
+ if ( inselection )
+ {
+ if ( !drawingselection )
+ {
+ drawingselection = true;
+ boundary = true;
+ }
+ }
+ else if ( drawingselection )
+ {
+ boundary = true;
+ drawingselection = false;
+ }
+
+ if ( inselection || boundary )
+ {
+ int top, bottom;
+
+ bottom = outrect.bottom;
+
+ HPEN *usePen;
+ if ( boundary )
+ {
+ usePen = &pen4;
+ top = outrect.top;
+ }
+ else
+ {
+ usePen = &pen3;
+ top = outrect.bottom - 19;
+ }
+
+ HPEN old = (HPEN)SelectObject( dc, *usePen );
+
+ MoveToEx( dc, outrect.left + pixel, top, NULL );
+ LineTo( dc, outrect.left + pixel, bottom-1 );
+
+ SelectObject( dc, old );
+ }
+ }
+
+
+ int maxvalue = -65536;
+ int minvalue = 65536;
+
+ short *pData = samples;
+
+ // only take fix samples
+ int step = 2;
+ int count = 2 * samplecount;
+
+ for ( int i = 0; i < count; i+=step )
+ {
+ int val = (int)( pData[i] + pData[i+1] ) / 2;
+
+ if ( val > maxvalue )
+ {
+ maxvalue = val;
+ }
+
+ if ( val < minvalue )
+ {
+ minvalue = val;
+ }
+ }
+
+ float maxv = (float)( maxvalue ) / 32768.0f;
+ float minv = (float)( minvalue ) / 32768.0f;
+
+ MoveToEx( dc, outrect.left + pixel, midy + ( int ) ( maxv * height ), NULL );
+ LineTo( dc, outrect.left + pixel, midy + ( int ) ( minv * height ) );
+
+ pixel++;
+ }
+ currenttime += timeperpixel;
+ }
+
+ delete[] samples;
+
+ SelectObject( dc, oldPen );
+ DeleteObject( pen );
+ DeleteObject( pen2 );
+ DeleteObject( pen3 );
+
+ delete pMixer;
+}
+
+bool CFacePoserSound::IsSoundPlaying( CAudioMixer *pMixer )
+{
+ if ( !m_pAudio || !pMixer )
+ {
+ return false;
+ }
+
+ //
+ int index = m_pAudio->FindSourceIndex( pMixer );
+ if ( index != -1 )
+ return true;
+
+ return false;
+}
+
+CAudioMixer *CFacePoserSound::FindMixer( CAudioSource *source )
+{
+ if ( !m_pAudio )
+ return NULL;
+
+ return m_pAudio->GetMixerForSource( source );
+}
+
+
+void CFacePoserSound::EnsureNoModelReferences( CAudioSource *source )
+{
+ int c = models->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ StudioModel *model = models->GetStudioModel( i );
+ if ( model->m_mouth.IsSourceReferenced( source ) )
+ {
+ model->m_mouth.RemoveSource( source );
+ }
+ }
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/sound.h b/utils/hlfaceposer/sound.h
new file mode 100644
index 0000000..0199701
--- /dev/null
+++ b/utils/hlfaceposer/sound.h
@@ -0,0 +1,97 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#ifndef SOUND_H
+#define SOUND_H
+#pragma once
+
+class CAudioMixer;
+
+class CAudioInput
+{
+public:
+ // factory to create a suitable audio input for this system
+ static CAudioInput *Create( void );
+
+ // base class needs virtual destructor
+ virtual ~CAudioInput( void ) {}
+
+ // ------------------- interface ------------------------
+
+ // Returns the current count of available samples
+ virtual int SampleCount( void ) = 0;
+
+ // returns the size of each sample in bytes
+ virtual int SampleSize( void ) = 0;
+
+ // returns the sampling rate of the data
+ virtual int SampleRate( void ) = 0;
+
+ // returns a pointer to the available data
+ virtual void *SampleData( void ) = 0;
+
+ // release the available data (mark as done)
+ virtual void SampleRelease( void ) = 0;
+
+ // returns the mono/stereo status of this device (true if stereo)
+ virtual bool IsStereo( void ) = 0;
+
+ // begin sampling
+ virtual void Start( void ) = 0;
+
+ // stop sampling
+ virtual void Stop( void ) = 0;
+};
+
+class CAudioSource;
+
+class CAudioOutput
+{
+public:
+ // factory to create a suitable audio output for this system
+ static CAudioOutput *Create( void );
+
+ // base class needs virtual destructor
+ virtual ~CAudioOutput( void ) {}
+
+ // ------------------- interface ------------------------
+
+ // returns the size of each sample in bytes
+ virtual int SampleSize( void ) = 0;
+
+ // returns the sampling rate of the data
+ virtual int SampleRate( void ) = 0;
+
+ // returns the mono/stereo status of this device (true if stereo)
+ virtual bool IsStereo( void ) = 0;
+
+ // move up to time (time is absolute)
+ virtual void Update( float dt ) = 0;
+
+ virtual void Flush( void ) = 0;
+
+ // Hook up a filter to the input channel
+ virtual void AddSource( CAudioMixer *pSource ) = 0;
+
+ virtual void StopSounds( void ) = 0;
+
+ virtual void FreeChannel( int channel ) = 0;
+
+ virtual int FindSourceIndex( CAudioMixer *pSource ) = 0;
+
+ virtual float GetAmountofTimeAhead( void ) = 0;
+
+ virtual int GetNumberofSamplesAhead( void ) = 0;
+
+ virtual CAudioMixer *GetMixerForSource( CAudioSource *pDevice ) = 0;
+};
+
+
+int AudioResample( void *pInput, int inCount, int inSize, bool inStereo, int inRate,
+ void *pOutput, int outCount, int outSize, bool outStereo, int outRate );
+
+#endif // SOUND_H
diff --git a/utils/hlfaceposer/soundlookup.cpp b/utils/hlfaceposer/soundlookup.cpp
new file mode 100644
index 0000000..128e340
--- /dev/null
+++ b/utils/hlfaceposer/soundlookup.cpp
@@ -0,0 +1,140 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "mxtk/mx.h"
+#include "resource.h"
+#include "SoundLookup.h"
+#include "mdlviewer.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "addsoundentry.h"
+
+static CSoundLookupParams g_Params;
+
+static void PopulateSoundEntryList( HWND wnd, CSoundLookupParams *params )
+{
+ HWND control = GetDlgItem( wnd, IDC_SOUNDENTRYLIST );
+ if ( !control )
+ return;
+
+ SendMessage( control, LB_RESETCONTENT, 0, 0 );
+ SendMessage( control, WM_SETFONT, (WPARAM) (HFONT) GetStockObject (ANSI_FIXED_FONT), MAKELPARAM (TRUE, 0) );
+
+ int c = params->entryList->Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ int soundindex = (*params->entryList)[ i ];
+ char text[ 128 ];
+ text[ 0 ] = 0;
+ Q_strncpy( text, soundemitter->GetSoundName( soundindex ), sizeof( text ) );
+ if ( text[0] )
+ {
+ char const *script = soundemitter->GetSourceFileForSound( soundindex );
+
+ int idx = SendMessage( control, LB_ADDSTRING, 0, (LPARAM)va( "%20s: '%s'", script, text ) );
+ SendMessage( control, LB_SETITEMDATA, idx, (LPARAM)soundindex );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hwndDlg -
+// uMsg -
+// wParam -
+// lParam -
+// Output : static BOOL CALLBACK
+//-----------------------------------------------------------------------------
+static BOOL CALLBACK SoundLookupDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ // Insert code here to put the string (to find and replace with)
+ // into the edit controls.
+ // ...
+ {
+ g_Params.PositionSelf( hwndDlg );
+
+ PopulateSoundEntryList( hwndDlg, &g_Params );
+
+ SetDlgItemText( hwndDlg, IDC_STATIC_PROMPT, g_Params.m_szPrompt );
+
+ SetWindowText( hwndDlg, g_Params.m_szDialogTitle );
+
+ SetFocus( GetDlgItem( hwndDlg, IDC_SOUNDENTRYLIST ) );
+ }
+ return FALSE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ int selindex = SendMessage( GetDlgItem( hwndDlg, IDC_SOUNDENTRYLIST ), LB_GETCURSEL, 0, 0 );
+ if ( selindex == LB_ERR )
+ {
+ mxMessageBox( NULL, "You must select an entry from the list", g_appTitle, MB_OK );
+ return TRUE;
+ }
+
+ int soundindex = SendMessage( GetDlgItem( hwndDlg, IDC_SOUNDENTRYLIST ), LB_GETITEMDATA, selindex, 0 );
+
+ Assert( soundindex != LB_ERR );
+
+ Q_strncpy( g_Params.m_szSoundName, soundemitter->GetSoundName( soundindex ), sizeof ( g_Params.m_szSoundName ) );
+ EndDialog( hwndDlg, 1 );
+ }
+ break;
+ case IDCANCEL:
+ EndDialog( hwndDlg, 0 );
+ break;
+ case IDC_ADDENTRY:
+ {
+ // Create a new sound entry for this sound
+ CAddSoundParams params;
+ Q_memset( &params, 0, sizeof( params ) );
+ Q_strcpy( params.m_szDialogTitle, "Add Sound Entry" );
+ Q_strcpy( params.m_szWaveFile, g_Params.m_szWaveFile );
+
+ if ( AddSound( &params, hwndDlg ) )
+ {
+ // Add it to soundemitter and check out script files
+ if ( params.m_szSoundName[ 0 ] &&
+ params.m_szScriptName[ 0 ] )
+ {
+ Q_strcpy( g_Params.m_szSoundName, params.m_szSoundName );
+ // Press the OK button for the user...
+ EndDialog( hwndDlg, 1 );
+ }
+ }
+ }
+ break;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *view -
+// *actor -
+// Output : int
+//-----------------------------------------------------------------------------
+int SoundLookup( CSoundLookupParams *params, HWND parent )
+{
+ g_Params = *params;
+
+ int retval = DialogBox( (HINSTANCE)GetModuleHandle( 0 ),
+ MAKEINTRESOURCE( IDD_WAVELOOKUP ),
+ parent,
+ (DLGPROC)SoundLookupDialogProc );
+
+ *params = g_Params;
+
+ return retval;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/soundlookup.h b/utils/hlfaceposer/soundlookup.h
new file mode 100644
index 0000000..d6a2de2
--- /dev/null
+++ b/utils/hlfaceposer/soundlookup.h
@@ -0,0 +1,33 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef SOUNDLOOKUP_H
+#define SOUNDLOOKUP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basedialogparams.h"
+#include "utlvector.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CSoundLookupParams : public CBaseDialogParams
+{
+ char m_szWaveFile[ 256 ];
+
+ char m_szPrompt[ 256 ];
+
+ CUtlVector< int > *entryList;
+
+ char m_szSoundName[ 256 ];
+};
+
+// Display/create dialog
+int SoundLookup( CSoundLookupParams *params, HWND parent );
+
+#endif // SOUNDLOOKUP_H
diff --git a/utils/hlfaceposer/tabwindow.cpp b/utils/hlfaceposer/tabwindow.cpp
new file mode 100644
index 0000000..cdd07ba
--- /dev/null
+++ b/utils/hlfaceposer/tabwindow.cpp
@@ -0,0 +1,480 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "tabwindow.h"
+#include "ChoreoWidgetDrawHelper.h"
+#include "hlfaceposer.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : *parent -
+// x -
+// y -
+// w -
+// h -
+// id -
+// style -
+//-----------------------------------------------------------------------------
+CTabWindow::CTabWindow( mxWindow *parent, int x, int y, int w, int h, int id /*= 0*/, int style /*=0*/ )
+: mxWindow( parent, x, y, w, h, "", style )
+{
+ setId( id );
+
+ m_nSelected = -1;
+
+ m_nRowHeight = 20;
+ m_nRowsRequired = 1;
+
+ m_nTabWidth = 80;
+ m_nPixelDelta = 3;
+ m_bInverted = false;
+ m_bRightJustify = false;
+ SetColor( COLOR_BG, GetSysColor( COLOR_BTNFACE ) );
+ SetColor( COLOR_FG, GetSysColor( COLOR_INACTIVECAPTION ) );
+ SetColor( COLOR_FG_SELECTED, GetSysColor( COLOR_ACTIVECAPTION ) );
+ SetColor( COLOR_HILITE, GetSysColor( COLOR_3DSHADOW ) );
+ SetColor( COLOR_HILITE_SELECTED, GetSysColor( COLOR_3DHILIGHT ) );
+ SetColor( COLOR_TEXT, GetSysColor( COLOR_CAPTIONTEXT ) );
+ SetColor( COLOR_TEXT_SELECTED, GetSysColor( COLOR_INACTIVECAPTIONTEXT ) );
+
+ FacePoser_AddWindowStyle( this, WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CTabWindow::~CTabWindow
+//-----------------------------------------------------------------------------
+CTabWindow::~CTabWindow ( void )
+{
+ removeAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : index -
+// clr -
+//-----------------------------------------------------------------------------
+void CTabWindow::SetColor( int index, COLORREF clr )
+{
+ if ( index < 0 || index >= NUM_COLORS )
+ return;
+
+ m_Colors[ index ] = clr;
+}
+
+void CTabWindow::SetInverted( bool invert )
+{
+ m_bInverted = invert;
+ RecomputeLayout( w2() );
+}
+
+void CTabWindow::SetRightJustify( bool rightjustify )
+{
+ m_bRightJustify = true;
+ RecomputeLayout( w2() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tabs are sized to string content
+// Input : rcClient -
+// tabRect -
+// tabNum -
+//-----------------------------------------------------------------------------
+void CTabWindow::GetTabRect( const RECT& rcClient, RECT& tabRect, int tabNum )
+{
+ tabRect = m_Items[ tabNum ].rect;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+// rcClient -
+// tabnum -
+// selected -
+//-----------------------------------------------------------------------------
+void CTabWindow::DrawTab( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient, int tabnum, bool selected )
+{
+ RECT rcTab;
+
+ if ( tabnum < 0 || tabnum >= m_Items.Size() )
+ return;
+
+ GetTabRect( rcClient, rcTab, tabnum );
+
+ COLORREF fgcolor = m_Colors[ selected ? COLOR_FG_SELECTED : COLOR_FG ];
+ COLORREF hilightcolor = m_Colors[ selected ? COLOR_HILITE_SELECTED : COLOR_HILITE ];
+ COLORREF text = m_Colors[ selected ? COLOR_TEXT_SELECTED : COLOR_TEXT ];
+
+ // Create a trapezoid/paralleogram
+ POINT region[4];
+ int cPoints = 4;
+
+ OffsetRect( &rcTab, 0, m_bInverted ? 1 : -1 );
+
+ if ( m_bInverted )
+ {
+ region[ 0 ].x = rcTab.left - m_nPixelDelta;
+ region[ 0 ].y = rcTab.top;
+
+ region[ 1 ].x = rcTab.right + m_nPixelDelta;
+ region[ 1 ].y = rcTab.top;
+
+ region[ 2 ].x = rcTab.right - m_nPixelDelta;
+ region[ 2 ].y = rcTab.bottom;
+
+ region[ 3 ].x = rcTab.left + m_nPixelDelta;
+ region[ 3 ].y = rcTab.bottom;
+ }
+ else
+ {
+ region[ 0 ].x = rcTab.left + m_nPixelDelta;
+ region[ 0 ].y = rcTab.top;
+
+ region[ 1 ].x = rcTab.right - m_nPixelDelta;
+ region[ 1 ].y = rcTab.top;
+
+ region[ 2 ].x = rcTab.right + m_nPixelDelta;
+ region[ 2 ].y = rcTab.bottom;
+
+ region[ 3 ].x = rcTab.left - m_nPixelDelta;
+ region[ 3 ].y = rcTab.bottom;
+ }
+
+ HDC dc = drawHelper.GrabDC();
+
+ HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
+
+ int oldPF = SetPolyFillMode( dc, ALTERNATE );
+
+ HBRUSH brBg = CreateSolidBrush( fgcolor );
+ HBRUSH brBorder = CreateSolidBrush( hilightcolor );
+ //HBRUSH brInset = CreateSolidBrush( fgcolor );
+
+ FillRgn( dc, rgn, brBg );
+ FrameRgn( dc, rgn, brBorder, 1, 1 );
+
+ SetPolyFillMode( dc, oldPF );
+
+ DeleteObject( rgn );
+
+ DeleteObject( brBg );
+ DeleteObject( brBorder );
+ //DeleteObject( brInset );
+
+ // Position label
+ InflateRect( &rcTab, -5, 0 );
+ OffsetRect( &rcTab, 2, 0 );
+
+ // Draw label
+ drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, text, rcTab, "%s%s", getPrefix( tabnum ), getLabel( tabnum ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTabWindow::redraw( void )
+{
+ CChoreoWidgetDrawHelper drawHelper( this, m_Colors[ COLOR_BG ] );
+
+ int liney = m_bInverted ? 1 : h2() - 2;
+
+ drawHelper.DrawColoredLine( m_Colors[ COLOR_HILITE ], PS_SOLID, 1, 0, liney, w(), liney );
+ RECT rc;
+ drawHelper.GetClientRect( rc );
+
+ // Draw non-selected first
+ for ( int i = 0 ; i < m_Items.Size(); i++ )
+ {
+ if ( i == m_nSelected )
+ continue;
+
+ DrawTab( drawHelper, rc, i );
+ }
+
+ // Draw selected last, so that it appears to pop to top of z order
+ if ( m_nSelected >= 0 && m_nSelected < m_Items.Size() )
+ {
+ DrawTab( drawHelper, rc, m_nSelected, true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// my -
+// Output : int
+//-----------------------------------------------------------------------------
+int CTabWindow::GetItemUnderMouse( int mx, int my )
+{
+ RECT rcClient;
+ GetClientRect( (HWND)getHandle(), &rcClient );
+
+ for ( int i = 0; i < m_Items.Size() ; i++ )
+ {
+ RECT rcTab;
+ GetTabRect( rcClient, rcTab, i );
+
+ if ( mx < rcTab.left ||
+ mx > rcTab.right ||
+ my < rcTab.top ||
+ my > rcTab.bottom )
+ {
+ continue;
+ }
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int CTabWindow::handleEvent
+//-----------------------------------------------------------------------------
+int CTabWindow::handleEvent (mxEvent *event)
+{
+ int iret = 0;
+
+ switch ( event->event )
+ {
+ case mxEvent::MouseDown:
+ {
+ int item = GetItemUnderMouse( (short)event->x, (short)event->y );
+ if ( item != -1 )
+ {
+ m_nSelected = item;
+ redraw();
+
+ // Send CBN_SELCHANGE WM_COMMAND message to parent
+ HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
+ if ( parent )
+ {
+ LPARAM lp;
+ WPARAM wp;
+
+ wp = MAKEWPARAM( getId(), CBN_SELCHANGE );
+ lp = (long)getHandle();
+
+ PostMessage( parent, WM_COMMAND, wp, lp );
+ }
+ iret = 1;
+ }
+
+ if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ ShowRightClickMenu( (short)event->x, (short)event->y );
+ iret = 1;
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ RecomputeLayout( w2() );
+ }
+ break;
+ }
+ return iret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add string to table
+// Input : *item -
+//-----------------------------------------------------------------------------
+void CTabWindow::add( const char *item )
+{
+ m_Items.AddToTail();
+ CETItem *p = &m_Items[ m_Items.Size() - 1 ];
+ Assert( p );
+
+ Q_memset( &p->rect, 0, sizeof( p->rect) );
+
+ strcpy( p->m_szString, item );
+ p->m_szPrefix[ 0 ] = 0;
+ m_nSelected = min( m_nSelected, m_Items.Size() - 1 );
+ m_nSelected = max( m_nSelected, 0 );
+
+ RecomputeLayout( w2() );
+
+ redraw();
+}
+
+void CTabWindow::setPrefix( int item, char const *prefix )
+{
+ if ( item < 0 || item >= m_Items.Size() )
+ return;
+
+ Q_strncpy( m_Items[ item ].m_szPrefix, prefix, sizeof( m_Items[ item ].m_szPrefix ) );
+// RecomputeLayout( w2() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Change selected item
+// Input : index -
+//-----------------------------------------------------------------------------
+void CTabWindow::select( int index )
+{
+ if ( index < 0 || index >= m_Items.Size() )
+ return;
+
+ m_nSelected = index;
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove a string
+// Input : index -
+//-----------------------------------------------------------------------------
+void CTabWindow::remove( int index )
+{
+ if ( index < 0 || index >= m_Items.Size() )
+ return;
+
+ m_Items.Remove( index );
+
+ m_nSelected = min( m_nSelected, m_Items.Size() - 1 );
+ m_nSelected = max( m_nSelected, 0 );
+
+ RecomputeLayout( w2() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clear out everything
+//-----------------------------------------------------------------------------
+void CTabWindow::removeAll()
+{
+ m_nSelected = -1;
+ m_Items.RemoveAll();
+
+ RecomputeLayout( w2() );
+
+ redraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CTabWindow::getItemCount () const
+{
+ return m_Items.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CTabWindow::getSelectedIndex () const
+{
+ // Convert based on override index
+ return m_nSelected;
+}
+
+char const *CTabWindow::getLabel( int item )
+{
+ if ( item < 0 || item >= m_Items.Count() )
+ return "";
+
+ return m_Items[ item ].m_szString;
+}
+
+char const *CTabWindow::getPrefix( int item )
+{
+ if ( item < 0 || item >= m_Items.Count() )
+ return "";
+
+ return m_Items[ item ].m_szPrefix;
+}
+
+void CTabWindow::SetRowHeight( int rowheight )
+{
+ m_nRowHeight = rowheight;
+ RecomputeLayout( w2() );
+ redraw();
+}
+
+int CTabWindow::GetBestHeight( int width )
+{
+ return RecomputeLayout( width, false ) * m_nRowHeight;
+}
+
+
+int CTabWindow::RecomputeLayout( int windowWidth, bool dolayout /*=true*/ )
+{
+ // Draw non-selected first
+ int curedge = m_nPixelDelta + 1;
+ int curtop = 0;
+
+ if ( m_bRightJustify )
+ {
+ curedge = windowWidth - ( m_nPixelDelta + 1 ) - 5;
+ }
+
+ int startedge = curedge;
+
+ int currentrow = 0;
+
+ for ( int i = 0 ; i < m_Items.Size(); i++ )
+ {
+ CETItem *p = &m_Items[ i ];
+
+ RECT rc;
+
+ int textwidth = CChoreoWidgetDrawHelper::CalcTextWidth( "Arial", 9, FW_NORMAL, "%s%s", p->m_szPrefix, p->m_szString ) + 15;
+
+ if ( !m_bRightJustify )
+ {
+ // Starting column
+ if ( curedge + textwidth > windowWidth )
+ {
+ curedge = startedge;
+ curtop += m_nRowHeight;
+ currentrow++;
+ }
+
+ rc.left = curedge;
+ rc.right = curedge + textwidth;
+ rc.top = curtop + 2;
+ rc.bottom = curtop + m_nRowHeight;
+
+ curedge += textwidth;
+
+ p->rect = rc;
+ }
+ else
+ {
+ // Starting column
+ if ( curedge - textwidth < 0 )
+ {
+ curedge = startedge;
+ curtop += m_nRowHeight;
+ currentrow++;
+ }
+
+ rc.left = curedge - textwidth;
+ rc.right = curedge;
+ rc.top = curtop;
+ rc.bottom = curtop + m_nRowHeight - 2;
+
+ curedge -= textwidth;
+ }
+
+ if ( dolayout )
+ {
+ p->rect = rc;
+ }
+ }
+
+ if ( dolayout )
+ {
+ m_nRowsRequired = currentrow + 1;
+ }
+
+ return currentrow + 1;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/tabwindow.h b/utils/hlfaceposer/tabwindow.h
new file mode 100644
index 0000000..28a0447
--- /dev/null
+++ b/utils/hlfaceposer/tabwindow.h
@@ -0,0 +1,102 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TABWINDOW_H
+#define TABWINDOW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "utlvector.h"
+
+class CChoreoWidgetDrawHelper;
+
+//-----------------------------------------------------------------------------
+// Purpose: A custom tab control for handling expression class strings
+//-----------------------------------------------------------------------------
+class CTabWindow : public mxWindow
+{
+public:
+ enum
+ {
+ COLOR_BG = 0,
+ COLOR_FG,
+ COLOR_FG_SELECTED,
+ COLOR_HILITE,
+ COLOR_HILITE_SELECTED,
+ COLOR_TEXT,
+ COLOR_TEXT_SELECTED,
+
+ NUM_COLORS
+ };
+
+ CTabWindow( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 );
+ virtual ~CTabWindow ( void );
+
+ virtual void redraw( void );
+ virtual int handleEvent (mxEvent *event);
+
+ // MANIPULATORS
+ virtual void add (const char *item);
+ virtual void select (int index);
+ virtual void remove (int index);
+ virtual void removeAll ();
+ virtual void setPrefix( int item, char const *prefix );
+
+ // ACCESSORS
+ virtual int getItemCount () const;
+ virtual int getSelectedIndex () const;
+
+ virtual char const *getLabel( int item );
+ virtual char const *getPrefix( int item );
+ virtual void ShowRightClickMenu( int mx, int my ) = 0;
+
+ void SetColor( int index, COLORREF clr );
+
+ void SetInverted( bool invert );
+ void SetRightJustify( bool rightjustify );
+
+ int GetBestHeight( int width );
+ void SetRowHeight( int rowheight );
+
+protected:
+ void GetTabRect( const RECT& rcClient, RECT& tabRect, int tabNum );
+ virtual void DrawTab( CChoreoWidgetDrawHelper& drawHelper, RECT& rcClient, int tabnum, bool selected = false );
+
+ int RecomputeLayout( int windowWidth, bool dolayout = true );
+
+ class CETItem
+ {
+ public:
+ enum
+ {
+ MAX_ET_STRING_LENGTH = 64
+ };
+
+ char m_szString[ MAX_ET_STRING_LENGTH ];
+ char m_szPrefix[ MAX_ET_STRING_LENGTH ];
+ RECT rect;
+ };
+
+ int GetItemUnderMouse( int mx, int my );
+
+ CUtlVector <CETItem> m_Items;
+ int m_nRowsRequired;
+
+ int m_nSelected;
+
+ int m_nTabWidth;
+ int m_nPixelDelta;
+ bool m_bInverted;
+ bool m_bRightJustify;
+
+ COLORREF m_Colors[ NUM_COLORS ];
+
+ int m_nRowHeight;
+};
+#endif // TABWINDOW_H
diff --git a/utils/hlfaceposer/timelineitem.cpp b/utils/hlfaceposer/timelineitem.cpp
new file mode 100644
index 0000000..50a4b86
--- /dev/null
+++ b/utils/hlfaceposer/timelineitem.cpp
@@ -0,0 +1,1763 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "hlfaceposer.h"
+#include <stdio.h>
+#include "TimelineItem.h"
+#include "choreowidgetdrawhelper.h"
+#include "mathlib/mathlib.h"
+#include "expressions.h"
+#include "StudioModel.h"
+#include "expclass.h"
+#include "mathlib/mathlib.h"
+#include "ExpressionTool.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "ChoreoView.h"
+#include "ControlPanel.h"
+#include "faceposer_models.h"
+#include "MatSysWin.h"
+#include "choreoviewcolors.h"
+#include "ifaceposersound.h"
+#include "curveeditorhelpers.h"
+
+extern double realtime;
+
+#define DOUBLE_CLICK_TIME 0.2
+
+#define GROW_HANDLE_WIDTH 66
+#define GROW_HANDLE_HEIGHT 8
+#define GROW_HANDLE_INSETPIXELS 8
+
+#define TIMELINEITEM_DEFAULT_HEIGHT 100
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+TimelineItem::TimelineItem( mxWindow *workspace )
+{
+ m_pHelper = new CCurveEditorHelper< TimelineItem >( this );
+
+ m_pWorkspace = workspace;
+
+ m_nDragging = DRAGTYPE_NONE;
+ m_nLastX = 0;
+ m_nLastY = 0;
+ m_nStartX = 0;
+ m_nStartY = 0;
+
+ SetExpressionInfo( NULL, 0 );
+
+ m_nNumSelected = 0;
+
+ m_nEditType = 0;
+
+ SetCollapsed( false );
+ SetActive( false );
+
+ m_nUndoSetup = 0;
+ m_rcBounds.top = 0;
+ m_rcBounds.bottom = 0;
+ m_rcBounds.left = 0;
+ m_rcBounds.right = 0;
+ m_bVisible = false;
+ m_flLastClickTime = -1;
+
+ m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
+}
+
+TimelineItem::~TimelineItem( void )
+{
+ delete m_pHelper;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::ResetHeight()
+{
+ m_nCurrentHeight = TIMELINEITEM_DEFAULT_HEIGHT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mx -
+// Output : float
+//-----------------------------------------------------------------------------
+float TimelineItem::GetTimeForMouse( int mx, bool clip /*= false*/ )
+{
+ float start, end;
+ g_pExpressionTool->GetStartAndEndTime( start, end );
+
+ if ( clip )
+ {
+ if ( mx < m_rcBounds.left )
+ {
+ return start;
+ }
+ else if ( mx >= m_rcBounds.right )
+ {
+ return end;
+ }
+ }
+
+ float frac = (float)( mx - m_rcBounds.left ) / (float)( m_rcBounds.right - m_rcBounds.left );
+ float t = start + frac * ( end - start );
+ return t;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : t -
+// Output : int
+//-----------------------------------------------------------------------------
+int TimelineItem::GetMouseForTime( float t, bool *clipped /*= NULL*/ )
+{
+ float start, end;
+ g_pExpressionTool->GetStartAndEndTime( start, end );
+
+ float frac = ( t - start ) / ( end - start );
+
+ if ( frac < 0.0 || frac > 1.0 )
+ {
+ if ( clipped )
+ {
+ *clipped = true;
+ }
+ }
+
+ int mx = m_rcBounds.left + ( int ) ( frac * (float)( m_rcBounds.right - m_rcBounds.left ) );
+
+ return mx;
+}
+
+int TimelineItem::NumSamples()
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return 0;
+
+ // Aggregate both types of tracks together
+ return track->GetNumSamples( 0 ) + track->GetNumSamples( 1 );
+ // return track->GetNumSamples( m_nEditType );
+}
+
+CExpressionSample *TimelineItem::GetSample( int idx )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return NULL;
+
+ if ( idx >= track->GetNumSamples( 0 ) )
+ {
+ // Rebase and look at left/right track instead
+ idx -= track->GetNumSamples( 0 );
+ return track->GetSample( idx, 1 );
+ }
+ return track->GetSample( idx, 0 );
+}
+
+CExpressionSample *TimelineItem::GetSampleUnderMouse( int mx, int my, float tolerance /*= FP_TL_SELECTION_TOLERANCE*/ )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return NULL;
+
+ CChoreoEvent *e = track->GetEvent();
+ if ( !e )
+ return NULL;
+
+ float closest_dist = 9999999.f;
+ CExpressionSample *bestsample = NULL;
+
+ // Add a sample point
+ int height = m_rcBounds.bottom - m_rcBounds.top;
+
+ mx += m_rcBounds.left;
+
+ for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, m_nEditType );
+
+ bool clipped = false;
+ int px = GetMouseForTime( sample->time, &clipped );
+ int py = height * ( 1.0f - sample->value );
+
+ int dx = px - mx;
+ int dy = py - my;
+
+ float dist = sqrt( (float)(dx * dx + dy * dy) );
+ if ( dist < closest_dist )
+ {
+ bestsample = sample;
+ closest_dist = dist;
+ }
+ }
+
+ // Not close to any of them!!!
+ if ( ( tolerance != 0.0f ) &&
+ ( closest_dist > tolerance ) )
+ return NULL;
+
+ return bestsample;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::DeselectAll( void )
+{
+ g_pExpressionTool->DeselectAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::SelectAll( void )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ for ( int i = 0; i < track->GetNumSamples( t ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ sample->selected = true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::Delete( void )
+{
+ g_pExpressionTool->DeleteSelectedSamples();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : sample -
+//-----------------------------------------------------------------------------
+void TimelineItem::AddSample( CExpressionSample const& sample )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ PreDataChanged( "Add sample point" );
+
+ track->AddSample( sample.time, sample.value, m_nEditType );
+ track->Resort( m_nEditType );
+
+ SetActive( true );
+
+ PostDataChanged( "Add sample point" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int TimelineItem::CountSelected( void )
+{
+ m_nNumSelected = m_pHelper->CountSelected( false );
+ return m_nNumSelected;
+}
+
+void TimelineItem::SetMousePositionForEvent( mxEvent *event )
+{
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)m_pWorkspace->getHandle(), &pt );
+
+ pt.x -= m_rcBounds.left;
+ pt.y -= m_rcBounds.top;
+
+ event->x = pt.x;
+ event->y = pt.y;
+}
+
+int TimelineItem::handleEvent( mxEvent *event )
+{
+ int iret = 0;
+
+ // Give helper a shot at the event
+ if ( m_pHelper->HelperHandleEvent( event ) )
+ {
+ return 1;
+ }
+
+ switch ( event->event )
+ {
+ case mxEvent::KeyDown:
+ {
+ switch ( event->key )
+ {
+ default:
+ iret = g_pChoreoView->HandleZoomKey( g_pExpressionTool, event->key );
+ break;
+ case VK_ESCAPE:
+ DeselectAll();
+ DrawSelf();
+ break;
+ case VK_DELETE:
+ Delete();
+ DrawSelf();
+ break;
+ case 'C':
+ Copy();
+ DrawSelf();
+ break;
+ case 'V':
+ Paste();
+ DrawSelf();
+ break;
+ case 'J':
+ {
+ g_pExpressionTool->OnCopyToFlex( g_pExpressionTool->GetScrubberSceneTime(), true );
+ }
+ break;
+ case 'K':
+ {
+ g_pExpressionTool->OnCopyFromFlex( g_pExpressionTool->GetScrubberSceneTime(), false );
+ }
+ break;
+ case 188: // VK_OEM_COMMA:
+ {
+ g_pExpressionTool->SetScrubTargetTime( 0.0f );
+ }
+ break;
+ case 190: // VK_OEM_PERIOD:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene )
+ {
+ g_pExpressionTool->SetScrubTargetTime( scene->FindStopTime() );
+ }
+ }
+ break;
+ case VK_LEFT:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = g_pExpressionTool->GetScrub();
+ curscrub -= ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = max( curscrub, 0.0f );
+ g_pExpressionTool->SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case VK_RIGHT:
+ {
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( scene && scene->GetSceneFPS() > 0 )
+ {
+ float curscrub = g_pExpressionTool->GetScrub();
+ curscrub += ( 1.0f / (float)scene->GetSceneFPS() );
+ curscrub = min( curscrub, scene->FindStopTime() );
+ g_pExpressionTool->SetScrubTargetTime( curscrub );
+ }
+ }
+ break;
+ case 191:
+ {
+ if ( g_pChoreoView->IsPlayingScene() )
+ {
+ g_pChoreoView->StopScene();
+ }
+ }
+ break;
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::KeyUp:
+ {
+ switch ( event->key )
+ {
+ case VK_SPACE:
+ {
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( track && track->IsComboType() )
+ {
+ SetEditType( m_nEditType == 0 ? 1 : 0 );
+ DrawSelf();
+ }
+ }
+ break;
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDown:
+ {
+ sound->Flush();
+
+ SetFocus( (HWND)g_pExpressionTool->getHandle() );
+
+ int height = m_rcBounds.bottom - m_rcBounds.top;
+
+ bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
+
+ if ( m_nDragging == DRAGTYPE_NONE )
+ {
+ bool ctrlDown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+
+ CExpressionSample *sample = GetSampleUnderMouse( event->x, event->y, ctrlDown ? FP_TL_ADDSAMPLE_TOLERANCE : FP_TL_SELECTION_TOLERANCE );
+
+ if ( IsMouseOverGrowHandle( (short)event->x, (short)event->y ) )
+ {
+ m_nDragging = DRAGTYPE_GROW;
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+
+ m_nStartX = m_nLastX;
+ m_nStartY = m_nLastY;
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers );
+
+ DrawGrowRect();
+ }
+ else if ( sample )
+ {
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ sample->selected = !sample->selected;
+ DrawSelf();
+ }
+ else if ( sample->selected )
+ {
+ m_nDragging = rightbutton ? DRAGTYPE_MOVEPOINTS_TIME : DRAGTYPE_MOVEPOINTS_VALUE;
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+
+ m_nStartX = m_nLastX;
+ m_nStartY = m_nLastY;
+
+ PreDataChanged( "Move sample point(s)" );
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers );
+
+ DrawSelf();
+ }
+ else
+ {
+ if ( !( event->modifiers & mxEvent::KeyShift ) )
+ {
+ DeselectAll();
+ DrawSelf();
+ }
+
+ m_nDragging = DRAGTYPE_SELECTION;
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+
+ m_nStartX = m_nLastX;
+ m_nStartY = m_nLastY;
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers );
+
+ DrawFocusRect();
+ }
+ }
+ else if ( event->modifiers & mxEvent::KeyCtrl )
+ {
+ CChoreoEvent *e = g_pExpressionTool->GetSafeEvent();
+ if ( e )
+ {
+ // Add a sample point
+ float t = GetTimeForMouse( (short)event->x + m_rcBounds.left );
+
+ CExpressionSample sample;
+ sample.time = FacePoser_SnapTime( t );
+ sample.value = 1.0f - (float)( (short)( event->y ) ) / (float)height;
+ sample.selected = false;
+
+ AddSample( sample );
+
+ DrawSelf();
+ }
+ }
+ else
+ {
+ if ( rightbutton )
+ {
+ POINT pt;
+ pt.x = event->x;
+ pt.y = event->y;
+ ClientToScreen( (HWND)m_pWorkspace->getHandle(), &pt );
+ ScreenToClient( (HWND)g_pExpressionTool->getHandle(), &pt );
+ event->x = pt.x;
+ event->y = pt.y;
+ g_pExpressionTool->ShowContextMenu( event, true );
+ return iret;
+ }
+
+ if ( !( event->modifiers & mxEvent::KeyShift ) )
+ {
+ DeselectAll();
+ DrawSelf();
+ }
+
+ m_nDragging = DRAGTYPE_SELECTION;
+ m_nLastX = (short)event->x;
+ m_nLastY = (short)event->y;
+
+ m_nStartX = m_nLastX;
+ m_nStartY = m_nLastY;
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers );
+
+ DrawFocusRect();
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseDrag:
+ case mxEvent::MouseMove:
+ {
+ if ( m_nDragging != DRAGTYPE_NONE )
+ {
+ if ( m_nDragging == DRAGTYPE_SELECTION )
+ {
+ DrawFocusRect();
+ }
+ else if ( m_nDragging == DRAGTYPE_GROW )
+ {
+ DrawGrowRect();
+ }
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers );
+
+ if ( m_nDragging == DRAGTYPE_SELECTION )
+ {
+ DrawFocusRect();
+ }
+ else if ( m_nDragging == DRAGTYPE_GROW )
+ {
+ DrawGrowRect();
+ }
+
+ if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
+ m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
+ {
+ DrawSelf();
+ }
+ }
+ else
+ {
+ // See if anything is selected
+ CountSelected();
+ if ( m_nNumSelected <= 0 && g_pExpressionTool->IsFocusItem( this ) )
+ {
+ // Nothing selected
+ // Draw auto highlight
+ DrawAutoHighlight( event );
+ }
+ }
+ iret = 1;
+ }
+ break;
+ case mxEvent::MouseUp:
+ {
+ bool overgrow = IsMouseOverGrowHandle( (short)event->x, (short)event->y );
+
+ if ( m_nDragging != DRAGTYPE_NONE )
+ {
+ if ( m_nDragging == DRAGTYPE_SELECTION )
+ {
+ DrawFocusRect();
+ }
+ else if ( m_nDragging == DRAGTYPE_GROW )
+ {
+ DrawGrowRect();
+ }
+
+ MouseDrag( (short)event->x, (short)event->y, event->modifiers, true );
+
+ if ( m_nDragging == DRAGTYPE_GROW )
+ {
+ // Finish grow by resizing control
+ int desiredheight = m_nCurrentHeight + event->y - m_nStartY;
+ if ( desiredheight >= 10 )
+ {
+ m_nCurrentHeight = desiredheight;
+ g_pExpressionTool->LayoutItems( true );
+ }
+ }
+ else if ( m_nDragging != DRAGTYPE_MOVEPOINTS_VALUE &&
+ m_nDragging != DRAGTYPE_MOVEPOINTS_TIME )
+ {
+ SelectPoints();
+ }
+ else
+ {
+ PostDataChanged( "Move sample point(s)" );
+ }
+
+ m_nDragging = DRAGTYPE_NONE;
+
+ DrawSelf();
+ }
+
+ bool rightbutton = ( event->buttons & mxEvent::MouseRightButton ) ? true : false;
+ bool shift = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
+ bool ctrl = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
+
+ if ( !rightbutton && !shift && !ctrl )
+ {
+ if ( realtime - m_flLastClickTime < DOUBLE_CLICK_TIME )
+ {
+ if ( overgrow || IsCollapsed() )
+ {
+ OnDoubleClicked();
+ }
+ }
+
+ m_flLastClickTime = realtime;
+ }
+
+ iret = 1;
+ }
+ break;
+ }
+
+ return iret;
+}
+
+void TimelineItem::MouseDrag( int x, int y, int modifiers, bool snap /*=false*/ )
+{
+ if ( m_nDragging == DRAGTYPE_NONE )
+ return;
+
+ int width = m_rcBounds.right - m_rcBounds.left;
+ int height = m_rcBounds.bottom - m_rcBounds.top;
+
+ if ( m_nDragging == DRAGTYPE_MOVEPOINTS_TIME ||
+ m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
+ {
+ int dx = x - m_nLastX;
+ int dy = y - m_nLastY;
+
+ if ( !( modifiers & mxEvent::KeyCtrl ) )
+ {
+ // Zero out motion on other axis
+ if ( m_nDragging == DRAGTYPE_MOVEPOINTS_VALUE )
+ {
+ dx = 0;
+ x = m_nLastX;
+ }
+ else
+ {
+ dy = 0;
+ y = m_nLastY;
+ }
+ }
+
+ float dfdx = (float)dx / g_pExpressionTool->GetPixelsPerSecond();
+ float dfdy = (float)dy / (float)height;
+
+ g_pExpressionTool->MoveSelectedSamples( dfdx, dfdy, snap );
+
+ // Update the scrubber
+ if ( (float)width > 0 )
+ {
+ float t = GetTimeForMouse( x + m_rcBounds.left );
+ g_pExpressionTool->ForceScrubPosition( t );
+
+ g_pMatSysWindow->Frame();
+ }
+ }
+
+ m_nLastX = x;
+ m_nLastY = y;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::DrawFocusRect( void )
+{
+ RECT rcFocus;
+
+ rcFocus.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcFocus.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcFocus.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcFocus.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ POINT offset;
+ offset.x = m_rcBounds.left;
+ offset.y = m_rcBounds.top;
+ ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+void TimelineItem::DrawSelf( void )
+{
+ CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds );
+ Draw( drawHelper );
+}
+
+void TimelineItem::Draw( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ CChoreoEvent *e = track->GetEvent();
+ if ( !e )
+ return;
+
+ Assert( e->HasEndTime() );
+
+ bool active = track && ( IsValid() || IsActive() );
+
+ float starttime;
+ float endtime;
+
+ g_pExpressionTool->GetStartAndEndTime( starttime, endtime );
+
+ CountSelected();
+ int scount = GetNumSelected();
+
+ COLORREF bgColor = RGB( 230, 230, 200 );
+ if ( IsCollapsed() && active )
+ {
+ bgColor = RGB( 200, 230, 200 );
+ }
+
+ RECT rcClient = m_rcBounds;
+
+ drawHelper.DrawFilledRect( bgColor, rcClient );
+
+ COLORREF gray = RGB( 200, 200, 200 );
+
+ DrawEventEnd( drawHelper );
+
+ DrawRelativeTags( drawHelper );
+ if ( !IsCollapsed() && track )
+ {
+ if ( m_nEditType == 1 )
+ {
+ float zero = track->GetZeroValue( m_nEditType, true );
+
+ drawHelper.DrawColoredLine( RGB( 180, 200, 220 ), PS_SOLID, 1,
+ rcClient.left, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) ,
+ rcClient.right, ( rcClient.top * zero + rcClient.bottom * (1 - zero)) );
+ }
+
+ drawHelper.DrawOutlinedRect( RGB( 100, 150, 200 ), PS_SOLID, 1, rcClient );
+
+ // Draw grow handle into background...
+ if ( CanHaveGrowHandle() )
+ {
+ RECT handleRect;
+ GetGrowHandleRect( handleRect );
+ DrawGrowHandle( drawHelper, handleRect );
+ }
+
+ // Draw left/right underneath amount so go backbard
+ for ( int type = ( track->IsComboType() ? 1 : 0 ); type >= 0; type-- )
+ {
+ COLORREF lineColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
+ COLORREF shadowColor = ( type == m_nEditType ) ? RGB( 150, 150, 250 ) : gray;
+ COLORREF dotColor = ( type == m_nEditType ) ? RGB( 0, 0, 255 ) : gray;
+ COLORREF dotColorSelected = ( type == m_nEditType ) ? RGB( 240, 80, 20 ) : gray;
+
+ int height = rcClient.bottom - rcClient.top;
+ int bottom = rcClient.bottom;
+
+ // Fixme, could look at 1st derivative and do more sampling at high rate of change?
+ // or near actual sample points!
+ float linelength = g_pExpressionTool->IsFocusItem( this ) ? 2.0f : 8.0f;
+
+ float timestepperpixel = linelength / g_pExpressionTool->GetPixelsPerSecond();
+
+ float stoptime = min( endtime, e->GetDuration() );
+
+ float prev_t = starttime;
+ float prev_value = track->GetFracIntensity( prev_t, type );
+
+ CUtlVector< POINT > segments;
+
+ /*
+ if (type == m_nEditType)
+ {
+ // draw hermite version of time step
+ float i0, i1, i2;
+ float time10hz = starttime;
+
+ i0 = track->GetFracIntensity( time10hz, type );
+ i1 = i0;
+ time10hz = starttime + 0.1;
+ i2 = track->GetFracIntensity( time10hz, type );;
+
+ for ( float t = starttime; t <= stoptime; t += timestepperpixel )
+ {
+ while (t >= time10hz)
+ {
+ time10hz += 0.1;
+ i0 = i1;
+ i1 = i2;
+ i2 = track->GetFracIntensity( time10hz, type );;
+ }
+
+ float value = Hermite_Spline( i0, i1, i2, (t - time10hz + 0.1) / 0.1 );
+
+ int prevx, x;
+
+ bool clipped1, clipped2;
+ x = GetMouseForTime( t, &clipped1 );
+ prevx = GetMouseForTime( prev_t, &clipped2 );
+
+ //if ( !clipped1 && !clipped2 )
+ {
+ // Draw segment
+ //drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ // prevx, bottom - prev_value * height,
+ // x, bottom - value * height );
+
+ POINT pt;
+
+ if ( segments.Count() == 0 )
+ {
+ pt.x = prevx;
+ pt.y = bottom - prev_value * height;
+
+ segments.AddToTail( pt );
+ }
+
+ pt.x = x;
+ pt.y = bottom - value * height;
+
+ segments.AddToTail( pt );
+ }
+
+ prev_t = t;
+ prev_value = value;
+ }
+
+ if ( segments.Count() >= 2 )
+ {
+ drawHelper.DrawColoredPolyLine( shadowColor, PS_SOLID, 1, segments );
+ }
+
+ segments.RemoveAll();
+ }
+ */
+ for ( float t = starttime; t <= stoptime; t += timestepperpixel )
+ {
+ float value = track->GetFracIntensity( t, type );
+
+ int prevx, x;
+
+ bool clipped1, clipped2;
+ x = GetMouseForTime( t, &clipped1 );
+ prevx = GetMouseForTime( prev_t, &clipped2 );
+
+ //if ( !clipped1 && !clipped2 )
+ {
+ // Draw segment
+ //drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
+ // prevx, bottom - prev_value * height,
+ // x, bottom - value * height );
+
+ POINT pt;
+
+ if ( segments.Count() == 0 )
+ {
+ pt.x = prevx;
+ pt.y = bottom - prev_value * height;
+
+ segments.AddToTail( pt );
+ }
+
+ pt.x = x;
+ pt.y = bottom - value * height;
+
+ segments.AddToTail( pt );
+ }
+
+ prev_t = t;
+ prev_value = value;
+ }
+
+ if ( segments.Count() >= 2 )
+ {
+ drawHelper.DrawColoredPolyLine( lineColor, PS_SOLID, 1, segments );
+ }
+
+ for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
+ {
+ bool dummy;
+ CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
+
+ /*
+ int pixel = (int)( ( start->time / event_time ) * width + 0.5f);
+ int x = m_rcBounds.left + pixel;
+ float roundedfrac = (float)pixel / (float)width;
+ */
+ float value = start->value; // track->GetFracIntensity( start->time, type );
+ bool clipped = false;
+ int x = GetMouseForTime( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+
+
+ if ( !start->selected )
+ continue;
+
+ if ( start->GetCurveType() == CURVE_DEFAULT )
+ continue;
+
+ // Draw curve type indicator...
+ char sz[ 128 ];
+ Q_snprintf( sz, sizeof( sz ), "%s", Interpolator_NameForCurveType( start->GetCurveType(), true ) );
+ RECT rc;
+ int fontSize = 9;
+ rc.top = clamp( y + 5, rcClient.top + 2, rcClient.bottom - 2 - fontSize );
+ rc.bottom = rc.top + fontSize + 1;
+ rc.left = x - 75;
+ rc.right = x + 175;
+ drawHelper.DrawColoredText( "Arial", fontSize, 500, shadowColor, rc, sz );
+ }
+ }
+ }
+
+ if ( track && track->IsComboType() && !IsCollapsed() )
+ {
+ RECT title = rcClient;
+ title.left += 10;
+ title.top += 14;
+ title.bottom = title.top + 9;
+
+ char sz[ 128 ];
+
+ if ( m_nEditType == 1 )
+ {
+ sprintf( sz, "left" );
+
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
+
+ sprintf( sz, "right" );
+
+ title.top = rcClient.bottom - 22;
+ title.bottom = rcClient.bottom;
+
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
+ }
+
+ int mid = ( rcClient.top + rcClient.bottom ) / 2;
+
+ title.top = mid - 10;
+ title.bottom = mid;
+
+ sprintf( sz, "editmode: <%s>", m_nEditType == 0 ? "amount" : "left/right" );
+
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 0, 0, 255 ), title, sz );
+ }
+
+ if ( track )
+ {
+ RECT title = rcClient;
+ title.left += 2;
+ title.top += 2;
+ title.bottom = title.top + 9;
+
+ char const *name = track->GetFlexControllerName();
+ char sz[ 128 ];
+
+ if ( scount > 0 )
+ {
+ sprintf( sz, "{%i} - ", scount );
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
+ drawHelper.DrawColoredText( "Arial", 9, 500,
+ RGB( 120, 120, 0 ), title, sz );
+
+ title.left += len + 2;
+ }
+
+ sprintf( sz, "%s -", name );
+
+ int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
+
+ drawHelper.DrawColoredText( "Arial", 9, 500,
+ active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
+ title, sz );
+
+ sprintf( sz, "%s", IsActive() ? "enabled" : "disabled" );
+
+ title.left += len + 2;
+
+ len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
+ drawHelper.DrawColoredText( "Arial", 9, 500,
+ active ? RGB( 0, 150, 100 ) : RGB( 100, 100, 100 ),
+ title, sz );
+
+ if ( active )
+ {
+ title.left += len + 2;
+
+ sprintf( sz, " <%i>", track->GetNumSamples( 0 ) );
+
+ len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
+ drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 220, 0, 00 ), title, sz );
+ }
+ }
+}
+
+void TimelineItem::DrawAutoHighlight( mxEvent *event )
+{
+ if ( IsCollapsed() )
+ return;
+
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ CExpressionSample *hover = GetSampleUnderMouse( event->x, event->y, 0.0f );
+ CChoreoWidgetDrawHelper drawHelper( m_pWorkspace, m_rcBounds, true );
+
+ RECT rcClient = m_rcBounds;
+
+ // Draw left/right underneath amount so go backbard
+ int type = m_nEditType;
+
+ COLORREF dotColor = RGB( 0, 0, 255 );
+ COLORREF dotColorSelected = RGB( 240, 80, 20 );
+ COLORREF clrHighlighted = RGB( 0, 200, 0 );
+
+ int height = rcClient.bottom - rcClient.top;
+ int bottom = rcClient.bottom;
+
+ int dotsize = 6;
+ int dotSizeSelected = 6;
+ int dotSizeHighlighted = 6;
+
+ COLORREF clr = dotColor;
+ COLORREF clrSelected = dotColorSelected;
+ COLORREF bgColor = RGB( 230, 230, 200 );
+
+ // Fixme, could look at 1st derivative and do more sampling at high rate of change?
+ // or near actual sample points!
+ for ( int sample = 0; sample < track->GetNumSamples( type ); sample++ )
+ {
+ bool dummy;
+ CExpressionSample *start = track->GetBoundedSample( sample, dummy, type );
+
+ float value = start->value;
+ bool clipped = false;
+ int x = GetMouseForTime( start->time, &clipped );
+ if ( clipped )
+ continue;
+ int y = bottom - value * height;
+
+ if ( hover == start )
+ {
+ drawHelper.DrawCircle(
+ bgColor,
+ x, y,
+ dotSizeHighlighted,
+ true );
+
+ drawHelper.DrawCircle(
+ clrHighlighted,
+ x, y,
+ dotSizeHighlighted,
+ false );
+
+
+ }
+ else
+ {
+ drawHelper.DrawCircle(
+ start->selected ? clrSelected : clr,
+ x, y,
+ start->selected ? dotSizeSelected : dotsize,
+ true );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : drawHelper -
+//-----------------------------------------------------------------------------
+void TimelineItem::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ CChoreoEvent *event = track->GetEvent();
+ if ( !event )
+ return;
+
+ float duration = event->GetDuration();
+
+ if ( duration <= 0.0f )
+ return;
+
+ CChoreoScene *scene = g_pChoreoView->GetScene();
+ if ( !scene )
+ return;
+
+ RECT rcClient = m_rcBounds;;
+ //drawHelper.GetClientRect( rcClient );
+
+ // Iterate relative tags
+ for ( int i = 0; i < scene->GetNumActors(); i++ )
+ {
+ CChoreoActor *a = scene->GetActor( i );
+ if ( !a )
+ continue;
+
+ for ( int j = 0; j < a->GetNumChannels(); j++ )
+ {
+ CChoreoChannel *c = a->GetChannel( j );
+ if ( !c )
+ continue;
+
+ for ( int k = 0 ; k < c->GetNumEvents(); k++ )
+ {
+ CChoreoEvent *e = c->GetEvent( k );
+ if ( !e )
+ continue;
+
+ // add each tag to combo box
+ for ( int t = 0; t < e->GetNumRelativeTags(); t++ )
+ {
+ CEventRelativeTag *tag = e->GetRelativeTag( t );
+ if ( !tag )
+ continue;
+
+ //SendMessage( control, CB_ADDSTRING, 0, (LPARAM)va( "\"%s\" \"%s\"", tag->GetName(), e->GetParameters() ) );
+ bool clipped = false;
+ int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
+ if ( clipped )
+ continue;
+
+ drawHelper.DrawColoredLine( RGB( 180, 180, 220 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
+ }
+ }
+ }
+ }
+
+ for ( int t = 0; t < event->GetNumTimingTags(); t++ )
+ {
+ CFlexTimingTag *tag = event->GetTimingTag( t );
+ if ( !tag )
+ continue;
+
+ bool clipped = false;
+ int tagx = GetMouseForTime( tag->GetStartTime() - event->GetStartTime(), &clipped );
+ if ( clipped )
+ continue;
+
+ // Draw relative tag marker
+ drawHelper.DrawColoredLine( RGB( 220, 180, 180 ), PS_SOLID, 1, tagx, rcClient.top, tagx, rcClient.bottom );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *exp -
+// flexnum -
+//-----------------------------------------------------------------------------
+void TimelineItem::SetExpressionInfo( CFlexAnimationTrack *track, int flexnum )
+{
+ m_szTrackName[ 0 ] = 0;
+ if ( track )
+ {
+ V_strcpy_safe( m_szTrackName, track->GetFlexControllerName() );
+ SetActive( track->IsTrackActive() );
+ }
+
+ m_nFlexNum = flexnum;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::Copy( void )
+{
+ if ( !g_pExpressionTool )
+ return;
+
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ g_pExpressionTool->Copy( track );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::Paste( void )
+{
+ if ( !g_pExpressionTool )
+ return;
+
+ if ( !g_pExpressionTool->HasCopyData() )
+ return;
+
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ g_pExpressionTool->Paste( track );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::Clear( bool preserveundo )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ if ( preserveundo )
+ {
+ PreDataChanged( "Clear" );
+ }
+
+ track->Clear();
+
+ if ( preserveundo )
+ {
+ PostDataChanged( "Clear" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void TimelineItem::SetCollapsed( bool state )
+{
+ m_bCollapsed = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TimelineItem::IsCollapsed( void ) const
+{
+ return m_bCollapsed;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int TimelineItem::GetHeight( void )
+{
+ return ( IsCollapsed() ? 12 : m_nCurrentHeight );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void TimelineItem::SetActive( bool state )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ track->SetTrackActive( state );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TimelineItem::IsActive( void )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return false;
+
+ return track->IsTrackActive();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TimelineItem::IsValid( void )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return false;
+
+ if ( track->GetNumSamples( 0 ) > 0 )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CFlexAnimationTrack
+//-----------------------------------------------------------------------------
+CFlexAnimationTrack *TimelineItem::GetSafeTrack( void )
+{
+ if ( !g_pExpressionTool )
+ return NULL;
+
+ CChoreoEvent *ev = g_pExpressionTool->GetSafeEvent();
+ if ( !ev )
+ return NULL;
+
+ // Find track by name
+ for ( int i = 0; i < ev->GetNumFlexAnimationTracks() ; i++ )
+ {
+ CFlexAnimationTrack *track = ev->GetFlexAnimationTrack( i );
+ if ( track && !stricmp( track->GetFlexControllerName(), m_szTrackName ) )
+ {
+ return track;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+//-----------------------------------------------------------------------------
+void TimelineItem::SetEditType( int type )
+{
+ Assert( type == 0 || type == 1 );
+
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track || !track->IsComboType() )
+ {
+ type = 0;
+ }
+
+ m_nEditType = type;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int TimelineItem::GetEditType( void )
+{
+ return m_nEditType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::SelectPoints( void )
+{
+ RECT rcSelection;
+
+ rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
+ rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
+
+ rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
+ rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
+
+ int selW = rcSelection.right - rcSelection.left;
+ int selH = rcSelection.bottom - rcSelection.top;
+
+ float tolerance = FP_TL_SELECTION_RECTANGLE_TOLERANCE;
+ // If they are just clicking and releasing in one spot, capture any items w/in a larger tolerance
+ if ( selW <= 2 && selH <= 2 )
+ {
+ tolerance = FP_TL_SELECTION_TOLERANCE;
+
+ CExpressionSample *sample = GetSampleUnderMouse( rcSelection.left + selW * 0.5f, rcSelection.top + selH * 0.5f );
+ if ( sample )
+ {
+ sample->selected = true;
+ return;
+ }
+ }
+ else
+ {
+ InflateRect( &rcSelection, 3, 3 );
+ }
+
+ int width = m_rcBounds.right - m_rcBounds.left;
+ int height = m_rcBounds.bottom - m_rcBounds.top;
+
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track || !width || !height )
+ return;
+
+ CChoreoEvent *e = track->GetEvent();
+ Assert( e );
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+
+ float fleft = (float)GetTimeForMouse( rcSelection.left + m_rcBounds.left );
+ float fright = (float)GetTimeForMouse( rcSelection.right + m_rcBounds.left );
+
+ //fleft *= duration;
+ //fright *= duration;
+
+ float ftop = (float)rcSelection.top / (float)height;
+ float fbottom = (float)rcSelection.bottom / (float)height;
+
+ fleft = clamp( fleft, 0.0f, duration );
+ fright = clamp( fright, 0.0f, duration );
+ ftop = clamp( ftop, 0.0f, 1.0f );
+ fbottom = clamp( fbottom, 0.0f, 1.0f );
+
+ float timestepperpixel = 1.0f / g_pExpressionTool->GetPixelsPerSecond();
+ float yfracstepperpixel = 1.0f / (float)height;
+
+ float epsx = tolerance*timestepperpixel;
+ float epsy = tolerance*yfracstepperpixel;
+
+ for ( int i = 0; i < track->GetNumSamples( m_nEditType ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, m_nEditType );
+
+ if ( sample->time + epsx < fleft )
+ continue;
+
+ if ( sample->time - epsx > fright )
+ continue;
+
+ if ( (1.0f - sample->value ) + epsy < ftop )
+ continue;
+
+ if ( (1.0f - sample->value ) - epsy > fbottom )
+ continue;
+
+ sample->selected = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *undodescription -
+//-----------------------------------------------------------------------------
+void TimelineItem::PreDataChanged( char const *undodescription )
+{
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->SetDirty( true );
+ g_pChoreoView->PushUndo( undodescription );
+ }
+ ++m_nUndoSetup;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *redodescription -
+//-----------------------------------------------------------------------------
+void TimelineItem::PostDataChanged( char const *redodescription )
+{
+ --m_nUndoSetup;
+ if ( m_nUndoSetup == 0 )
+ {
+ g_pChoreoView->PushRedo( redodescription );
+ g_pExpressionTool->InvalidateLayout();
+ }
+}
+
+void TimelineItem::SetBounds( const RECT& rect )
+{
+ m_rcBounds = rect;
+}
+
+void TimelineItem::GetBounds( RECT& rect )
+{
+ rect = m_rcBounds;
+}
+
+void TimelineItem::SetVisible( bool vis )
+{
+ m_bVisible = vis;
+}
+
+bool TimelineItem::GetVisible( void ) const
+{
+ return m_bVisible;
+}
+
+int TimelineItem::GetNumSelected( void )
+{
+ return m_nNumSelected;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::SnapAll()
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ for ( int i = 0; i < track->GetNumSamples( t ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ sample->time = FacePoser_SnapTime( sample->time );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::SnapSelected()
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ for ( int i = 0; i < track->GetNumSamples( t ); i++ )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( !sample->selected )
+ continue;
+
+ sample->time = FacePoser_SnapTime( sample->time );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : start -
+// end -
+//-----------------------------------------------------------------------------
+void TimelineItem::DeletePoints( float start, float end )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ for ( int t = 0; t < 2; t++ )
+ {
+ int num = track->GetNumSamples( t );
+ for ( int i = num - 1; i >= 0 ; i-- )
+ {
+ CExpressionSample *sample = track->GetSample( i, t );
+ if ( sample->time < start || sample->time > end )
+ continue;
+
+ track->RemoveSample( i, t );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void TimelineItem::OnDoubleClicked()
+{
+ // Disabled for now by request of BillF
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ SetCollapsed( !IsCollapsed() );
+ g_pExpressionTool->LayoutItems( true );
+}
+
+void TimelineItem::DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper )
+{
+ CFlexAnimationTrack *track = GetSafeTrack();
+ if ( !track )
+ return;
+
+ CChoreoEvent *e = track->GetEvent();
+ if ( !e )
+ return;
+
+ float duration = e->GetDuration();
+ if ( !duration )
+ return;
+
+ int leftx = GetMouseForTime( duration );
+ if ( leftx > m_rcBounds.right )
+ return;
+
+ drawHelper.DrawColoredLine(
+ COLOR_CHOREO_ENDTIME, PS_SOLID, 1,
+ leftx, m_rcBounds.top, leftx, m_rcBounds.bottom );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : helper -
+// handleRect -
+//-----------------------------------------------------------------------------
+void TimelineItem::DrawGrowHandle( CChoreoWidgetDrawHelper& helper, RECT& handleRect )
+{
+ Assert(CanHaveGrowHandle());
+
+ RECT useRect = handleRect;
+ helper.OffsetSubRect( useRect );
+
+ POINT region[4];
+ int cPoints = 4;
+
+ region[ 0 ].x = useRect.left + GROW_HANDLE_INSETPIXELS;
+ region[ 0 ].y = useRect.top;
+
+ region[ 1 ].x = useRect.right - GROW_HANDLE_INSETPIXELS;
+ region[ 1 ].y = useRect.top;
+
+ region[ 2 ].x = useRect.right;
+ region[ 2 ].y = useRect.bottom;
+
+ region[ 3 ].x = useRect.left;
+ region[ 3 ].y = useRect.bottom;
+
+ HDC dc = helper.GrabDC();
+
+ HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
+
+ int oldPF = SetPolyFillMode( dc, ALTERNATE );
+
+ HBRUSH brBg = CreateSolidBrush( RGB( 150, 150, 150 ) );
+ HBRUSH brBorder = CreateSolidBrush( RGB( 200, 200, 200 ) );
+
+ FillRgn( dc, rgn, brBg );
+ FrameRgn( dc, rgn, brBorder, 1, 1 );
+
+ SetPolyFillMode( dc, oldPF );
+
+ DeleteObject( rgn );
+
+ DeleteObject( brBg );
+ DeleteObject( brBorder );
+
+ // draw a line in the middle
+ int midy = ( handleRect.bottom + handleRect.top ) * 0.5f;
+ int lineinset = GROW_HANDLE_INSETPIXELS *1.5;
+
+ helper.DrawColoredLine( RGB( 63, 63, 63 ), PS_SOLID, 1,
+ handleRect.left + lineinset, midy,
+ handleRect.right - lineinset, midy );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : rc -
+//-----------------------------------------------------------------------------
+void TimelineItem::GetGrowHandleRect( RECT& rc )
+{
+ rc = m_rcBounds;
+ rc.bottom -= 1;
+ rc.top = rc.bottom - GROW_HANDLE_HEIGHT;
+ rc.left = ( rc.right + rc.left ) / 2 - GROW_HANDLE_WIDTH / 2;
+ rc.right = rc.left + GROW_HANDLE_WIDTH;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TimelineItem::CanHaveGrowHandle()
+{
+ if ( IsCollapsed() )
+ return false;
+
+ if ( !g_pExpressionTool->IsFocusItem( this ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : x -
+// y -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool TimelineItem::IsMouseOverGrowHandle( int x, int y)
+{
+ if ( !CanHaveGrowHandle() )
+ return false;
+
+ RECT rcGrowHandle;
+ GetGrowHandleRect( rcGrowHandle );
+
+ POINT pt;
+ pt.x = x + m_rcBounds.left;
+ pt.y = y + m_rcBounds.top;
+
+ return PtInRect( &rcGrowHandle, pt ) ? true : false;
+}
+
+void TimelineItem::DrawGrowRect()
+{
+ RECT rcFocus = m_rcBounds;
+ rcFocus.bottom = m_rcBounds.top + m_nLastY;
+ OffsetRect( &rcFocus, -m_rcBounds.left, -m_rcBounds.top );
+
+ POINT offset;
+ offset.x = m_rcBounds.left;
+ offset.y = m_rcBounds.top;
+ ClientToScreen( (HWND)m_pWorkspace->getHandle(), &offset );
+ OffsetRect( &rcFocus, offset.x, offset.y );
+
+ HDC dc = GetDC( NULL );
+
+ ::DrawFocusRect( dc, &rcFocus );
+
+ ReleaseDC( NULL, dc );
+}
+
+void TimelineItem::GetWorkList( bool reflect, CUtlVector< TimelineItem * >& list )
+{
+ if ( !reflect )
+ {
+ list.AddToTail( this );
+ }
+ else
+ {
+ g_pExpressionTool->GetTimelineItems( list );
+ }
+}
diff --git a/utils/hlfaceposer/timelineitem.h b/utils/hlfaceposer/timelineitem.h
new file mode 100644
index 0000000..34cc472
--- /dev/null
+++ b/utils/hlfaceposer/timelineitem.h
@@ -0,0 +1,161 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TIMELINEITEM_H
+#define TIMELINEITEM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <mxtk/mx.h>
+#include "utlvector.h"
+#include "ExpressionSample.h"
+
+class CExpression;
+class ExpressionTool;
+class CFlexAnimationTrack;
+class CChoreoWidgetDrawHelper;
+class CChoreoView;
+
+template< class T > class CCurveEditorHelper;
+
+#define FP_TL_SELECTION_TOLERANCE 30.0f
+#define FP_TL_SELECTION_RECTANGLE_TOLERANCE 5.0f
+#define FP_TL_ADDSAMPLE_TOLERANCE 5.0f
+
+class TimelineItem
+{
+public:
+ // Construction
+ TimelineItem( mxWindow *workspace );
+ ~TimelineItem( void );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void Draw( CChoreoWidgetDrawHelper& drawHelper );
+ void DrawEventEnd( CChoreoWidgetDrawHelper& drawHelper );
+ void DrawSelf( void );
+
+ void SetExpressionInfo( CFlexAnimationTrack *track, int flexnum );
+
+ void Clear( bool preserveundo );
+
+ void SetCollapsed( bool state );
+ bool IsCollapsed( void ) const;
+
+ void SetActive( bool state );
+ bool IsActive( void );
+
+ int GetHeight( void );
+ void ResetHeight();
+
+ // If samples > 0
+ bool IsValid( void );
+
+ void SetEditType( int type );
+ int GetEditType( void );
+
+ void SetBounds( const RECT& rect );
+
+ void GetBounds( RECT& rect );
+
+ void SetVisible( bool vis );
+ bool GetVisible( void ) const;
+
+ int CountSelected( void );
+ CFlexAnimationTrack *GetSafeTrack( void );
+ int GetNumSelected( void );
+
+ void Copy( void );
+ void Paste( void );
+
+ void SelectAll( void );
+ void DeselectAll( void );
+ void Delete( void );
+
+ void GetLastMouse( int& mx, int& my )
+ {
+ mx = m_nLastX;
+ my = m_nLastY;
+ }
+
+ void SnapAll();
+ void SnapSelected();
+ void DeletePoints( float start, float end );
+
+ float GetTimeForMouse( int mx, bool clip = false );
+ int GetMouseForTime( float t, bool *clipped = NULL );
+
+ void SetMousePositionForEvent( mxEvent *event );
+
+ int NumSamples();
+ CExpressionSample *GetSample( int idx );
+ void PreDataChanged( char const *undodescription );
+ void PostDataChanged( char const *redodescription );
+ CExpressionSample *GetSampleUnderMouse( int mx, int my, float tolerance = FP_TL_SELECTION_TOLERANCE );
+ void GetWorkList( bool reflect, CUtlVector< TimelineItem * >& list );
+
+private:
+ enum
+ {
+ DRAGTYPE_NONE = 0,
+ DRAGTYPE_MOVEPOINTS_VALUE,
+ DRAGTYPE_MOVEPOINTS_TIME,
+ DRAGTYPE_SELECTION,
+ DRAGTYPE_GROW,
+ };
+
+
+ bool CanHaveGrowHandle();
+ void DrawGrowHandle( CChoreoWidgetDrawHelper& helper, RECT& handleRect );
+ void GetGrowHandleRect( RECT& rc );
+ bool IsMouseOverGrowHandle( int x, int y);
+
+ void MouseDrag( int x, int y, int modifiers, bool snap = false );
+
+ void DrawGrowRect();
+
+ void DrawFocusRect( void );
+ void SelectPoints( void );
+
+ void OnDoubleClicked( void );
+
+ void DrawAutoHighlight( mxEvent *event );
+
+ int m_nDragging;
+ int m_nLastX;
+ int m_nLastY;
+
+ int m_nStartX;
+ int m_nStartY;
+
+ void AddSample( CExpressionSample const& sample );
+
+ void DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper );
+
+ int m_nNumSelected;
+
+ int m_nFlexNum;
+
+ bool m_bCollapsed;
+
+ char m_szTrackName[ 128 ];
+
+ int m_nEditType;
+
+ int m_nUndoSetup;
+ RECT m_rcBounds;
+ bool m_bVisible;
+
+ mxWindow *m_pWorkspace;
+ double m_flLastClickTime;
+
+ int m_nCurrentHeight;
+
+ CCurveEditorHelper< TimelineItem > *m_pHelper;
+};
+
+#endif // TIMELINEITEM_H
diff --git a/utils/hlfaceposer/vcd1.ico b/utils/hlfaceposer/vcd1.ico
new file mode 100644
index 0000000..249d061
--- /dev/null
+++ b/utils/hlfaceposer/vcd1.ico
Binary files differ
diff --git a/utils/hlfaceposer/vcdbrowser.cpp b/utils/hlfaceposer/vcdbrowser.cpp
new file mode 100644
index 0000000..0aa6263
--- /dev/null
+++ b/utils/hlfaceposer/vcdbrowser.cpp
@@ -0,0 +1,881 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include <windows.h>
+#include "resource.h"
+#include "vcdbrowser.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "filesystem.h"
+#include "tabwindow.h"
+#include "inputproperties.h"
+#include "choreowidgetdrawhelper.h"
+#include "UtlBuffer.h"
+#include "ChoreoEvent.h"
+#include "ChoreoView.h"
+
+CVCDBrowser *g_pVCDBrowser = NULL;
+
+enum
+{
+ // Controls
+ IDC_VB_LISTVIEW = 101,
+ IDC_VB_FILETREE,
+ // Messages
+ IDC_VB_OPENVCD = 1000,
+};
+
+enum
+{
+ COL_VCD = 0,
+};
+
+class CVCDList : public mxListView
+{
+public:
+ CVCDList( mxWindow *parent, int id = 0 )
+ : mxListView( parent, 0, 0, 0, 0, id )
+ {
+ // Add column headers
+ insertTextColumn( COL_VCD, 700, "VCD" );
+ }
+};
+
+class CUtlSymbolTree : public mxTreeView
+{
+public:
+ CUtlSymbolTree( mxWindow *parent, int id = 0 ) : mxTreeView( parent, 0, 0, 0, 0, id ),
+ m_Paths( 0, 0, FileTreeLessFunc )
+ {
+ }
+
+ void Clear()
+ {
+ removeAll();
+ m_Paths.RemoveAll();
+ }
+
+ void FindOrAddSubdirectory( char const *subdir )
+ {
+ FileTreePath fp;
+ Q_strcpy( fp.path, subdir );
+
+ if ( m_Paths.Find( fp ) != m_Paths.InvalidIndex() )
+ return;
+
+ m_Paths.Insert( fp );
+ }
+
+ mxTreeViewItem *FindOrAddChildItem( mxTreeViewItem *parent, char const *child )
+ {
+ mxTreeViewItem *p = getFirstChild( parent );
+ if ( !p )
+ {
+ return add( parent, child );
+ }
+
+ while ( p )
+ {
+ if ( !Q_stricmp( getLabel( p ), child ) )
+ return p;
+
+ p = getNextChild( p );
+ }
+
+ return add( parent, child );
+ }
+
+ void _PopulateTree( int pathId, char const *path )
+ {
+ char sz[ 512 ];
+ Q_strcpy( sz, path );
+ char *p = sz;
+
+ // Start at root
+ mxTreeViewItem *cur = NULL;
+
+ // Tokenize path
+ while ( p && p[0] )
+ {
+ char *slash = Q_strstr( p, "/" );
+ if ( !slash )
+ {
+ slash = Q_strstr( p, "\\" );
+ }
+
+ char *check = p;
+
+ if ( slash )
+ {
+ *slash = 0;
+
+ // see if a child of current already exists with this name
+ p = slash + 1;
+ }
+ else
+ {
+ p = NULL;
+ }
+
+ Assert( check );
+
+ cur = FindOrAddChildItem( cur, check );
+ }
+
+ setUserData( cur, (void *)pathId );
+ }
+
+ char const *GetSelectedPath( void )
+ {
+ mxTreeViewItem *tvi = getSelectedItem();
+ unsigned int id = (unsigned int)getUserData( tvi );
+
+ if ( id < 0 || id >= m_Paths.Count() )
+ {
+ Assert( 0 );
+ return "";
+ }
+ return m_Paths[ id ].path;
+ }
+
+ void PopulateTree()
+ {
+ int i;
+ for ( i = m_Paths.FirstInorder(); i != m_Paths.InvalidIndex(); i = m_Paths.NextInorder( i ) )
+ {
+ _PopulateTree( i, m_Paths[ i ].path );
+ }
+
+ mxTreeViewItem *p = getFirstChild( NULL );
+ setOpen( p, true );
+ }
+
+ struct FileTreePath
+ {
+ char path[ MAX_PATH ];
+ };
+
+ static bool FileTreeLessFunc( const FileTreePath &lhs, const FileTreePath &rhs )
+ {
+ return Q_stricmp( lhs.path, rhs.path ) < 0;
+ }
+
+ CUtlRBTree< FileTreePath, int > m_Paths;
+};
+
+#pragma optimize( "", off )
+class CVCDOptionsWindow : public mxWindow
+{
+typedef mxWindow BaseClass;
+public:
+ enum
+ {
+ IDC_OPENFILE = 1000,
+ IDC_SEARCH,
+ IDC_CANCELSEARCH,
+ };
+
+ CVCDOptionsWindow( CVCDBrowser *browser ) : BaseClass( browser, 0, 0, 0, 0 ), m_pBrowser( browser )
+ {
+ FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+
+ m_szSearchString[0]=0;
+
+ m_pOpen = new mxButton( this, 0, 0, 0, 0, "Open", IDC_OPENFILE );
+
+ m_pSearch = new mxLineEdit( this, 0, 0, 0, 0, "", IDC_SEARCH );
+
+ m_pCancelSearch = new mxButton( this, 0, 0, 0, 0, "Cancel", IDC_CANCELSEARCH );
+ }
+
+ bool PaintBackground( void )
+ {
+ redraw();
+ return false;
+ }
+
+
+ virtual void redraw()
+ {
+ CChoreoWidgetDrawHelper drawHelper( this, GetSysColor( COLOR_BTNFACE ) );
+ }
+ virtual int handleEvent( mxEvent *event )
+ {
+ int iret = 0;
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Size:
+ {
+ iret = 1;
+
+ int split = 120;
+
+ int x = 1;
+
+ m_pOpen->setBounds( x, 1, split, h2() - 2 );
+
+
+ x += split + 10;
+
+ m_pCancelSearch->setBounds( x, 1, split, h2() - 2 );
+
+ x += split + 10;
+
+ m_pSearch->setBounds( x, 0, split * 3, h2() - 1 );
+
+ x += split * 3 + 10;
+ }
+ break;
+ case mxEvent::KeyDown:
+ switch ( event->action )
+ {
+ default:
+ break;
+ case IDC_SEARCH:
+ {
+ if ( event->event == mxEvent::KeyDown )
+ {
+ OnSearch();
+ }
+ iret = 1;
+ };
+ break;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ switch ( event->action )
+ {
+ case IDC_SEARCH:
+ iret = 1;
+ break;
+ case IDC_OPENFILE:
+ {
+ iret = 1;
+ m_pBrowser->OnOpen();
+ }
+ break;
+ case IDC_CANCELSEARCH:
+ {
+ iret = 1;
+ OnCancelSearch();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ return iret;
+ }
+
+ char const *GetSearchString()
+ {
+ return m_szSearchString;
+ }
+
+ void OnSearch()
+ {
+ m_pSearch->getText( m_szSearchString, sizeof( m_szSearchString ) );
+
+ m_pBrowser->OnSearch();
+ }
+
+ void OnCancelSearch()
+ {
+ m_szSearchString[ 0 ] = 0;
+ m_pSearch->clear();
+
+ m_pBrowser->OnCancelSearch();
+ }
+
+private:
+
+ mxButton *m_pOpen;
+ mxLineEdit *m_pSearch;
+ mxButton *m_pCancelSearch;
+
+ CVCDBrowser *m_pBrowser;
+
+ char m_szSearchString[ 256 ];
+};
+
+#pragma optimize( "", on )
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CVCDBrowser::CVCDBrowser( mxWindow *parent )
+ : IFacePoserToolWindow( "VCDBrowser", "VCDs" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ SetAutoProcess( false );
+
+ m_bTextSearch = false;
+ m_nPrevProcessed = -1;
+
+ m_pListView = new CVCDList( this, IDC_VB_LISTVIEW );
+ m_pOptions = new CVCDOptionsWindow( this );
+ m_pFileTree = new CUtlSymbolTree( this, IDC_VB_FILETREE );
+
+ //HIMAGELIST list = CreateImageList();
+
+ // Associate the image list with the tree-view control.
+ //m_pListView->setImageList( (void *)list );
+
+ LoadAllSounds();
+
+ PopulateTree( NULL );
+}
+
+#define CX_ICON 16
+#define CY_ICON 16
+
+HIMAGELIST CVCDBrowser::CreateImageList()
+{
+ HIMAGELIST list;
+
+ list = ImageList_Create( CX_ICON, CY_ICON,
+ FALSE, VCD_NUM_IMAGES, 0 );
+
+ // Load the icon resources, and add the icons to the image list.
+ HICON hicon;
+ int slot;
+#if defined( DBGFLAG_ASSERT )
+ int c = 0;
+#endif
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ return list;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVCDBrowser::OnDelete()
+{
+ RemoveAllSounds();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int CVCDBrowser::handleEvent( mxEvent *event )
+{
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ {
+ iret = 0;
+ }
+ break;
+ case IDC_VB_LISTVIEW:
+ {
+ SetActiveTool( this );
+
+ bool rightmouse = ( event->flags == mxEvent::RightClicked ) ? true : false;
+ bool doubleclicked = ( event->flags == mxEvent::DoubleClicked ) ? true : false;
+
+ if ( rightmouse )
+ {
+ ShowContextMenu();
+ }
+ else if ( doubleclicked )
+ {
+ if ( m_pListView->getNumSelected() == 1 )
+ {
+ int index = m_pListView->getNextSelectedItem( -1 );
+ if ( index >= 0 )
+ {
+ FileNameHandle_t vcd = (FileNameHandle_t)m_pListView->getUserData( index, 0 );
+ OpenVCD( vcd );
+ }
+ }
+ }
+ }
+ break;
+ case IDC_VB_FILETREE:
+ {
+ SetActiveTool( this );
+
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+ }
+ break;
+ case IDC_VB_OPENVCD:
+ {
+ OnOpen();
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int optionsh = 20;
+
+ m_pOptions->setBounds( 0, 0, w2(), optionsh );
+
+ int filetreewidth = 175;
+
+ m_pFileTree->setBounds( 0, optionsh, filetreewidth, h2() - optionsh );
+ m_pListView->setBounds( filetreewidth, optionsh, w2() - filetreewidth, h2() - optionsh );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Close:
+ {
+ iret = 1;
+ }
+ break;
+ }
+
+ return iret;
+}
+
+bool CVCDBrowser::CNameLessFunc::Less( const FileNameHandle_t &name1, const FileNameHandle_t &name2, void *pContext )
+{
+ if ( name1 < name2 )
+ return true;
+ return false;
+}
+
+void CVCDBrowser::OpenVCD( const FileNameHandle_t& handle )
+{
+ char fn[ 512 ];
+ if ( filesystem->String( handle, fn, sizeof( fn ) ) )
+ {
+ char pFullPath[MAX_PATH];
+ const char *pFileName = filesystem->RelativePathToFullPath( fn, "GAME", pFullPath, sizeof(pFullPath) );
+ if ( !pFileName )
+ {
+ pFileName = fn;
+ }
+ g_pChoreoView->LoadSceneFromFile( pFileName );
+ }
+}
+
+#define SCENES_PREFIX_LEN 0
+//-----------------------------------------------------------------------------
+// Finds all .vcd files in a particular directory
+//-----------------------------------------------------------------------------
+bool CVCDBrowser::LoadVCDsFilesInDirectory( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName, int nDirectoryNameLen )
+{
+ char *pWildCard;
+ pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 );
+ Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.vcd", pDirectoryName );
+
+ if ( !filesystem )
+ {
+ return false;
+ }
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
+ while( pFileName )
+ {
+ if( !filesystem->FindIsDirectory( findHandle ) )
+ {
+ // Strip off the 'sound/' part of the name.
+ char *pFileNameWithPath;
+ int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2;
+ pFileNameWithPath = (char *)stackalloc( nAllocSize );
+ Q_snprintf( pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[ SCENES_PREFIX_LEN ], pFileName );
+ Q_strnlwr( pFileNameWithPath, nAllocSize );
+
+ FileNameHandle_t vcd;
+ vcd = filesystem->FindOrAddFileName( pFileNameWithPath );
+ soundlist.InsertNoSort( vcd );
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ m_pFileTree->FindOrAddSubdirectory( &pDirectoryName[ SCENES_PREFIX_LEN ] );
+
+ filesystem->FindClose( findHandle );
+ return true;
+}
+
+bool CVCDBrowser::InitDirectoryRecursive( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName )
+{
+ // Compute directory name length
+ int nDirectoryNameLen = Q_strlen( pDirectoryName );
+
+ if (!LoadVCDsFilesInDirectory( soundlist, pDirectoryName, nDirectoryNameLen ) )
+ return false;
+
+ char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 4 );
+ strcpy(pWildCard, pDirectoryName);
+ strcat(pWildCard, "/*.");
+ int nPathStrLen = nDirectoryNameLen + 1;
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
+ while( pFileName )
+ {
+ if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0))
+ {
+ if( filesystem->FindIsDirectory( findHandle ) )
+ {
+ int fileNameStrLen = Q_strlen( pFileName );
+ char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 );
+ memcpy( pFileNameWithPath, pWildCard, nPathStrLen );
+ pFileNameWithPath[nPathStrLen] = '\0';
+ strcat( pFileNameWithPath, pFileName );
+
+ if (!InitDirectoryRecursive( soundlist, pFileNameWithPath ))
+ return false;
+ }
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ return true;
+}
+
+void CVCDBrowser::LoadAllSounds()
+{
+ RemoveAllSounds();
+
+ Con_Printf( "Building list of all .vcds in sound/ folder\n" );
+
+ InitDirectoryRecursive( m_AllVCDs, "scenes" );
+ m_AllVCDs.RedoSort();
+
+ m_pFileTree->PopulateTree();
+}
+
+void CVCDBrowser::RemoveAllSounds()
+{
+ m_AllVCDs.Purge();
+ m_Scripts.RemoveAll();
+ m_CurrentSelection.RemoveAll();
+
+ m_pFileTree->Clear();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVCDBrowser::PopulateTree( char const *subdirectory )
+{
+ char subdir[ 512 ];
+ subdir[ 0 ] = 0;
+
+ int i;
+
+ CUtlSortVector< FileNameHandle_t, CNameLessFunc > sorted( 0, 0 );
+
+ char const *texttofind = NULL;
+
+ if ( m_bTextSearch )
+ {
+ subdirectory = NULL;
+ texttofind = GetSearchString();
+ }
+
+ int len = 0;
+ if ( subdirectory )
+ {
+ len = Q_strlen( subdirectory );
+ Q_strncpy( subdir, subdirectory, sizeof( subdir ) );
+ Q_strlower( subdir );
+ Q_FixSlashes( subdir );
+ }
+
+ int c = m_AllVCDs.Count();
+ for ( i = 0; i < c; i++ )
+ {
+ const FileNameHandle_t &vcd = m_AllVCDs[ i ];
+ char name[ 512 ];
+ if ( !filesystem->String( vcd, name, sizeof( name ) ) )
+ continue;
+
+ if ( subdirectory )
+ {
+ if ( Q_strnicmp( subdir, name, len ) )
+ continue;
+ }
+
+ if ( m_bTextSearch && texttofind )
+ {
+ if ( !Q_stristr( name, texttofind ) )
+ continue;
+ }
+
+ sorted.InsertNoSort( vcd );
+ }
+
+ sorted.RedoSort();
+
+ char prevSelectedName[ 512 ];
+ prevSelectedName[ 0 ] = 0;
+ if ( m_pListView->getNumSelected() == 1 )
+ {
+ int selectedItem = m_pListView->getNextSelectedItem( 0 );
+ if ( selectedItem >= 0 )
+ {
+ // Grab name of previously selected item
+ Q_strcpy( prevSelectedName, m_pListView->getLabel( selectedItem, 0 ) );
+ }
+ }
+
+// Repopulate tree
+ m_pListView->removeAll();
+
+ int loadcount = 0;
+
+ m_pListView->setDrawingEnabled( false );
+
+ int selectedSlot = -1;
+
+ for ( i = 0; i < sorted.Count(); ++i )
+ {
+ const FileNameHandle_t &vcd = sorted[ i ];
+ char name[ 512 ];
+ if ( !filesystem->String( vcd, name, sizeof( name ) ) )
+ continue;
+
+ int slot = m_pListView->add( name );
+ m_pListView->setUserData( slot, COL_VCD, (void *)vcd );
+
+ if ( !Q_stricmp( prevSelectedName, name ) )
+ {
+ selectedSlot = slot;
+ }
+ ++loadcount;
+ }
+
+ m_pListView->setDrawingEnabled( true );
+
+ if ( selectedSlot != -1 )
+ {
+ m_pListView->setSelected( selectedSlot, true );
+ m_pListView->scrollToItem( selectedSlot );
+ }
+}
+
+void CVCDBrowser::RepopulateTree()
+{
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+}
+
+void CVCDBrowser::BuildSelectionList( CUtlVector< FileNameHandle_t >& selected )
+{
+ selected.RemoveAll();
+
+ int idx = -1;
+ do
+ {
+ idx = m_pListView->getNextSelectedItem( idx );
+ if ( idx != -1 )
+ {
+ FileNameHandle_t vcd = (FileNameHandle_t)m_pListView->getUserData( idx, 0 );
+ selected.AddToTail( vcd );
+ }
+ } while ( idx != -1 );
+
+}
+
+void CVCDBrowser::ShowContextMenu( void )
+{
+ SetActiveTool( this );
+
+ BuildSelectionList( m_CurrentSelection );
+ if ( m_CurrentSelection.Count() <= 0 )
+ return;
+
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( m_CurrentSelection.Count() == 1 && m_CurrentSelection[ 0 ] )
+ {
+ char sz[ 512 ];
+ char name[ 512 ];
+ if ( filesystem->String( m_CurrentSelection[ 0 ], name, sizeof( name ) ) )
+ {
+ Q_snprintf( sz, sizeof( sz ), "&Open '%s'", name );
+ pop->add ( sz, IDC_VB_OPENVCD );
+ }
+ }
+
+ pop->popup( this, pt.x, pt.y );
+}
+
+void CVCDBrowser::OnOpen()
+{
+ SetActiveTool( this );
+
+ BuildSelectionList( m_CurrentSelection );
+ if ( m_CurrentSelection.Count() == 1 )
+ {
+ FileNameHandle_t& vcd = m_CurrentSelection[ 0 ];
+ OpenVCD( vcd );
+ }
+}
+
+static void SplitFileName( char const *in, char *path, int maxpath, char *filename, int maxfilename )
+{
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+
+ _splitpath( in, drive, dir, fname, ext );
+
+ if ( dir[0] )
+ {
+ Q_snprintf( path, maxpath, "\\%s", dir );
+ }
+ else
+ {
+ path[0] = 0;
+ }
+ Q_snprintf( filename, maxfilename, "%s%s", fname, ext );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *se -
+//-----------------------------------------------------------------------------
+void CVCDBrowser::JumpToItem( const FileNameHandle_t& vcd )
+{
+ SetActiveTool( this );
+
+ char path[ 256 ];
+ char filename[ 256 ];
+
+ char vcdfile[ 512 ];
+ if ( !filesystem->String( vcd, vcdfile, sizeof( vcdfile ) ) )
+ return;
+
+ SplitFileName( vcdfile, path, sizeof( path ), filename, sizeof( filename ) );
+
+ char *usepath = path + Q_strlen( "/scenes/" );
+ PopulateTree( usepath );
+
+ int idx = 0;
+ int c = m_pListView->getItemCount();
+ for ( ; idx < c; idx++ )
+ {
+ FileNameHandle_t item = (FileNameHandle_t)m_pListView->getUserData( idx, 0 );
+ if ( item == vcd )
+ {
+ break;
+ }
+ }
+
+ if ( idx < c )
+ {
+ m_pListView->scrollToItem( idx );
+ }
+}
+
+int CVCDBrowser::GetVCDCount() const
+{
+ return m_AllVCDs.Count();
+}
+
+FileNameHandle_t CVCDBrowser::GetVCD( int index )
+{
+ if ( index < 0 || index >= (int)m_AllVCDs.Count() )
+ return NULL;
+
+ return m_AllVCDs[ index ];
+}
+
+
+void CVCDBrowser::OnSearch()
+{
+ if ( !GetSearchString()[ 0 ] )
+ {
+ OnCancelSearch();
+ return;
+ }
+
+ SetActiveTool( this );
+ m_bTextSearch = true;
+ PopulateTree( GetSearchString());
+}
+
+void CVCDBrowser::OnCancelSearch()
+{
+ SetActiveTool( this );
+
+ m_bTextSearch = false;
+
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+}
+
+char const *CVCDBrowser::GetSearchString()
+{
+ return m_pOptions->GetSearchString();
+}
+
+void CVCDBrowser::SetCurrent( char const *filename )
+{
+// Get sound name and look up .vcd from it
+ char const *p = filename;
+ if ( p &&
+ ( !Q_strnicmp( p, "sound/", 6 ) || !Q_strnicmp( p, "sound\\", 6 ) ) )
+ {
+ p += 6;
+ }
+
+ char fn[ 512 ];
+ Q_strncpy( fn, p, sizeof( fn ) );
+ Q_FixSlashes( fn );
+
+ int i;
+ int c = m_pListView->getItemCount();
+
+ for ( i = 0; i < c; ++i )
+ {
+ FileNameHandle_t vcd = (FileNameHandle_t)( m_pListView->getUserData( i, COL_VCD ) );
+
+ char fixed[ 512 ];
+ if ( !filesystem->String( vcd, fixed, sizeof( fixed ) ) )
+ continue;
+
+ Q_FixSlashes( fixed );
+
+ if ( !Q_stricmp( fixed, fn ) )
+ {
+ m_pListView->scrollToItem( i );
+ m_pListView->setSelected( i, true );
+ }
+ else
+ {
+ m_pListView->setSelected( i, false );
+ }
+ }
+}
diff --git a/utils/hlfaceposer/vcdbrowser.h b/utils/hlfaceposer/vcdbrowser.h
new file mode 100644
index 0000000..d2e82ff
--- /dev/null
+++ b/utils/hlfaceposer/vcdbrowser.h
@@ -0,0 +1,120 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VCDBROWSER_H
+#define VCDBROWSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxtk/mxListView.h"
+#include "commctrl.h"
+#include "utldict.h"
+#include "faceposertoolwindow.h"
+#include "filesystem.h"
+#include "tier1/UtlSortVector.h"
+
+class CVCDList;
+class CUtlSymbolTree;
+class CVCDOptionsWindow;
+// class CChoreoEvent;
+
+struct _IMAGELIST;
+typedef struct _IMAGELIST NEAR* HIMAGELIST;
+
+enum
+{
+/// IMAGE_WORKSPACE = 0,
+// IMAGE_WORKSPACE_CHECKEDOUT,
+// IMAGE_PROJECT,
+// IMAGE_PROJECT_CHECKEDOUT,
+// IMAGE_SCENE,
+// IMAGE_SCENE_CHECKEDOUT,
+// IMAGE_VCD,
+// IMAGE_VCD_CHECKEDOUT,
+// IMAGE_WAV,
+// IMAGE_WAV_CHECKEDOUT,
+// IMAGE_SPEAK,
+// IMAGE_SPEAK_CHECKEDOUT,
+
+ VCD_NUM_IMAGES,
+};
+
+class CVCDBrowser : public mxWindow, public IFacePoserToolWindow
+{
+ typedef mxWindow BaseClass;
+public:
+
+ CVCDBrowser( mxWindow *parent );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void OnDelete();
+
+ void RepopulateTree();
+
+ void BuildSelectionList( CUtlVector< FileNameHandle_t >& selected );
+
+ void OnOpen();
+
+ void JumpToItem( const FileNameHandle_t& vcd );
+
+ int GetVCDCount() const;
+ FileNameHandle_t GetVCD( int index );
+
+ void OnSearch();
+ void OnCancelSearch();
+
+ HIMAGELIST CreateImageList();
+
+ void SetCurrent( char const *fn );
+
+private:
+
+ class CNameLessFunc
+ {
+ public:
+ bool Less( const FileNameHandle_t &name1, const FileNameHandle_t &name2, void *pContext );
+ };
+
+
+ void OpenVCD( const FileNameHandle_t& handle );
+
+ char const *GetSearchString();
+
+ bool LoadVCDsFilesInDirectory( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName, int nDirectoryNameLen );
+ bool InitDirectoryRecursive( CUtlSortVector< FileNameHandle_t, CNameLessFunc >& soundlist, char const* pDirectoryName );
+
+ void PopulateTree( char const *subdirectory );
+
+ void ShowContextMenu( void );
+
+ void LoadAllSounds();
+ void RemoveAllSounds();
+
+ CVCDList *m_pListView;
+
+ enum
+ {
+ NUM_BITMAPS = 6,
+ };
+
+ CUtlSortVector< FileNameHandle_t, CNameLessFunc > m_AllVCDs;
+ CUtlSymbolTable m_ScriptTable;
+
+ CUtlVector< CUtlSymbol > m_Scripts;
+
+ CVCDOptionsWindow *m_pOptions;
+ CUtlSymbolTree *m_pFileTree;
+
+ CUtlVector< FileNameHandle_t > m_CurrentSelection;
+
+ int m_nPrevProcessed;
+ bool m_bTextSearch;
+};
+
+extern CVCDBrowser *g_pVCDBrowser;
+
+#endif // VCDBROWSER_H
diff --git a/utils/hlfaceposer/wav1.ico b/utils/hlfaceposer/wav1.ico
new file mode 100644
index 0000000..d4ae51b
--- /dev/null
+++ b/utils/hlfaceposer/wav1.ico
Binary files differ
diff --git a/utils/hlfaceposer/wavebrowser.cpp b/utils/hlfaceposer/wavebrowser.cpp
new file mode 100644
index 0000000..c961369
--- /dev/null
+++ b/utils/hlfaceposer/wavebrowser.cpp
@@ -0,0 +1,1182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "cbase.h"
+#include <windows.h>
+#include "resource.h"
+#include "wavefile.h"
+#include "wavebrowser.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "ifaceposersound.h"
+#include "snd_wave_source.h"
+#include "filesystem.h"
+#include "tabwindow.h"
+#include "inputproperties.h"
+#include "choreowidgetdrawhelper.h"
+#include "ifileloader.h"
+#include "tier2/riff.h"
+#include "UtlBuffer.h"
+#include "ChoreoEvent.h"
+
+CWaveBrowser *g_pWaveBrowser = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the RIFF i/o interface on stdio
+//-----------------------------------------------------------------------------
+class StdIOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName )
+ {
+ return (int)filesystem->Open( pFileName, "rb" );
+ }
+
+ int read( void *pOutput, int size, int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Read( pOutput, size, (FileHandle_t)file );
+ }
+
+ void seek( int file, int pos )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+ }
+
+ unsigned int tell( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Tell( (FileHandle_t)file );
+ }
+
+ unsigned int size( int file )
+ {
+ if ( !file )
+ return 0;
+
+ return filesystem->Size( (FileHandle_t)file );
+ }
+
+ void close( int file )
+ {
+ if ( !file )
+ return;
+
+ filesystem->Close( (FileHandle_t)file );
+ }
+};
+
+class StdIOWriteBinary : public IFileWriteBinary
+{
+public:
+ int create( const char *pFileName )
+ {
+ return (int)filesystem->Open( pFileName, "wb" );
+ }
+
+ int write( void *pData, int size, int file )
+ {
+ return filesystem->Write( pData, size, (FileHandle_t)file );
+ }
+
+ void close( int file )
+ {
+ filesystem->Close( (FileHandle_t)file );
+ }
+
+ void seek( int file, int pos )
+ {
+ filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+ }
+
+ unsigned int tell( int file )
+ {
+ return filesystem->Tell( (FileHandle_t)file );
+ }
+};
+
+static StdIOReadBinary io_in;
+static StdIOWriteBinary io_out;
+
+#define RIFF_WAVE MAKEID('W','A','V','E')
+#define WAVE_FMT MAKEID('f','m','t',' ')
+#define WAVE_DATA MAKEID('d','a','t','a')
+#define WAVE_FACT MAKEID('f','a','c','t')
+#define WAVE_CUE MAKEID('c','u','e',' ')
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &walk -
+//-----------------------------------------------------------------------------
+static void SceneManager_ParseSentence( CSentence& sentence, IterateRIFF &walk )
+{
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.EnsureCapacity( walk.ChunkSize() );
+ walk.ChunkRead( buf.Base() );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );
+
+ sentence.InitFromDataChunk( buf.Base(), buf.TellPut() );
+}
+
+bool SceneManager_LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io )
+{
+ sentence.Reset();
+
+ InFileRIFF riff( wavfile, io );
+
+ // UNDONE: Don't use printf to handle errors
+ if ( riff.RIFFName() != RIFF_WAVE )
+ {
+ return false;
+ }
+
+ // set up the iterator for the whole file (root RIFF is a chunk)
+ IterateRIFF walk( riff, riff.RIFFSize() );
+
+ // This chunk must be first as it contains the wave's format
+ // break out when we've parsed it
+ bool found = false;
+ while ( walk.ChunkAvailable() && !found )
+ {
+ switch( walk.ChunkName() )
+ {
+ case WAVE_VALVEDATA:
+ {
+ found = true;
+ SceneManager_ParseSentence( sentence, walk );
+ }
+ break;
+ }
+ walk.ChunkNext();
+ }
+
+ return true;
+}
+
+bool SceneManager_LoadSentenceFromWavFile( char const *wavfile, CSentence& sentence )
+{
+ return SceneManager_LoadSentenceFromWavFileUsingIO( wavfile, sentence, io_in );
+}
+
+enum
+{
+ // Controls
+ IDC_SB_LISTVIEW = 101,
+ IDC_SB_FILETREE,
+
+ // Messages
+ IDC_SB_PLAY = 1000,
+};
+
+enum
+{
+ COL_WAV = 0,
+ COL_DUCKED,
+ COL_PHONEMES,
+ COL_SENTENCE
+};
+
+class CWaveList : public mxListView
+{
+public:
+ CWaveList( mxWindow *parent, int id = 0 )
+ : mxListView( parent, 0, 0, 0, 0, id )
+ {
+ // Add column headers
+ insertTextColumn( COL_WAV, 300, "WAV" );
+ insertTextColumn( COL_DUCKED, 50, "Ducked" );
+ insertTextColumn( COL_PHONEMES, 120, "Words [ Phonemes ]" );
+ insertTextColumn( COL_SENTENCE, 300, "Sentence Text" );
+ }
+};
+
+class CWaveFileTree : public mxTreeView
+{
+public:
+ CWaveFileTree( mxWindow *parent, int id = 0 ) : mxTreeView( parent, 0, 0, 0, 0, id ),
+ m_Paths( 0, 0, FileTreeLessFunc )
+ {
+ }
+
+ void Clear()
+ {
+ removeAll();
+ m_Paths.RemoveAll();
+ }
+
+ void FindOrAddSubdirectory( char const *subdir )
+ {
+ FileTreePath fp;
+ Q_strcpy( fp.path, subdir );
+
+ if ( m_Paths.Find( fp ) != m_Paths.InvalidIndex() )
+ return;
+
+ m_Paths.Insert( fp );
+ }
+
+ mxTreeViewItem *FindOrAddChildItem( mxTreeViewItem *parent, char const *child )
+ {
+ mxTreeViewItem *p = getFirstChild( parent );
+ if ( !p )
+ {
+ return add( parent, child );
+ }
+
+ while ( p )
+ {
+ if ( !Q_stricmp( getLabel( p ), child ) )
+ return p;
+
+ p = getNextChild( p );
+ }
+
+ return add( parent, child );
+ }
+
+ void _PopulateTree( int pathId, char const *path )
+ {
+ char sz[ 512 ];
+ Q_strcpy( sz, path );
+ char *p = sz;
+
+ // Start at root
+ mxTreeViewItem *cur = NULL;
+
+ // Tokenize path
+ while ( p && p[0] )
+ {
+ char *slash = Q_strstr( p, "/" );
+ if ( !slash )
+ {
+ slash = Q_strstr( p, "\\" );
+ }
+
+ char *check = p;
+
+ if ( slash )
+ {
+ *slash = 0;
+
+ // see if a child of current already exists with this name
+ p = slash + 1;
+ }
+ else
+ {
+ p = NULL;
+ }
+
+ Assert( check );
+
+ cur = FindOrAddChildItem( cur, check );
+ }
+
+ setUserData( cur, (void *)pathId );
+ }
+
+ char const *GetSelectedPath( void )
+ {
+ mxTreeViewItem *tvi = getSelectedItem();
+ unsigned int id = (unsigned int)getUserData( tvi );
+
+ if ( id < 0 || id >= m_Paths.Count() )
+ {
+ Assert( 0 );
+ return "";
+ }
+ return m_Paths[ id ].path;
+ }
+
+ void PopulateTree()
+ {
+ int i;
+ for ( i = m_Paths.FirstInorder(); i != m_Paths.InvalidIndex(); i = m_Paths.NextInorder( i ) )
+ {
+ _PopulateTree( i, m_Paths[ i ].path );
+ }
+
+ mxTreeViewItem *p = getFirstChild( NULL );
+ setOpen( p, true );
+ }
+
+ struct FileTreePath
+ {
+ char path[ MAX_PATH ];
+ };
+
+ static bool FileTreeLessFunc( const FileTreePath &lhs, const FileTreePath &rhs )
+ {
+ return Q_stricmp( lhs.path, rhs.path ) < 0;
+ }
+
+ CUtlRBTree< FileTreePath, int > m_Paths;
+};
+#pragma optimize( "", off )
+class CWaveOptionsWindow : public mxWindow
+{
+typedef mxWindow BaseClass;
+public:
+ enum
+ {
+ IDC_PLAYSOUND = 1000,
+ IDC_STOP_SOUNDS,
+ IDC_SEARCH,
+ IDC_CANCELSEARCH,
+ };
+
+ CWaveOptionsWindow( CWaveBrowser *browser ) : BaseClass( browser, 0, 0, 0, 0 ), m_pBrowser( browser )
+ {
+ FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+
+ m_szSearchString[0]=0;
+
+ m_pPlay = new mxButton( this, 0, 0, 0, 0, "Play", IDC_PLAYSOUND );
+
+ m_pStopSounds = new mxButton( this, 0, 0, 0, 0, "Stop Sounds", IDC_STOP_SOUNDS );
+
+ m_pSearch = new mxLineEdit( this, 0, 0, 0, 0, "", IDC_SEARCH );
+
+ m_pCancelSearch = new mxButton( this, 0, 0, 0, 0, "Cancel", IDC_CANCELSEARCH );
+ }
+
+ bool PaintBackground( void )
+ {
+ redraw();
+ return false;
+ }
+
+
+ virtual void redraw()
+ {
+ CChoreoWidgetDrawHelper drawHelper( this, GetSysColor( COLOR_BTNFACE ) );
+ }
+ virtual int handleEvent( mxEvent *event )
+ {
+ int iret = 0;
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Size:
+ {
+ iret = 1;
+
+ int split = 120;
+
+ int x = 1;
+
+ m_pPlay->setBounds( x, 1, split, h2() - 2 );
+
+ x += split + 10;
+
+ m_pStopSounds->setBounds( x, 1, split, h2()-2 );
+
+ x += split + 10;
+
+ m_pCancelSearch->setBounds( x, 1, split, h2() - 2 );
+
+ x += split + 10;
+
+ m_pSearch->setBounds( x, 0, split * 3, h2() - 1 );
+
+ x += split * 3 + 10;
+ }
+ break;
+ case mxEvent::KeyDown:
+ switch ( event->action )
+ {
+ default:
+ break;
+ case IDC_SEARCH:
+ {
+ if ( event->event == mxEvent::KeyDown )
+ {
+ OnSearch();
+ }
+ iret = 1;
+ };
+ break;
+ }
+ break;
+ case mxEvent::Action:
+ {
+ switch ( event->action )
+ {
+ case IDC_STOP_SOUNDS:
+ {
+ iret = 1;
+ sound->StopAll();
+ }
+ break;
+ case IDC_SEARCH:
+ iret = 1;
+ break;
+ case IDC_PLAYSOUND:
+ {
+ iret = 1;
+ m_pBrowser->OnPlay();
+ }
+ break;
+ case IDC_CANCELSEARCH:
+ {
+ iret = 1;
+ OnCancelSearch();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ return iret;
+ }
+
+ char const *GetSearchString()
+ {
+ return m_szSearchString;
+ }
+
+ void OnSearch()
+ {
+ m_pSearch->getText( m_szSearchString, sizeof( m_szSearchString ) );
+
+ m_pBrowser->OnSearch();
+ }
+
+ void OnCancelSearch()
+ {
+ m_szSearchString[ 0 ] = 0;
+ m_pSearch->clear();
+
+ m_pBrowser->OnCancelSearch();
+ }
+
+private:
+
+ mxButton *m_pStopSounds;
+ mxButton *m_pPlay;
+ mxLineEdit *m_pSearch;
+ mxButton *m_pCancelSearch;
+
+ CWaveBrowser *m_pBrowser;
+
+ char m_szSearchString[ 256 ];
+};
+
+#pragma optimize( "", on )
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CWaveBrowser::CWaveBrowser( mxWindow *parent )
+ : IFacePoserToolWindow( "WaveBrowser", "Waves" ), mxWindow( parent, 0, 0, 0, 0 )
+{
+ SetAutoProcess( false );
+
+ m_bTextSearch = false;
+ m_nPrevProcessed = -1;
+
+ m_pListView = new CWaveList( this, IDC_SB_LISTVIEW );
+ m_pOptions = new CWaveOptionsWindow( this );
+ m_pFileTree = new CWaveFileTree( this, IDC_SB_FILETREE );
+
+ //HIMAGELIST list = CreateImageList();
+
+ // Associate the image list with the tree-view control.
+ //m_pListView->setImageList( (void *)list );
+
+ LoadAllSounds();
+
+ PopulateTree( NULL );
+}
+
+#define CX_ICON 16
+#define CY_ICON 16
+
+HIMAGELIST CWaveBrowser::CreateImageList()
+{
+ HIMAGELIST list;
+
+ list = ImageList_Create( CX_ICON, CY_ICON,
+ FALSE, NUM_IMAGES, 0 );
+
+ // Load the icon resources, and add the icons to the image list.
+ HICON hicon;
+ int slot;
+#if defined( DBGFLAG_ASSERT )
+ int c = 0;
+#endif
+
+ /*
+ hicon = LoadIcon( GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WORKSPACE));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon( GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WORKSPACE_CHECKEDOUT));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_PROJECT));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_PROJECT_CHECKEDOUT));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SCENE));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+ */
+
+// hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SCENE_CHECKEDOUT));
+// slot = ImageList_AddIcon(list, hicon);
+// Assert( slot == c++ );
+// DeleteObject( hicon );
+
+ /*
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_VCD_CHECKEDOUT ));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+ */
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WAV));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ /*
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_WAV_CHECKEDOUT));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SPEAK));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+
+ hicon = LoadIcon(GetModuleHandle( 0 ), MAKEINTRESOURCE(IDI_SPEAK_CHECKEDOUT));
+ slot = ImageList_AddIcon(list, hicon);
+ Assert( slot == c++ );
+ DeleteObject( hicon );
+ */
+
+ return list;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWaveBrowser::OnDelete()
+{
+ RemoveAllSounds();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+// Output : int
+//-----------------------------------------------------------------------------
+int CWaveBrowser::handleEvent( mxEvent *event )
+{
+ int iret = 0;
+
+ if ( HandleToolEvent( event ) )
+ {
+ return iret;
+ }
+
+ switch ( event->event )
+ {
+ default:
+ break;
+ case mxEvent::Action:
+ {
+ iret = 1;
+ switch ( event->action )
+ {
+ default:
+ {
+ iret = 0;
+ }
+ break;
+ case IDC_SB_LISTVIEW:
+ {
+ SetActiveTool( this );
+
+ bool rightmouse = ( event->flags == mxEvent::RightClicked ) ? true : false;
+ bool doubleclicked = ( event->flags == mxEvent::DoubleClicked ) ? true : false;
+
+ if ( rightmouse )
+ {
+ ShowContextMenu();
+ }
+ else if ( doubleclicked )
+ {
+ if ( m_pListView->getNumSelected() == 1 )
+ {
+ int index = m_pListView->getNextSelectedItem( -1 );
+ if ( index >= 0 )
+ {
+ CWaveFile *wav = (CWaveFile *)m_pListView->getUserData( index, 0 );
+ if ( wav )
+ {
+ wav->Play();
+ }
+ }
+ }
+ }
+ }
+ break;
+ case IDC_SB_FILETREE:
+ {
+ SetActiveTool( this );
+
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+ }
+ break;
+ case IDC_SB_PLAY:
+ {
+ OnPlay();
+ }
+ break;
+ }
+ }
+ break;
+ case mxEvent::Size:
+ {
+ int optionsh = 20;
+
+ m_pOptions->setBounds( 0, 0, w2(), optionsh );
+
+ int filetreewidth = 175;
+
+ m_pFileTree->setBounds( 0, optionsh, filetreewidth, h2() - optionsh );
+ m_pListView->setBounds( filetreewidth, optionsh, w2() - filetreewidth, h2() - optionsh );
+
+ iret = 1;
+ }
+ break;
+ case mxEvent::Close:
+ {
+ iret = 1;
+ }
+ break;
+ }
+
+ return iret;
+}
+
+static bool NameLessFunc( CWaveFile *const& name1, CWaveFile *const& name2 )
+{
+ if ( Q_stricmp( name1->GetName(), name2->GetName() ) < 0 )
+ return true;
+ return false;
+}
+
+#define SOUND_PREFIX_LEN 6
+//-----------------------------------------------------------------------------
+// Finds all .wav files in a particular directory
+//-----------------------------------------------------------------------------
+bool CWaveBrowser::LoadWaveFilesInDirectory( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName, int nDirectoryNameLen )
+{
+ Assert( Q_strnicmp( pDirectoryName, "sound", 5 ) == 0 );
+
+ char *pWildCard;
+ pWildCard = ( char * )stackalloc( nDirectoryNameLen + 7 );
+ Q_snprintf( pWildCard, nDirectoryNameLen + 7, "%s/*.wav", pDirectoryName );
+
+ if ( !filesystem )
+ {
+ return false;
+ }
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
+ while( pFileName )
+ {
+ if( !filesystem->FindIsDirectory( findHandle ) )
+ {
+ // Strip off the 'sound/' part of the name.
+ char *pFileNameWithPath;
+ int nAllocSize = nDirectoryNameLen + Q_strlen(pFileName) + 2;
+ pFileNameWithPath = (char *)stackalloc( nAllocSize );
+ Q_snprintf( pFileNameWithPath, nAllocSize, "%s/%s", &pDirectoryName[SOUND_PREFIX_LEN], pFileName );
+ Q_strnlwr( pFileNameWithPath, nAllocSize );
+
+ CWaveFile *wav = new CWaveFile( pFileNameWithPath );
+ soundlist.Insert( pFileNameWithPath, wav );
+
+ /*
+ if ( !(soundlist.Count() % 500 ) )
+ {
+ Con_Printf( "CWaveBrowser: loaded %i sounds\n", soundlist.Count() );
+ }
+ */
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ m_pFileTree->FindOrAddSubdirectory( &pDirectoryName[ SOUND_PREFIX_LEN ] );
+
+ filesystem->FindClose( findHandle );
+ return true;
+}
+
+bool CWaveBrowser::InitDirectoryRecursive( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName )
+{
+ // Compute directory name length
+ int nDirectoryNameLen = Q_strlen( pDirectoryName );
+
+ if (!LoadWaveFilesInDirectory( soundlist, pDirectoryName, nDirectoryNameLen ) )
+ return false;
+
+ char *pWildCard = ( char * )stackalloc( nDirectoryNameLen + 4 );
+ strcpy(pWildCard, pDirectoryName);
+ strcat(pWildCard, "/*.");
+ int nPathStrLen = nDirectoryNameLen + 1;
+
+ FileFindHandle_t findHandle;
+ const char *pFileName = filesystem->FindFirst( pWildCard, &findHandle );
+ while( pFileName )
+ {
+ if ((pFileName[0] != '.') || (pFileName[1] != '.' && pFileName[1] != 0))
+ {
+ if( filesystem->FindIsDirectory( findHandle ) )
+ {
+ int fileNameStrLen = Q_strlen( pFileName );
+ char *pFileNameWithPath = ( char * )stackalloc( nPathStrLen + fileNameStrLen + 1 );
+ memcpy( pFileNameWithPath, pWildCard, nPathStrLen );
+ pFileNameWithPath[nPathStrLen] = '\0';
+ strcat( pFileNameWithPath, pFileName );
+
+ if (!InitDirectoryRecursive( soundlist, pFileNameWithPath ))
+ return false;
+ }
+ }
+ pFileName = filesystem->FindNext( findHandle );
+ }
+
+ return true;
+}
+
+void CWaveBrowser::LoadAllSounds()
+{
+ RemoveAllSounds();
+
+ Con_Printf( "Building list of all .wavs in sound/ folder\n" );
+
+ InitDirectoryRecursive( m_AllSounds, "sound" );
+
+ m_pFileTree->PopulateTree();
+}
+
+void CWaveBrowser::RemoveAllSounds()
+{
+ int c = m_AllSounds.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CWaveFile *wav = m_AllSounds[ i ];
+ delete wav;
+ }
+
+ m_AllSounds.RemoveAll();
+ m_Scripts.RemoveAll();
+ m_CurrentSelection.RemoveAll();
+
+ m_pFileTree->Clear();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWaveBrowser::PopulateTree( char const *subdirectory )
+{
+ int i;
+
+ CUtlRBTree< CWaveFile *, int > m_Sorted( 0, 0, NameLessFunc );
+
+ bool check_load_sentence_data = false;
+
+ char const *texttofind = NULL;
+
+ if ( m_bTextSearch )
+ {
+ subdirectory = NULL;
+ texttofind = GetSearchString();
+ }
+
+ int len = 0;
+ if ( subdirectory )
+ {
+ len = Q_strlen( subdirectory );
+ check_load_sentence_data = ( Q_strstr( subdirectory, "/" ) || subdirectory[0] ) ? true : false;
+ }
+
+ int c = m_AllSounds.Count();
+ for ( i = 0; i < c; i++ )
+ {
+ CWaveFile *wav = m_AllSounds[ i ];
+ char const *name = wav->GetName();
+
+ if ( subdirectory )
+ {
+ if ( Q_strnicmp( subdirectory, wav->GetName(), len ) )
+ continue;
+ }
+
+ if ( m_bTextSearch && texttofind )
+ {
+ if ( !Q_stristr( name, texttofind ) )
+ continue;
+ }
+
+ m_Sorted.Insert( wav );
+ }
+
+ char prevSelectedName[ 512 ];
+ prevSelectedName[ 0 ] = 0;
+ if ( m_pListView->getNumSelected() == 1 )
+ {
+ int selectedItem = m_pListView->getNextSelectedItem( 0 );
+ if ( selectedItem >= 0 )
+ {
+ // Grab wave name of previously selected item
+ Q_strcpy( prevSelectedName, m_pListView->getLabel( selectedItem, 0 ) );
+ }
+ }
+
+// Repopulate tree
+ m_pListView->removeAll();
+
+ int loadcount = 0;
+
+ m_pListView->setDrawingEnabled( false );
+
+ int selectedSlot = -1;
+
+ CUtlVector< CWaveFile * > list;
+
+
+ for ( i = m_Sorted.FirstInorder(); i != m_Sorted.InvalidIndex(); i = m_Sorted.NextInorder( i ) )
+ {
+ CWaveFile *wav = m_Sorted[ i ];
+ char const *name = wav->GetName();
+
+ int slot = m_pListView->add( name );
+
+ if ( !Q_stricmp( prevSelectedName, name ) )
+ {
+ selectedSlot = slot;
+ }
+
+ if ( ( check_load_sentence_data || m_bTextSearch ) &&
+ !wav->HasLoadedSentenceInfo() && !wav->IsAsyncLoading() )
+ {
+ wav->SetAsyncLoading( true );
+ list.AddToTail( wav );
+ }
+
+ // m_pListView->setImage( slot, COL_WAV, wav->GetIconIndex() );
+ m_pListView->setUserData( slot, COL_WAV, (void *)wav );
+
+ if ( wav->HasLoadedSentenceInfo() )
+ {
+ m_pListView->setLabel( slot, COL_DUCKED, wav->GetVoiceDuck() ? "yes" : "no" );
+ m_pListView->setLabel( slot, COL_PHONEMES, wav->GetPhonemeCount() || wav->GetWordCount() ? va( "%i [ %i ]", wav->GetWordCount(), wav->GetPhonemeCount() ) : "" );
+ m_pListView->setLabel( slot, COL_SENTENCE, wav->GetSentenceText() );
+ }
+ else
+ {
+ m_pListView->setLabel( slot, COL_SENTENCE, "(loading...)" );
+ }
+
+ ++loadcount;
+ }
+
+ m_pListView->setDrawingEnabled( true );
+
+ if ( selectedSlot != -1 )
+ {
+ m_pListView->setSelected( selectedSlot, true );
+ m_pListView->scrollToItem( selectedSlot );
+ }
+
+ if ( list.Count() > 0 )
+ {
+ fileloader->AddWaveFilesToThread( list );
+ }
+
+ // Con_Printf( "CWaveBrowser: selected %i sounds\n", loadcount );
+}
+
+void CWaveBrowser::RepopulateTree()
+{
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+}
+
+void CWaveBrowser::BuildSelectionList( CUtlVector< CWaveFile * >& selected )
+{
+ selected.RemoveAll();
+
+ int idx = -1;
+ do
+ {
+ idx = m_pListView->getNextSelectedItem( idx );
+ if ( idx != -1 )
+ {
+ CWaveFile *wav = (CWaveFile *)m_pListView->getUserData( idx, 0 );
+ if ( wav )
+ {
+ selected.AddToTail( wav );
+ }
+ }
+ } while ( idx != -1 );
+
+}
+
+void CWaveBrowser::ShowContextMenu( void )
+{
+ SetActiveTool( this );
+
+ BuildSelectionList( m_CurrentSelection );
+ if ( m_CurrentSelection.Count() <= 0 )
+ return;
+
+ POINT pt;
+ GetCursorPos( &pt );
+ ScreenToClient( (HWND)getHandle(), &pt );
+
+ // New scene, edit comments
+ mxPopupMenu *pop = new mxPopupMenu();
+
+ if ( m_CurrentSelection.Count() == 1 )
+ {
+ pop->add ("&Play", IDC_SB_PLAY );
+// pop->addSeparator();
+ }
+
+// pop->add( "Import Sentence Data", IDC_SB_IMPORTSENTENCE );
+// pop->add( "Export Sentence Data", IDC_SB_EXPORTSENTENCE );
+
+ pop->popup( this, pt.x, pt.y );
+}
+
+void CWaveBrowser::OnPlay()
+{
+ SetActiveTool( this );
+
+ BuildSelectionList( m_CurrentSelection );
+ if ( m_CurrentSelection.Count() == 1 )
+ {
+ CWaveFile *wav = m_CurrentSelection[ 0 ];
+ if ( wav )
+ {
+ wav->Play();
+ }
+ }
+}
+
+static void SplitFileName( char const *in, char *path, int maxpath, char *filename, int maxfilename )
+{
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+
+ _splitpath( in, drive, dir, fname, ext );
+
+ if ( dir[0] )
+ {
+ Q_snprintf( path, maxpath, "\\%s", dir );
+ }
+ else
+ {
+ path[0] = 0;
+ }
+ Q_snprintf( filename, maxfilename, "%s%s", fname, ext );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *se -
+//-----------------------------------------------------------------------------
+void CWaveBrowser::JumpToItem( CWaveFile *wav )
+{
+
+ SetActiveTool( this );
+
+ char path[ 256 ];
+ char filename[ 256 ];
+
+ SplitFileName( wav->GetFileName(), path, sizeof( path ), filename, sizeof( filename ) );
+
+ char *usepath = path + Q_strlen( "/sound/" );
+ PopulateTree( usepath );
+
+ int idx = 0;
+ int c = m_pListView->getItemCount();
+ for ( ; idx < c; idx++ )
+ {
+ CWaveFile *item = (CWaveFile *)m_pListView->getUserData( idx, 0 );
+ if ( !Q_stricmp( item->GetFileName(), wav->GetFileName() ) )
+ {
+ break;
+ }
+ }
+
+ if ( idx < c )
+ {
+ m_pListView->scrollToItem( idx );
+ }
+}
+
+CWaveFile *CWaveBrowser::FindEntry( char const *wavname, bool jump /*= false*/ )
+{
+ int idx = m_AllSounds.Find( wavname );
+ if ( idx != m_AllSounds.InvalidIndex() )
+ {
+ CWaveFile *wav = m_AllSounds[ idx ];
+ if ( jump )
+ {
+ JumpToItem( wav );
+ }
+
+ return wav;
+ }
+
+ return NULL;
+}
+
+int CWaveBrowser::GetSoundCount() const
+{
+ return m_AllSounds.Count();
+}
+
+CWaveFile *CWaveBrowser::GetSound( int index )
+{
+ if ( index < 0 || index >= (int)m_AllSounds.Count() )
+ return NULL;
+
+ return m_AllSounds[ index ];
+}
+
+
+void CWaveBrowser::OnSearch()
+{
+ SetActiveTool( this );
+
+ m_bTextSearch = true;
+
+ PopulateTree( GetSearchString());
+}
+
+void CWaveBrowser::OnCancelSearch()
+{
+ SetActiveTool( this );
+
+ m_bTextSearch = false;
+
+ PopulateTree( m_pFileTree->GetSelectedPath() );
+}
+
+char const *CWaveBrowser::GetSearchString()
+{
+ return m_pOptions->GetSearchString();
+}
+
+void CWaveBrowser::Think( float dt )
+{
+ int pending = fileloader->GetPendingLoadCount();
+ if ( pending != m_nPrevProcessed )
+ {
+ m_nPrevProcessed = pending;
+
+ // Put into suffix of window title
+ if ( pending == 0 )
+ {
+ SetSuffix( "" );
+ }
+ else
+ {
+ SetSuffix( va( " - %i", pending ) );
+ }
+ }
+
+ int c = fileloader->ProcessCompleted();
+ if ( c > 0 )
+ {
+ RepopulateTree();
+ }
+}
+
+void CWaveBrowser::SetEvent( CChoreoEvent *event )
+{
+ if ( event->GetType() != CChoreoEvent::SPEAK )
+ return;
+
+ SetCurrent( FacePoser_TranslateSoundName( event->GetParameters() ) );
+}
+
+void CWaveBrowser::SetCurrent( char const *filename )
+{
+// Get sound name and look up .wav from it
+ char const *p = filename;
+ if ( p &&
+ ( !Q_strnicmp( p, "sound/", 6 ) || !Q_strnicmp( p, "sound\\", 6 ) ) )
+ {
+ p += 6;
+ }
+
+ char fn[ 512 ];
+ Q_strncpy( fn, p, sizeof( fn ) );
+ Q_FixSlashes( fn );
+
+ int i;
+ int c = m_pListView->getItemCount();
+
+ for ( i = 0; i < c; ++i )
+ {
+ CWaveFile *wav = reinterpret_cast< CWaveFile * >( m_pListView->getUserData( i, COL_WAV ) );
+ if ( !wav )
+ continue;
+
+ char fixed[ 512 ];
+ Q_strncpy( fixed, wav->GetName(), sizeof( fixed ) );
+ Q_FixSlashes( fixed );
+
+ if ( !Q_stricmp( fixed, fn ) )
+ {
+ m_pListView->scrollToItem( i );
+ m_pListView->setSelected( i, true );
+ }
+ else
+ {
+ m_pListView->setSelected( i, false );
+ }
+ }
+}
diff --git a/utils/hlfaceposer/wavebrowser.h b/utils/hlfaceposer/wavebrowser.h
new file mode 100644
index 0000000..7959428
--- /dev/null
+++ b/utils/hlfaceposer/wavebrowser.h
@@ -0,0 +1,125 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WAVEBROWSER_H
+#define WAVEBROWSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mxtk/mxListView.h"
+#include "commctrl.h"
+#include "utldict.h"
+#include "faceposertoolwindow.h"
+
+class CWaveFile;
+class CWaveList;
+class CWaveFileTree;
+class CWaveOptionsWindow;
+class CChoreoEvent;
+
+struct _IMAGELIST;
+typedef struct _IMAGELIST NEAR* HIMAGELIST;
+
+enum
+{
+/// IMAGE_WORKSPACE = 0,
+// IMAGE_WORKSPACE_CHECKEDOUT,
+// IMAGE_PROJECT,
+// IMAGE_PROJECT_CHECKEDOUT,
+// IMAGE_SCENE,
+// IMAGE_SCENE_CHECKEDOUT,
+// IMAGE_VCD,
+// IMAGE_VCD_CHECKEDOUT,
+// IMAGE_WAV,
+// IMAGE_WAV_CHECKEDOUT,
+// IMAGE_SPEAK,
+// IMAGE_SPEAK_CHECKEDOUT,
+
+ NUM_IMAGES,
+};
+
+class CWaveBrowser : public mxWindow, public IFacePoserToolWindow
+{
+ typedef mxWindow BaseClass;
+public:
+
+ CWaveBrowser( mxWindow *parent );
+
+ virtual void Think( float dt );
+
+ virtual int handleEvent( mxEvent *event );
+ virtual void OnDelete();
+
+ void RepopulateTree();
+
+ void BuildSelectionList( CUtlVector< CWaveFile * >& selected );
+
+ void OnPlay();
+
+ void JumpToItem( CWaveFile *wav );
+
+ CWaveFile *FindEntry( char const *wavname, bool jump = false );
+
+
+ int GetSoundCount() const;
+ CWaveFile *GetSound( int index );
+
+ void OnSearch();
+ void OnCancelSearch();
+
+ HIMAGELIST CreateImageList();
+
+ void SetEvent( CChoreoEvent *event );
+ void SetCurrent( char const *fn );
+
+private:
+
+ char const *GetSearchString();
+
+ bool LoadWaveFilesInDirectory( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName, int nDirectoryNameLen );
+ bool InitDirectoryRecursive( CUtlDict< CWaveFile *, int >& soundlist, char const* pDirectoryName );
+
+ void OnWaveProperties();
+ void OnEnableVoiceDucking();
+ void OnDisableVoiceDucking();
+// void OnCheckout();
+// void OnCheckin();
+
+ void OnImportSentence();
+ void OnExportSentence();
+
+ void PopulateTree( char const *subdirectory );
+
+ void ShowContextMenu( void );
+
+ void LoadAllSounds();
+ void RemoveAllSounds();
+
+ CWaveList *m_pListView;
+
+ enum
+ {
+ NUM_BITMAPS = 6,
+ };
+
+ CUtlDict< CWaveFile *, int > m_AllSounds;
+ CUtlSymbolTable m_ScriptTable;
+
+ CUtlVector< CUtlSymbol > m_Scripts;
+
+ CWaveOptionsWindow *m_pOptions;
+ CWaveFileTree *m_pFileTree;
+
+ CUtlVector< CWaveFile * > m_CurrentSelection;
+
+ int m_nPrevProcessed;
+ bool m_bTextSearch;
+};
+
+extern CWaveBrowser *g_pWaveBrowser;
+
+#endif // WAVEBROWSER_H
diff --git a/utils/hlfaceposer/wavefile.cpp b/utils/hlfaceposer/wavefile.cpp
new file mode 100644
index 0000000..83cd418
--- /dev/null
+++ b/utils/hlfaceposer/wavefile.cpp
@@ -0,0 +1,132 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "wavefile.h"
+#include "wavebrowser.h"
+#include "sentence.h"
+#include "ifaceposersound.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "snd_wave_source.h"
+#include "filesystem.h"
+#include "UtlBuffer.h"
+#include "phonemeeditor.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+CWaveFile::CWaveFile( char const *filename )
+{
+ m_bAsyncLoading = false;
+
+ m_bSentenceLoaded = false;
+
+ m_Sentence.Reset();
+
+ Q_strncpy( m_szName, filename, sizeof( m_szName ) );
+
+ Q_snprintf( m_szFileName, sizeof( m_szFileName ), "sound/%s", filename );
+}
+
+CWaveFile::~CWaveFile()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CWaveFile::GetLanguageId()
+{
+ return GetCloseCaptionLanguageId();
+}
+
+bool SceneManager_LoadSentenceFromWavFile( char const *wavfile, CSentence& sentence );
+
+void CWaveFile::EnsureSentence()
+{
+ if ( m_bSentenceLoaded )
+ return;
+
+ m_bSentenceLoaded = true;
+
+ if ( m_szFileName[ 0 ] )
+ {
+ SceneManager_LoadSentenceFromWavFile( m_szFileName, m_Sentence );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWaveFile::HasLoadedSentenceInfo() const
+{
+ return m_bSentenceLoaded;
+}
+
+char const *CWaveFile::GetName() const
+{
+ return m_szName;
+}
+
+char const *CWaveFile::GetFileName() const
+{
+ return m_szFileName;
+}
+
+char const *CWaveFile::GetSentenceText()
+{
+ EnsureSentence();
+ return m_Sentence.GetText();
+}
+
+int CWaveFile::GetPhonemeCount()
+{
+ EnsureSentence();
+ return m_Sentence.CountPhonemes();
+}
+
+int CWaveFile::GetWordCount()
+{
+ EnsureSentence();
+ return m_Sentence.m_Words.Count();
+}
+
+
+void CWaveFile::Play()
+{
+ Con_Printf( "Playing '%s' : '%s'\n", GetFileName(), GetSentenceText() );
+
+ g_pPhonemeEditor->SetCurrentWaveFile( GetFileName() );
+ g_pPhonemeEditor->Play();
+}
+
+
+bool CWaveFile::GetVoiceDuck()
+{
+ EnsureSentence();
+ return m_Sentence.GetVoiceDuck();
+}
+
+int CWaveFile::GetIconIndex() const
+{
+ return 0; // IMAGE_WAV;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : sentence -
+//-----------------------------------------------------------------------------
+void CWaveFile::SetThreadLoadedSentence( CSentence& sentence )
+{
+ if ( m_bSentenceLoaded )
+ return;
+
+ m_bSentenceLoaded = true;
+ m_Sentence = sentence;
+} \ No newline at end of file
diff --git a/utils/hlfaceposer/wavefile.h b/utils/hlfaceposer/wavefile.h
new file mode 100644
index 0000000..482e769
--- /dev/null
+++ b/utils/hlfaceposer/wavefile.h
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WAVEFILE_H
+#define WAVEFILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "sentence.h"
+
+class CAudioSource;
+
+class CWaveFile
+{
+public:
+ // One or both may be valid
+ CWaveFile( char const *filename );
+
+ ~CWaveFile();
+
+ static int GetLanguageId();
+
+ char const *GetName() const;
+ char const *GetFileName() const;
+
+ char const *GetSentenceText();
+
+ int GetPhonemeCount();
+ int GetWordCount();
+
+ bool IsAsyncLoading() const { return m_bAsyncLoading; }
+ void SetAsyncLoading( bool async ) { m_bAsyncLoading = async; }
+
+ bool HasLoadedSentenceInfo() const;
+ void EnsureSentence();
+
+ void Play();
+
+
+ bool GetVoiceDuck();
+ /*
+ void SetVoiceDuck( bool duck );
+ void ToggleVoiceDucking();
+
+ virtual void Checkout( bool updatestateicons = true );
+ virtual void Checkin( bool updatestateicons = true );
+
+ bool IsCheckedOut() const;
+ */
+
+ int GetIconIndex() const;
+
+ void SetThreadLoadedSentence( CSentence& sentence );
+
+// void ExportValveDataChunk( char const *tempfile );
+// void ImportValveDataChunk( char const *tempfile );
+
+// void GetPhonemeExportFile( char *path, int maxlen );
+
+private:
+
+ CSentence m_Sentence;
+
+ enum
+ {
+ MAX_SOUND_NAME = 256,
+ MAX_SCRIPT_FILE = 64,
+ MAX_SOUND_FILENAME = 128,
+ };
+
+ char m_szName[ MAX_SOUND_FILENAME ];
+ char m_szFileName[ MAX_SOUND_FILENAME ];
+
+// CVCDFile *m_pOwner;
+// CSoundEntry *m_pOwnerSE;
+
+ bool m_bSentenceLoaded;
+ bool m_bAsyncLoading;
+};
+
+#endif // WAVEFILE_H
diff --git a/utils/hlfaceposer/workspac.ico b/utils/hlfaceposer/workspac.ico
new file mode 100644
index 0000000..b3e4e4a
--- /dev/null
+++ b/utils/hlfaceposer/workspac.ico
Binary files differ