You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1076 lines
32 KiB
1076 lines
32 KiB
//========= 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(); |
|
} |
|
|
|
|