From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- vgui2/dme_controls/BaseAnimationSetEditor.cpp | 1076 +++++++++++++++++++++++++ 1 file changed, 1076 insertions(+) create mode 100644 vgui2/dme_controls/BaseAnimationSetEditor.cpp (limited to 'vgui2/dme_controls/BaseAnimationSetEditor.cpp') 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( 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( 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(); +} + -- cgit v1.2.3