summaryrefslogtreecommitdiff
path: root/vgui2/dme_controls/BaseAnimationSetEditor.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vgui2/dme_controls/BaseAnimationSetEditor.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vgui2/dme_controls/BaseAnimationSetEditor.cpp')
-rw-r--r--vgui2/dme_controls/BaseAnimationSetEditor.cpp1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/vgui2/dme_controls/BaseAnimationSetEditor.cpp b/vgui2/dme_controls/BaseAnimationSetEditor.cpp
new file mode 100644
index 0000000..80151ef
--- /dev/null
+++ b/vgui2/dme_controls/BaseAnimationSetEditor.cpp
@@ -0,0 +1,1076 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dme_controls/BaseAnimationSetEditor.h"
+#include "tier1/KeyValues.h"
+#include "tier2/fileutils.h"
+#include "vgui_controls/Splitter.h"
+#include "vgui_controls/Menu.h"
+#include "vgui_controls/Label.h"
+#include "vgui_controls/ToggleButton.h"
+#include "vgui_controls/ComboBox.h"
+#include "vgui_controls/FileOpenDialog.h"
+#include "vgui_controls/MessageBox.h"
+#include "vgui_controls/perforcefilelistframe.h"
+#include "studio.h"
+#include "dme_controls/BaseAnimSetAttributeSliderPanel.h"
+#include "dme_controls/BaseAnimSetPresetFaderPanel.h"
+#include "dme_controls/BaseAnimSetControlGroupPanel.h"
+#include "dme_controls/dmecontrols_utils.h"
+#include "dme_controls/dmepicker.h"
+
+#include "sfmobjects/exportfacialanimation.h"
+
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmeanimationset.h"
+#include "movieobjects/dmeclip.h"
+#include "movieobjects/dmeanimationlist.h"
+#include "movieobjects/dmegamemodel.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+#define ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT 38
+#define ANIMATION_SET_EDITOR_BUTTONTRAY_YPOS 12
+#define ANIMATION_SET_BUTTON_INSET 0
+
+struct AnimSetLayout_t
+{
+ CBaseAnimationSetEditor::EAnimSetLayout_t type;
+ const char *shortname;
+ const char *contextmenulabel;
+};
+
+static AnimSetLayout_t g_AnimSetLayout[] =
+{
+ { CBaseAnimationSetEditor::LAYOUT_SPLIT, "split", "#BxAnimSetSplitLayout" },
+ { CBaseAnimationSetEditor::LAYOUT_VERTICAL, "vertical", "#BxAnimSetVerticalLayout" },
+ { CBaseAnimationSetEditor::LAYOUT_HORIZONTAL, "horizontal", "#BxAnimSetHorizontalLayout" },
+};
+
+static const char *NameForLayout( CBaseAnimationSetEditor::EAnimSetLayout_t layout, bool menu )
+{
+ int c = ARRAYSIZE( g_AnimSetLayout );
+ for ( int i = 0; i < c; ++i )
+ {
+ const AnimSetLayout_t& data = g_AnimSetLayout[ i ];
+ if ( data.type == layout )
+ {
+ return menu ? data.contextmenulabel : data.shortname;
+ }
+ }
+ Assert( 0 );
+ return menu ? g_AnimSetLayout[ 0 ].contextmenulabel : g_AnimSetLayout[ 0 ].shortname;
+}
+
+static CBaseAnimationSetEditor::EAnimSetLayout_t LayoutForName( const char *name )
+{
+ int c = ARRAYSIZE( g_AnimSetLayout );
+ for ( int i = 0; i < c; ++i )
+ {
+ const AnimSetLayout_t& data = g_AnimSetLayout[ i ];
+ if ( !Q_stricmp( data.shortname, name ) )
+ {
+ return data.type;
+ }
+ }
+
+ Assert( 0 );
+ return CBaseAnimationSetEditor::LAYOUT_SPLIT;
+}
+
+CBaseAnimationSetEditor::CBaseAnimationSetEditor( vgui::Panel *parent, const char *className, bool bShowGroups ) :
+ BaseClass( parent, className ),
+ m_Layout( LAYOUT_SPLIT ),
+ m_Images( false )
+{
+ const char *modes[] =
+ {
+ "AS_OFF",
+ "AS_PREVIEW",
+ "AS_RECORD",
+ "AS_PLAYBACK",
+ };
+
+ const char *imagefiles[] =
+ {
+ "tools/ifm/icon_recordingmode_off",
+ "tools/ifm/icon_recordingmode_preview",
+ "tools/ifm/icon_recordingmode_record",
+ "tools/ifm/icon_recordingmode_playback",
+ };
+
+ int i;
+ for ( i = 0 ; i < NUM_AS_RECORDING_STATES; ++i )
+ {
+ m_pState[ i ] = new ToggleButton( this, modes[ i ], "" );
+ m_pState[ i ]->SetContentAlignment( Label::a_center );
+ m_pState[ i ]->AddActionSignalTarget( this );
+ m_pState[ i ]->SetVisible( bShowGroups );
+ m_pState[ i ]->SetKeyBoardInputEnabled( false );
+ }
+
+ m_pSelectionModeType = new ToggleButton( this, "AnimSetSelectionModeType", "" );
+ m_pSelectionModeType->SetContentAlignment( Label::a_center );
+ m_pSelectionModeType->AddActionSignalTarget( this );
+ m_pSelectionModeType->SetSelected( false );
+ m_pSelectionModeType->SetKeyBoardInputEnabled( false );
+
+ m_pComboBox = new ComboBox( this, "AnimationSets", 10, false );
+
+ // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_lock", false ) );
+ // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_eyedropper", false ) );
+ // m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_selectionmodeactive", false ) );
+ m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_selectionmodeattached", false ) );
+ for ( i = 0; i < NUM_AS_RECORDING_STATES; ++i )
+ {
+ m_Images.AddImage( scheme()->GetImage( imagefiles[ i ], false ) );
+ }
+
+ int w, h;
+ m_Images.GetImage( 1 )->GetContentSize( w, h );
+ m_Images.GetImage( 1 )->SetSize( w, h );
+ m_Images.GetImage( 2 )->GetContentSize( w, h );
+ m_Images.GetImage( 2 )->SetSize( w, h );
+
+ // SETUP_PANEL( this );
+
+ PostMessage( GetVPanel(), new KeyValues( "OnChangeLayout", "value", m_Layout ) );
+ PostMessage( GetVPanel(), new KeyValues( "PopulateAnimationSetsChoice" ) );
+
+ m_pSelectionModeType->SetVisible( bShowGroups );
+ m_pComboBox->SetVisible( bShowGroups );
+
+ SetRecordingState( bShowGroups ? AS_PLAYBACK : AS_PREVIEW, true );
+
+ m_hFileOpenStateMachine = new vgui::FileOpenStateMachine( this, this );
+ m_hFileOpenStateMachine->AddActionSignalTarget( this );
+}
+
+CBaseAnimationSetEditor::~CBaseAnimationSetEditor()
+{
+}
+
+void CBaseAnimationSetEditor::CreateToolsSubPanels()
+{
+ m_hControlGroup = new CBaseAnimSetControlGroupPanel( (Panel *)NULL, "AnimSetControlGroup", this );
+ m_hPresetFader = new CBaseAnimSetPresetFaderPanel( (Panel *)NULL, "AnimSetPresetFader", this );
+ m_hAttributeSlider = new CBaseAnimSetAttributeSliderPanel( (Panel *)NULL, "AnimSetAttributeSliderPanel", this );
+}
+
+void CBaseAnimationSetEditor::OnButtonToggled( KeyValues *params )
+{
+ Panel *ptr = reinterpret_cast< Panel * >( params->GetPtr( "panel" ) );
+ /*
+
+ if ( ptr == m_pSelectionModeType )
+ {
+ // FIXME, could do this with MESSAGE_FUNC_PARAMS and look up "panel" ptr and compare to figure out which button was manipulated...
+ g_pMovieMaker->SetTimeSelectionModeType( !m_pSelectionModeType->IsSelected() ? CIFMTool::MODE_DETACHED : CIFMTool::MODE_ATTACHED );
+ }
+ else
+ */
+ {
+ for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i )
+ {
+ if ( ptr == m_pState[ i ] )
+ {
+ SetRecordingState( (RecordingState_t)i, true );
+ break;
+ }
+ }
+ }
+}
+
+void CBaseAnimationSetEditor::ChangeLayout( EAnimSetLayout_t newLayout )
+{
+ int i;
+
+ m_Layout = newLayout;
+
+ // Make sure these don't get blown away...
+ m_hControlGroup->SetParent( (Panel *)NULL );
+ m_hPresetFader->SetParent( (Panel *)NULL );
+ m_hAttributeSlider->SetParent( (Panel *)NULL );
+
+ delete m_Splitter.Get();
+ m_Splitter = NULL;
+
+ CUtlVector< Panel * > list;
+ list.AddToTail( m_hControlGroup.Get() );
+ list.AddToTail( m_hPresetFader.Get() );
+ list.AddToTail( m_hAttributeSlider.Get() );
+
+ Splitter *sub = NULL;
+
+ switch ( m_Layout )
+ {
+ default:
+ case LAYOUT_SPLIT:
+ {
+ m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_VERTICAL, 1 );
+ m_Splitter->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT,
+ 0, 0
+ );
+ m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT );
+ m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) );
+
+ // m_Splitter->EnableBorders( false );
+
+ m_hControlGroup->SetParent( m_Splitter->GetChild( 0 ) );
+ m_hControlGroup->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+
+ sub = new Splitter( m_Splitter->GetChild( 1 ), "AnimSetEditorSubSplitter", SPLITTER_MODE_HORIZONTAL, 1 );
+ sub->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+
+ m_hPresetFader->SetParent( sub->GetChild( 0 ) );
+ m_hPresetFader->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+ m_hAttributeSlider->SetParent( sub->GetChild( 1 ) );
+ m_hAttributeSlider->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+ }
+ break;
+ case LAYOUT_VERTICAL:
+ {
+ m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_VERTICAL, 2 );
+ m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) );
+ m_Splitter->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT,
+ 0, 0
+ );
+ m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT );
+
+ for ( i = 0; i < list.Count(); ++i )
+ {
+ list[ i ]->SetParent( m_Splitter->GetChild( i ) );
+ list[ i ]->SetSize( m_Splitter->GetChild( i )->GetWide(), m_Splitter->GetChild( i )->GetTall() );
+ list[ i ]->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+ }
+
+ m_Splitter->EvenlyRespaceSplitters();
+ }
+ break;
+ case LAYOUT_HORIZONTAL:
+ {
+ m_Splitter = new Splitter( this, "AnimSetEditorMainSplitter", SPLITTER_MODE_HORIZONTAL, 2 );
+ m_Splitter->SetSplitterColor( Color(32, 32, 32, 255) );
+ m_Splitter->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT,
+ 0, 0
+ );
+
+ m_Splitter->SetBounds( 0, ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT, GetWide(), GetTall() - ANIMATION_SET_EDITOR_BUTTONTRAY_HEIGHT );
+
+ for ( i = 0; i < list.Count(); ++i )
+ {
+ list[ i ]->SetParent( m_Splitter->GetChild( i ) );
+ list[ i ]->SetSize( m_Splitter->GetChild( i )->GetWide(), m_Splitter->GetChild( i )->GetTall() );
+ list[ i ]->SetAutoResize
+ (
+ Panel::PIN_TOPLEFT,
+ Panel::AUTORESIZE_DOWNANDRIGHT,
+ 0, 0,
+ 0, 0
+ );
+ }
+
+ m_Splitter->EvenlyRespaceSplitters();
+ }
+ break;
+ }
+
+ if ( sub )
+ {
+ sub->OnSizeChanged( sub->GetWide(), sub->GetTall() );
+ sub->EvenlyRespaceSplitters();
+ }
+}
+
+void CBaseAnimationSetEditor::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int w, h;
+ GetSize( w, h );
+
+ int ypos = ANIMATION_SET_EDITOR_BUTTONTRAY_YPOS;
+
+ int xpos = w - 25;
+ m_pSelectionModeType->SetBounds( xpos, ypos, 20, 20 );
+ for ( int i = NUM_AS_RECORDING_STATES - 1; i >= 0 ; --i )
+ {
+ xpos -= 23;
+ m_pState[ i ]->SetBounds( xpos, ypos, 20, 20 );
+ }
+
+ m_pComboBox->SetBounds( 10, ypos, xpos - 10- 5, 20 );
+}
+
+void CBaseAnimationSetEditor::OnChangeLayout( int value )
+{
+ ChangeLayout( ( EAnimSetLayout_t )value );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a channel in the animation set to overwrite with import data
+//-----------------------------------------------------------------------------
+CDmeChannel* CBaseAnimationSetEditor::FindImportChannel( CDmeChannel *pChannel, CDmeChannelsClip *pChannelsClip )
+{
+ CDmElement *pTargetElement = pChannel->GetToElement();
+ if ( !pTargetElement )
+ return NULL;
+
+ CDmAttribute *pTargetAttribute = pChannel->GetToAttribute();
+ if ( !pTargetAttribute )
+ return NULL;
+
+ const char *pTarget = pTargetAttribute->GetName();
+ const char *pTargetName = pTargetElement->GetName();
+ CDmeLog *pTargetLog = pChannel->GetLog();
+
+ int nCount = pChannelsClip->m_Channels.Count();
+ for ( int j = 0; j < nCount; ++j )
+ {
+ CDmeChannel *pImportChannel = pChannelsClip->m_Channels[j];
+ if ( !pImportChannel )
+ continue;
+
+ CDmeLog *pImportLog = pImportChannel->GetLog();
+ if ( !pImportLog )
+ continue;
+
+ if ( pTargetLog && ( pImportLog->GetType() != pTargetLog->GetType() ) )
+ continue;
+
+ if ( !pImportChannel->GetToAttribute() )
+ continue;
+
+ const char *pImportTarget = pImportChannel->GetToAttribute()->GetName();
+
+ // Attribute to write into has to match exactly
+ if ( Q_stricmp( pTarget, pImportTarget ) )
+ continue;
+
+ CDmElement *pImportTargetElement = pImportChannel->GetToElement();
+ const char *pImportName = pImportTargetElement->GetName();
+
+ // Element name has to match exactly or be of the form *(channel name)*
+ if ( !Q_stricmp( pTargetName, pImportName ) )
+ return pImportChannel;
+
+ char pTemp[512];
+ const char *pParen = strrchr( pTargetName, '(' );
+ if ( !pParen )
+ continue;
+ Q_strncpy( pTemp, pParen+1, sizeof(pTemp) );
+ char *pParen2 = strchr( pTemp, ')' );
+ if ( !pParen2 )
+ continue;
+ *pParen2 = 0;
+ if ( !Q_stricmp( pImportName, pTemp ) )
+ return pImportChannel;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Transforms an imported channel, if necessary
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::TransformImportedChannel( CDmeChannel *pChannel )
+{
+ CDmElement *pTarget = pChannel->GetToElement();
+ const static UtlSymId_t symBones = g_pDataModel->GetSymbol( "bones" );
+ CDmeGameModel *pGameModel = FindReferringElement<CDmeGameModel>( pTarget, symBones );
+ if ( !pGameModel )
+ return;
+
+ int nBoneIndex = pGameModel->FindBone( CastElement< CDmeTransform >( pTarget ) );
+ if ( nBoneIndex < 0 )
+ return;
+
+ // If we've got logs that have been imported, we need to compute our bounds.
+ pGameModel->m_bComputeBounds = true;
+
+ DmAttributeType_t logType = pChannel->GetLog()->GetDataType();
+ int nLayerCount = pChannel->GetLog()->GetNumLayers();
+
+ bool bHasPreTransform = false;
+ bool bHasPostTransform = false;
+
+ matrix3x4_t preTransform, postTransform;
+ if ( pGameModel->GetSrcBoneTransforms( &preTransform, &postTransform, nBoneIndex ) )
+ {
+ bHasPreTransform = true;
+ bHasPostTransform = true;
+ }
+
+ if ( pGameModel->IsRootTransform( nBoneIndex ) )
+ {
+ // NOTE: Root transforms require a pre-multiply of log data
+
+ // Deal with the 'up axis' rotation
+ matrix3x4_t rootTransform;
+ RadianEuler angles( M_PI / 2.0f, 0.0f, M_PI / 2.0f );
+ if ( bHasPreTransform )
+ {
+ AngleMatrix( angles, rootTransform );
+ ConcatTransforms( rootTransform, preTransform, preTransform );
+ }
+ else
+ {
+ AngleMatrix( angles, preTransform );
+ }
+ bHasPreTransform = true;
+ }
+
+ if ( !bHasPreTransform && !bHasPostTransform )
+ return;
+
+ for ( int i = 0; i < nLayerCount; ++i )
+ {
+ if ( logType == AT_VECTOR3 )
+ {
+ CDmeVector3LogLayer *pPositionLog = CastElement< CDmeVector3LogLayer >( pChannel->GetLog()->GetLayer( i ) );
+ if ( bHasPreTransform )
+ {
+ RotatePositionLog( pPositionLog, preTransform );
+ }
+
+#ifdef _DEBUG
+ // At the moment, we don't support anything but prerotation.
+ // This would be tricky because we'd need to read the quat logs
+ // to figure out how to translate in local space.
+ if ( bHasPostTransform )
+ {
+ Assert( fabs( postTransform[0][3] ) < 1e-3 && fabs( postTransform[1][3] ) < 1e-3 && fabs( postTransform[2][3] ) < 1e-3 );
+ }
+#endif
+ }
+ else
+ {
+ CDmeQuaternionLogLayer *pOrientationLog = CastElement< CDmeQuaternionLogLayer >( pChannel->GetLog()->GetLayer( i ) );
+ if ( bHasPreTransform )
+ {
+ RotateOrientationLog( pOrientationLog, preTransform, true );
+ }
+ if ( bHasPostTransform )
+ {
+ RotateOrientationLog( pOrientationLog, postTransform, false );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Expands channels clip time to encompass log
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::FixupChannelsClipTime( CDmeChannelsClip *pChannelsClip, CDmeLog *pLog )
+{
+ // Expand the channels clip to include the entire channel
+ DmeTime_t st = pLog->GetBeginTime();
+ DmeTime_t et = pLog->GetEndTime();
+ st = pChannelsClip->FromChildMediaTime( st, false );
+ et = pChannelsClip->FromChildMediaTime( et, false );
+ if ( et < pChannelsClip->GetEndTime() )
+ {
+ et = pChannelsClip->GetEndTime();
+ }
+ if ( st < pChannelsClip->GetStartTime() )
+ {
+ DmeTime_t tDelta = pChannelsClip->GetStartTime() - st;
+ DmeTime_t tOffset = pChannelsClip->GetTimeOffset();
+ pChannelsClip->SetStartTime( st );
+ pChannelsClip->SetTimeOffset( tOffset - tDelta );
+ }
+ else
+ {
+ st = pChannelsClip->GetStartTime();
+ }
+ DmeTime_t duration = et - st;
+ if ( duration > pChannelsClip->GetDuration() )
+ {
+ pChannelsClip->SetDuration( duration );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Expands channels clip time to encompass log
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::FixupChannelsClipTime( CDmeChannel *pChannel, CDmeLog *pLog )
+{
+ CUtlVector< CDmeChannelsClip* > clips;
+ FindAncestorsReferencingElement( pChannel, clips );
+ int nCount = clips.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ FixupChannelsClipTime( clips[i], pLog );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports a specific channels clip into the animation set
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnImportConfirmed( KeyValues *pParams )
+{
+ KeyValues *pImportParams = pParams->FindKey( "context" );
+ CDmeChannelsClip *pChannelsClip = GetElementKeyValue< CDmeChannelsClip >( pImportParams, "channelsClip" );
+ if ( pParams->GetInt( "operationPerformed" ) == 0 )
+ {
+ CDisableUndoScopeGuard sg;
+ g_pDataModel->RemoveFileId( pChannelsClip->GetFileId() );
+ return;
+ }
+
+ bool bVisibleOnly = pImportParams->GetInt( "visibleOnly" ) != 0;
+
+ CUtlVector< LogPreview_t > controls;
+ int nCount = bVisibleOnly ? BuildVisibleControlList( controls ) : BuildFullControlList( controls );
+
+ CUndoScopeGuard guard( "Import Animation" );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ for ( int k = 0; k < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++k )
+ {
+ CDmeChannel *pChannel = controls[i].m_hChannels[k];
+ if ( !pChannel )
+ continue;
+
+ CDmeChannel *pImportChannel = FindImportChannel( pChannel, pChannelsClip );
+ if ( !pImportChannel )
+ continue;
+
+ // Switch the log over
+ CDmeLog *pImportLog = pImportChannel->GetLog();
+ pChannel->SetLog( pImportLog );
+ pImportChannel->SetLog( NULL );
+ pImportLog->SetFileId( pChannel->GetFileId(), TD_DEEP );
+
+ TransformImportedChannel( pChannel );
+
+ // Expand the channels clip to include the entire channel
+ FixupChannelsClipTime( pChannel, pChannel->GetLog() );
+ }
+ }
+ guard.Release();
+
+ // Cleanup the file
+ CDisableUndoScopeGuard sg;
+ g_pDataModel->RemoveFileId( pChannelsClip->GetFileId() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports a specific channels clip into the animation set
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::ImportAnimation( CDmeChannelsClip *pChannelsClip, bool bVisibleOnly )
+{
+ CUtlVector< LogPreview_t > controls;
+ int nCount = bVisibleOnly ? BuildVisibleControlList( controls ) : BuildFullControlList( controls );
+
+ COperationFileListFrame *pStatusFrame = new COperationFileListFrame( this,
+ "Import the Following Channels?", "Target Control", false );
+ pStatusFrame->SetCloseButtonVisible( false );
+ pStatusFrame->SetOperationColumnHeaderText( "Source Channel" );
+
+ int nSrcCount = pChannelsClip->m_Channels.Count();
+ CDmeChannel** ppFoundChannels = (CDmeChannel**)_alloca( nSrcCount * sizeof(CDmeChannel*) );
+ int nFoundCount = 0;
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ for ( int k = 0; k < LOG_PREVIEW_MAX_CHANNEL_COUNT; ++k )
+ {
+ CDmeChannel *pChannel = controls[i].m_hChannels[k];
+ if ( !pChannel || pChannel->GetToElement() == NULL )
+ continue;
+
+ char pChannelInfo[512];
+ Q_snprintf( pChannelInfo, sizeof(pChannelInfo), "\"%s\" : %s",
+ pChannel->GetToElement()->GetName(), pChannel->GetToAttribute()->GetName() );
+
+ CDmeChannel *pImportChannel = FindImportChannel( pChannel, pChannelsClip );
+ if ( !pImportChannel )
+ {
+ pStatusFrame->AddOperation( "No source channel", pChannelInfo, Color( 255, 0, 0, 255 ) );
+ continue;
+ }
+
+ ppFoundChannels[nFoundCount++] = pImportChannel;
+
+ char pImportInfo[512];
+ Q_snprintf( pImportInfo, sizeof(pImportInfo), "\"%s\" : %s",
+ pImportChannel->GetToElement()->GetName(), pImportChannel->GetToAttribute()->GetName() );
+ pStatusFrame->AddOperation( pImportInfo, pChannelInfo, Color( 0, 255, 0, 255 ) );
+ }
+ }
+
+ for ( int i = 0; i < nSrcCount; ++i )
+ {
+ CDmeChannel *pMissingChannel = pChannelsClip->m_Channels[i];
+
+ int j;
+ for ( j = 0; j < nFoundCount; ++j )
+ {
+ if ( ppFoundChannels[j] == pMissingChannel )
+ break;
+ }
+
+ if ( j != nFoundCount )
+ continue;
+
+ char pImportInfo[512];
+ Q_snprintf( pImportInfo, sizeof(pImportInfo), "\"%s\" : %s",
+ pMissingChannel->GetToElement()->GetName(), pMissingChannel->GetToAttribute()->GetName() );
+ pStatusFrame->AddOperation( pImportInfo, "No destination control", Color( 255, 255, 0, 255 ) );
+ }
+
+ KeyValues *pContext = new KeyValues( "context" );
+ SetElementKeyValue( pContext, "channelsClip", pChannelsClip );
+ pContext->SetInt( "visibleOnly", bVisibleOnly );
+ pStatusFrame->DoModal( pContext, "ImportConfirmed" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by CDmePickerFrame in SelectImportAnimation
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnImportAnimationSelected( KeyValues *pParams )
+{
+ KeyValues *pContextKeyValues = pParams->FindKey( "context" );
+ CDmeChannelsClip *pChannelsClip = GetElementKeyValue< CDmeChannelsClip >( pParams, "dme" );
+ if ( pChannelsClip )
+ {
+ bool bVisibleOnly = pContextKeyValues->GetInt( "visibleOnly" ) != 0;
+ ImportAnimation( pChannelsClip, bVisibleOnly );
+ }
+ else
+ {
+ OnImportAnimationCancelled( pParams );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by CDmePickerFrame in SelectImportAnimation
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnImportAnimationCancelled( KeyValues *pParams )
+{
+ KeyValues *pContextKeyValues = pParams->FindKey( "context" );
+ CDmElement *pAnimationList = GetElementKeyValue<CDmElement>( pContextKeyValues, "animationList" );
+
+ // Cleanup the file
+ if ( pAnimationList )
+ {
+ CDisableUndoScopeGuard sg;
+ g_pDataModel->RemoveFileId( pAnimationList->GetFileId() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects an animation to import
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::SelectImportAnimation( CDmeAnimationList *pAnimationList, bool bVisibleOnly )
+{
+ KeyValues *pContextKeyValues = new KeyValues( "context" );
+ SetElementKeyValue( pContextKeyValues, "animationList", pAnimationList );
+ pContextKeyValues->SetInt( "visibleOnly", bVisibleOnly );
+
+ int nCount = pAnimationList->GetAnimationCount();
+ CUtlVector< DmePickerInfo_t > choices( 0, nCount );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeChannelsClip *pAnimation = pAnimationList->GetAnimation( i );
+ if ( !pAnimation )
+ continue;
+
+ int j = choices.AddToTail();
+ DmePickerInfo_t& info = choices[j];
+ info.m_hElement = pAnimation->GetHandle();
+ info.m_pChoiceString = pAnimation->GetName();
+ }
+
+ CDmePickerFrame *pAnimationPicker = new CDmePickerFrame( this, "Select Animation To Import" );
+ pAnimationPicker->DoModal( choices, pContextKeyValues );
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the FileOpenDialog in OnImportAnimation
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnFileSelected( KeyValues *kv )
+{
+ KeyValues *pContextKeyValues = kv->FindKey( "ImportAnimation" );
+ if ( !pContextKeyValues )
+ return;
+
+ bool bVisibleOnly = pContextKeyValues->GetInt( "visibleOnly" );
+ if ( bVisibleOnly )
+ {
+ CUtlVector< LogPreview_t > controls;
+ int nCount = BuildVisibleControlList( controls );
+ if ( nCount == 0 )
+ {
+ vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Importing Animations\n",
+ "Cannot import because there are no visible controls!\n", GetParent() );
+ pMessageBox->DoModal( );
+ return;
+ }
+ }
+
+ const char *pFileName = kv->GetString( "fullpath", NULL );
+ if ( !pFileName )
+ return;
+
+ CDmElement *pRoot;
+ CDisableUndoScopeGuard guard;
+ DmFileId_t fileId = g_pDataModel->RestoreFromFile( pFileName, NULL, NULL, &pRoot, CR_FORCE_COPY );
+ guard.Release();
+
+ if ( fileId == DMFILEID_INVALID )
+ return;
+
+ CDmeChannelsClip *pChannelsClip = CastElement< CDmeChannelsClip >( pRoot );
+ if ( pChannelsClip )
+ {
+ ImportAnimation( pChannelsClip, bVisibleOnly );
+ return;
+ }
+
+ CDmeAnimationList* pAnimationList = CastElement< CDmeAnimationList >( pRoot );
+ if ( !pAnimationList )
+ {
+ pAnimationList = pRoot->GetValueElement< CDmeAnimationList >( "animationList" );
+ }
+
+ if ( !pAnimationList || pAnimationList->GetAnimationCount() == 0 )
+ {
+ char pBuf[1024];
+ Q_snprintf( pBuf, sizeof(pBuf), "File \"%s\" contains no animations!\n", pFileName );
+ vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Importing Animations\n", pBuf, GetParent() );
+ pMessageBox->DoModal( );
+
+ CDisableUndoScopeGuard sg;
+ g_pDataModel->RemoveFileId( pRoot->GetFileId() );
+ sg.Release();
+ return;
+ }
+
+ if ( pAnimationList->GetAnimationCount() == 1 )
+ {
+ ImportAnimation( pAnimationList->GetAnimation( 0 ), bVisibleOnly );
+ }
+ else
+ {
+ SelectImportAnimation( pAnimationList, bVisibleOnly );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the context menu to import animation files
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnImportAnimation( KeyValues *pParams )
+{
+ // Compute starting directory
+ CDmeGameModel *pGameModel = m_AnimSet->GetValueElement< CDmeGameModel >( "gameModel" );
+
+ char pStartingDir[ MAX_PATH ];
+ if ( !pGameModel )
+ {
+ GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) );
+ }
+ else
+ {
+ char pModelName[ MAX_PATH ];
+ studiohdr_t *pStudioHdr = pGameModel->GetStudioHdr();
+ Q_StripExtension( pStudioHdr->pszName(), pModelName, sizeof(pModelName) );
+
+ char pRelativePath[ MAX_PATH ];
+ Q_snprintf( pRelativePath, sizeof(pRelativePath), "models/%s/animations/dmx", pModelName );
+ GetModContentSubdirectory( pRelativePath, pStartingDir, sizeof(pStartingDir) );
+ if ( !g_pFullFileSystem->IsDirectory( pStartingDir ) )
+ {
+ Q_snprintf( pRelativePath, sizeof(pRelativePath), "models/%s", pModelName );
+ GetModContentSubdirectory( pRelativePath, pStartingDir, sizeof(pStartingDir) );
+ if ( !g_pFullFileSystem->IsDirectory( pStartingDir ) )
+ {
+ GetModContentSubdirectory( "models", pStartingDir, sizeof(pStartingDir) );
+ }
+ }
+ }
+
+ KeyValues *pContextKeyValues = new KeyValues( "ImportAnimation", "visibleOnly", pParams->GetInt( "visibleOnly" ) );
+ FileOpenDialog *pDialog = new FileOpenDialog( this, "Select Animation File Name", true, pContextKeyValues );
+ pDialog->SetStartDirectoryContext( "animation_set_import_animation", pStartingDir );
+ pDialog->AddFilter( "*.*", "All Files (*.*)", false );
+ pDialog->AddFilter( "*.dmx", "Animation file (*.dmx)", true );
+ pDialog->SetDeleteSelfOnClose( true );
+ pDialog->AddActionSignalTarget( this );
+ pDialog->DoModal( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for exporting facial animation
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::SetupFileOpenDialog( vgui::FileOpenDialog *pDialog,
+ bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues )
+{
+ // Compute starting directory
+ char pStartingDir[ MAX_PATH ];
+ GetModSubdirectory( "scenes", pStartingDir, sizeof(pStartingDir) );
+
+ Assert( !bOpenFile );
+ pDialog->SetTitle( "Save Facial Animation As", true );
+
+ Assert( !V_strcmp( pFileFormat, "facial_animation" ) );
+ pDialog->SetStartDirectoryContext( "facial_animation_export", pStartingDir );
+ pDialog->AddFilter( "*.*", "All Files (*.*)", false );
+ pDialog->AddFilter( "*.dmx", "Facial animation file (*.dmx)", true, pFileFormat );
+}
+
+bool CBaseAnimationSetEditor::OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues )
+{
+ Assert( 0 );
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when it's time to write the file
+//-----------------------------------------------------------------------------
+bool CBaseAnimationSetEditor::OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues )
+{
+ // Recompute relative paths for each source now that we know the file name
+ // NOTE: This also updates the name of the fileID in the datamodel system
+ CDisableUndoScopeGuard guard;
+ bool bOk = ExportFacialAnimation( pFileName, GetRootClip(), GetAnimationSetClip(), m_AnimSet );
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for exporting facial animation
+//-----------------------------------------------------------------------------
+void CBaseAnimationSetEditor::OnExportFacialAnimation()
+{
+ KeyValues *pContextKeyValues = new KeyValues( "ExportFacialAnimation" );
+ m_hFileOpenStateMachine->SaveFile( pContextKeyValues, NULL, "facial_animation", FOSM_SHOW_PERFORCE_DIALOGS );
+}
+
+
+void CBaseAnimationSetEditor::OnOpenContextMenu( KeyValues *params )
+{
+ if ( m_hContextMenu.Get() )
+ {
+ delete m_hContextMenu.Get();
+ m_hContextMenu = NULL;
+ }
+
+ m_hContextMenu = new Menu( this, "ActionMenu" );
+
+ int c = ARRAYSIZE( g_AnimSetLayout );
+ for ( int i = 0; i < c; ++i )
+ {
+ const AnimSetLayout_t& data = g_AnimSetLayout[ i ];
+
+ m_hContextMenu->AddMenuItem( data.contextmenulabel, new KeyValues( "OnChangeLayout", "value", (int)data.type ), this );
+ }
+
+ if ( m_AnimSet.Get() )
+ {
+ m_hContextMenu->AddSeparator( );
+ m_hContextMenu->AddMenuItem( "#ImportAnimation", new KeyValues( "ImportAnimation" ), this );
+ m_hContextMenu->AddMenuItem( "#ReattachToModel", new KeyValues( "ReattachToModel" ), this );
+ m_hContextMenu->AddMenuItem( "#ExportFacialAnimation", new KeyValues( "ExportFacialAnimation" ), this );
+ }
+
+ Panel *rpanel = reinterpret_cast< Panel * >( params->GetPtr( "contextlabel" ) );
+ if ( rpanel )
+ {
+ // force the menu to compute required width/height
+ m_hContextMenu->PerformLayout();
+ m_hContextMenu->PositionRelativeToPanel( rpanel, Menu::DOWN, 0, true );
+ }
+ else
+ {
+ Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
+ }
+}
+
+void CBaseAnimationSetEditor::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // Have to manually apply settings here if they aren't attached in hierarchy
+ if ( m_hControlGroup->GetParent() != this )
+ {
+ m_hControlGroup->ApplySchemeSettings( pScheme );
+ }
+ if ( m_hPresetFader->GetParent() != this )
+ {
+ m_hPresetFader->ApplySchemeSettings( pScheme );
+ }
+ if ( m_hAttributeSlider->GetParent() != this )
+ {
+ m_hAttributeSlider->ApplySchemeSettings( pScheme );
+ }
+
+ m_pSelectionModeType->ClearImages();
+ m_pSelectionModeType->AddImage( m_Images.GetImage( 1 ), 0 );
+
+ for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i )
+ {
+ m_pState[ i ]->ClearImages();
+ m_pState[ i ]->AddImage( m_Images.GetImage( i + 2 ), 0 );
+ }
+
+ m_pComboBox->SetFont( pScheme->GetFont( "DefaultBold" ) );
+}
+
+CBaseAnimSetControlGroupPanel *CBaseAnimationSetEditor::GetControlGroup()
+{
+ return m_hControlGroup.Get();
+}
+
+CBaseAnimSetPresetFaderPanel *CBaseAnimationSetEditor::GetPresetFader()
+{
+ return m_hPresetFader.Get();
+}
+
+CBaseAnimSetAttributeSliderPanel *CBaseAnimationSetEditor::GetAttributeSlider()
+{
+ return m_hAttributeSlider.Get();
+}
+
+
+
+void CBaseAnimationSetEditor::ChangeAnimationSet( CDmeAnimationSet *newAnimSet )
+{
+ m_AnimSet = newAnimSet;
+
+ if ( newAnimSet )
+ {
+ CUndoScopeGuard guard( "Auto-create Procedural Presets" );
+ newAnimSet->EnsureProceduralPresets();
+ }
+
+ // send to sub controls
+ m_hControlGroup->ChangeAnimationSet( newAnimSet );
+ m_hPresetFader->ChangeAnimationSet( newAnimSet );
+ m_hAttributeSlider->ChangeAnimationSet( newAnimSet );
+}
+
+void CBaseAnimationSetEditor::OnDataChanged()
+{
+}
+
+void CBaseAnimationSetEditor::OnTextChanged()
+{
+ KeyValues *kv = m_pComboBox->GetActiveItemUserData();
+ if ( !kv )
+ return;
+
+ CDmeAnimationSet *set = GetElementKeyValue< CDmeAnimationSet >( kv, "handle" );
+ if ( set )
+ {
+ ChangeAnimationSet( set );
+ }
+}
+
+void CBaseAnimationSetEditor::SetRecordingState( RecordingState_t state, bool /*updateSettings*/ )
+{
+ m_RecordingState = state;
+
+ // Reset buttons as needed
+ for ( int i = 0; i < NUM_AS_RECORDING_STATES; ++i )
+ {
+ if ( (RecordingState_t)i == state )
+ {
+ m_pState[ i ]->SetSelected( true );
+ m_pState[ i ]->ForceDepressed( true );
+ }
+ else
+ {
+ m_pState[ i ]->SetSelected( false );
+ m_pState[ i ]->ForceDepressed( false );
+ }
+ }
+}
+
+RecordingState_t CBaseAnimationSetEditor::GetRecordingState() const
+{
+ return m_RecordingState;
+}
+
+CDmeAnimationSet *CBaseAnimationSetEditor::GetAnimationSet()
+{
+ return m_AnimSet;
+}
+
+int CBaseAnimationSetEditor::BuildVisibleControlList( CUtlVector< LogPreview_t >& list )
+{
+ return m_hAttributeSlider->BuildVisibleControlList( list );
+}
+
+int CBaseAnimationSetEditor::BuildFullControlList( CUtlVector< LogPreview_t >& list )
+{
+ return m_hAttributeSlider->BuildFullControlList( list );
+}
+
+void CBaseAnimationSetEditor::RecomputePreview()
+{
+ m_hAttributeSlider->RecomputePreview();
+}
+