You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Adding a public member to USkeletalMesh class is now prohibed since it can break asynchronous operations that will be added next to this submit. #rb danny.couture #jira UEENT-3936 #rnx [CL 14812920 by Alexis Matte in ue5-main branch]
620 lines
22 KiB
C++
620 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "SRetargetSourceWindow.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "AssetData.h"
|
|
#include "EditorStyleSet.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Views/SListView.h"
|
|
#include "IContentBrowserSingleton.h"
|
|
#include "ContentBrowserModule.h"
|
|
#include "AssetNotifications.h"
|
|
#include "Widgets/Input/SSearchBox.h"
|
|
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "IEditableSkeleton.h"
|
|
#include "AnimationRuntime.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SRetargetSourceWindow"
|
|
|
|
static const FName ColumnId_RetargetSourceNameLabel( "Retarget Source Name" );
|
|
static const FName ColumnID_BaseReferenceMeshLabel( "Reference Mesh" );
|
|
|
|
DECLARE_DELEGATE_TwoParams( FOnRenameCommit, const FName& /*OldName*/, const FString& /*NewName*/ )
|
|
DECLARE_DELEGATE_RetVal_ThreeParams( bool, FOnVerifyRenameCommit, const FName& /*OldName*/, const FString& /*NewName*/, FText& /*OutErrorMessage*/)
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SRetargetSourceListRow
|
|
|
|
typedef TSharedPtr< FDisplayedRetargetSourceInfo > FDisplayedRetargetSourceInfoPtr;
|
|
|
|
class SRetargetSourceListRow
|
|
: public SMultiColumnTableRow< FDisplayedRetargetSourceInfoPtr >
|
|
{
|
|
public:
|
|
|
|
SLATE_BEGIN_ARGS( SRetargetSourceListRow ) {}
|
|
|
|
/** The item for this row **/
|
|
SLATE_ARGUMENT( FDisplayedRetargetSourceInfoPtr, Item )
|
|
|
|
/* The SRetargetSourceWindow that handles all retarget sources */
|
|
SLATE_ARGUMENT( class SRetargetSourceWindow*, RetargetSourceWindow )
|
|
|
|
/* Widget used to display the list of retarget sources*/
|
|
SLATE_ARGUMENT( TSharedPtr<SRetargetSourceListType>, RetargetSourceListView )
|
|
|
|
/** Delegate for when an asset name has been entered for an item that is in a rename state */
|
|
SLATE_EVENT( FOnRenameCommit, OnRenameCommit )
|
|
|
|
/** Delegate for when an asset name has been entered for an item to verify the name before commit */
|
|
SLATE_EVENT( FOnVerifyRenameCommit, OnVerifyRenameCommit )
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView );
|
|
|
|
/** Overridden from SMultiColumnTableRow. Generates a widget for this column of the tree row. */
|
|
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override;
|
|
|
|
private:
|
|
|
|
/* Returns the reference mesh this pose is based on */
|
|
FString GetReferenceMeshName() { return Item->ReferenceMesh->GetPathName(); }
|
|
|
|
/* The SRetargetSourceWindow that handles all retarget sources*/
|
|
SRetargetSourceWindow* RetargetSourceWindow;
|
|
|
|
/** Widget used to display the list of retarget sources*/
|
|
TSharedPtr<SRetargetSourceListType> RetargetSourceListView;
|
|
|
|
/** The name and weight of the retarget source*/
|
|
FDisplayedRetargetSourceInfoPtr Item;
|
|
|
|
/** Delegate for when an asset name has been entered for an item that is in a rename state */
|
|
FOnRenameCommit OnRenameCommit;
|
|
|
|
/** Delegate for when an asset name has been entered for an item to verify the name before commit */
|
|
FOnVerifyRenameCommit OnVerifyRenameCommit;
|
|
|
|
/** Handles committing a name change */
|
|
virtual void HandleNameCommitted( const FText& NewText, ETextCommit::Type CommitInfo );
|
|
|
|
/** Handles verifying a name change */
|
|
virtual bool HandleVerifyNameChanged( const FText& NewText, FText& OutErrorMessage );
|
|
};
|
|
|
|
void SRetargetSourceListRow::Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
|
|
{
|
|
Item = InArgs._Item;
|
|
RetargetSourceWindow = InArgs._RetargetSourceWindow;
|
|
RetargetSourceListView = InArgs._RetargetSourceListView;
|
|
OnRenameCommit = InArgs._OnRenameCommit;
|
|
OnVerifyRenameCommit = InArgs._OnVerifyRenameCommit;
|
|
|
|
check( Item.IsValid() );
|
|
|
|
SMultiColumnTableRow< FDisplayedRetargetSourceInfoPtr >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
TSharedRef< SWidget > SRetargetSourceListRow::GenerateWidgetForColumn( const FName& ColumnName )
|
|
{
|
|
if ( ColumnName == ColumnId_RetargetSourceNameLabel )
|
|
{
|
|
TSharedPtr< SInlineEditableTextBlock > InlineWidget;
|
|
TSharedRef< SWidget > NewWidget =
|
|
SNew( SVerticalBox )
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding( 0.0f, 4.0f )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SAssignNew(InlineWidget, SInlineEditableTextBlock)
|
|
.Text( FText::FromString(Item->GetDisplayName()) )
|
|
.OnTextCommitted(this, &SRetargetSourceListRow::HandleNameCommitted)
|
|
.OnVerifyTextChanged(this, &SRetargetSourceListRow::HandleVerifyNameChanged)
|
|
.HighlightText( RetargetSourceWindow->GetFilterText() )
|
|
.IsReadOnly(false)
|
|
.IsSelected(this, &SMultiColumnTableRow< FDisplayedRetargetSourceInfoPtr >::IsSelectedExclusively)
|
|
];
|
|
|
|
Item->OnEnterEditingMode.BindSP( InlineWidget.Get(), &SInlineEditableTextBlock::EnterEditingMode);
|
|
|
|
return NewWidget;
|
|
}
|
|
else
|
|
{
|
|
// Encase the SSpinbox in an SVertical box so we can apply padding. Setting ItemHeight on the containing SListView has no effect :-(
|
|
return
|
|
SNew( SVerticalBox )
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding( 0.0f, 1.0f )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( FText::FromString(Item->GetReferenceMeshName()) )
|
|
.HighlightText( RetargetSourceWindow->GetFilterText() )
|
|
];
|
|
}
|
|
}
|
|
|
|
/** Handles committing a name change */
|
|
void SRetargetSourceListRow::HandleNameCommitted( const FText& NewText, ETextCommit::Type CommitInfo )
|
|
{
|
|
OnRenameCommit.ExecuteIfBound(Item->Name, NewText.ToString());
|
|
}
|
|
|
|
/** Handles verifying a name change */
|
|
bool SRetargetSourceListRow::HandleVerifyNameChanged( const FText& NewText, FText& OutErrorMessage )
|
|
{
|
|
return !OnVerifyRenameCommit.IsBound() || OnVerifyRenameCommit.Execute(Item->Name, NewText.ToString(), OutErrorMessage);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SRetargetSourceWindow
|
|
|
|
void SRetargetSourceWindow::Construct(const FArguments& InArgs, const TSharedRef<IEditableSkeleton>& InEditableSkeleton, FSimpleMulticastDelegate& InOnPostUndo)
|
|
{
|
|
EditableSkeletonPtr = InEditableSkeleton;
|
|
|
|
InOnPostUndo.Add(FSimpleDelegate::CreateSP( this, &SRetargetSourceWindow::PostUndo ) );
|
|
|
|
FText SkeletonName = FText::FromString(InEditableSkeleton->GetSkeleton().GetName());
|
|
|
|
ChildSlot
|
|
[
|
|
SNew( SVerticalBox )
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew( SHorizontalBox )
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("Skeleton_Label", "Current Skeleton "))
|
|
.Font(FEditorStyle::GetFontStyle("Persona.RetargetManager.BoldFont"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(SkeletonName)
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Right)
|
|
.AutoWidth()
|
|
.Padding(2, 0)
|
|
[
|
|
SNew(SButton)
|
|
.OnClicked(FOnClicked::CreateSP(this, &SRetargetSourceWindow::OnAddRetargetSourceButtonClicked))
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.Text(LOCTEXT("AddRetargetSourceButton_Label", "Add New"))
|
|
.ToolTipText(LOCTEXT("AddRetargetSourceButton_ToolTip", "Add new retarget source to the list. It won't add if one already exists."))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Right)
|
|
.AutoWidth()
|
|
.Padding(2, 0)
|
|
[
|
|
SNew(SButton)
|
|
.OnClicked(FOnClicked::CreateSP(this, &SRetargetSourceWindow::OnUpdateAllRetargetSourceButtonClicked))
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.Text(LOCTEXT("UpdateAllRetargetSourceButton_Label", "Update All"))
|
|
.ToolTipText(LOCTEXT("UpdateAllRetargetSourceButton_ToolTip", "Use this to update all retarget source poses with latest mesh. If you want to update individual, use the context menu."))
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0,2)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
// Filter entry
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth( 1 )
|
|
[
|
|
SAssignNew( NameFilterBox, SSearchBox )
|
|
.SelectAllTextWhenFocused( true )
|
|
.OnTextChanged( this, &SRetargetSourceWindow::OnFilterTextChanged )
|
|
.OnTextCommitted( this, &SRetargetSourceWindow::OnFilterTextCommitted )
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight( 1.0f ) // This is required to make the scrollbar work, as content overflows Slate containers by default
|
|
[
|
|
SAssignNew( RetargetSourceListView, SRetargetSourceListType )
|
|
.ListItemsSource( &RetargetSourceList )
|
|
.OnGenerateRow( this, &SRetargetSourceWindow::GenerateRetargetSourceRow )
|
|
.OnContextMenuOpening( this, &SRetargetSourceWindow::OnGetContextMenuContent )
|
|
.ItemHeight( 22.0f )
|
|
.HeaderRow
|
|
(
|
|
SNew( SHeaderRow )
|
|
+ SHeaderRow::Column( ColumnId_RetargetSourceNameLabel )
|
|
.DefaultLabel( LOCTEXT( "RetargetSourceNameLabel", "Retarget Source Name" ) )
|
|
|
|
+ SHeaderRow::Column( ColumnID_BaseReferenceMeshLabel )
|
|
.DefaultLabel( LOCTEXT( "RetargetSourceWeightLabel", "Source Mesh" ) )
|
|
)
|
|
]
|
|
];
|
|
|
|
CreateRetargetSourceList();
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnFilterTextChanged( const FText& SearchText )
|
|
{
|
|
FilterText = SearchText;
|
|
|
|
CreateRetargetSourceList( SearchText.ToString() );
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnFilterTextCommitted( const FText& SearchText, ETextCommit::Type CommitInfo )
|
|
{
|
|
// Just do the same as if the user typed in the box
|
|
OnFilterTextChanged( SearchText );
|
|
}
|
|
|
|
TSharedRef<ITableRow> SRetargetSourceWindow::GenerateRetargetSourceRow(TSharedPtr<FDisplayedRetargetSourceInfo> InInfo, const TSharedRef<STableViewBase>& OwnerTable)
|
|
{
|
|
check( InInfo.IsValid() );
|
|
|
|
return
|
|
SNew( SRetargetSourceListRow, OwnerTable )
|
|
.Item( InInfo )
|
|
.RetargetSourceWindow( this )
|
|
.RetargetSourceListView( RetargetSourceListView )
|
|
.OnRenameCommit(this, &SRetargetSourceWindow::OnRenameCommit)
|
|
.OnVerifyRenameCommit(this, &SRetargetSourceWindow::OnVerifyRenameCommit);
|
|
}
|
|
|
|
|
|
void SRetargetSourceWindow::OnRenameCommit( const FName& InOldName, const FString& InNewName )
|
|
{
|
|
FString NewNameString = InNewName;
|
|
if (InOldName != FName(*NewNameString.TrimStartAndEnd()))
|
|
{
|
|
FName NewName = *InNewName;
|
|
EditableSkeletonPtr.Pin()->RenameRetargetSource(InOldName, NewName);
|
|
|
|
FAssetNotifications::SkeletonNeedsToBeSaved(&EditableSkeletonPtr.Pin()->GetSkeleton());
|
|
CreateRetargetSourceList( NameFilterBox->GetText().ToString() );
|
|
}
|
|
}
|
|
|
|
bool SRetargetSourceWindow::OnVerifyRenameCommit( const FName& OldName, const FString& NewName, FText& OutErrorMessage)
|
|
{
|
|
// if same reject
|
|
FString NewString = NewName;
|
|
|
|
if (OldName == FName(*NewString.TrimStartAndEnd()))
|
|
{
|
|
OutErrorMessage = FText::Format (LOCTEXT("RetargetSourceWindowNameSame", "{0} Nothing modified"), FText::FromString(OldName.ToString()) );
|
|
return false;
|
|
}
|
|
|
|
const USkeleton& Skeleton = EditableSkeletonPtr.Pin()->GetSkeleton();
|
|
const FReferencePose* Pose = Skeleton.AnimRetargetSources.Find(OldName);
|
|
if (!Pose)
|
|
{
|
|
OutErrorMessage = FText::Format (LOCTEXT("RetargetSourceWindowNameNotFound", "{0} Not found"), FText::FromString(OldName.ToString()) );
|
|
return false;
|
|
}
|
|
|
|
Pose = Skeleton.AnimRetargetSources.Find(FName(*NewName));
|
|
if (Pose)
|
|
{
|
|
OutErrorMessage = FText::Format (LOCTEXT("RetargetSourceWindowNameDuplicated", "{0} already exists"), FText::FromString(NewName) );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<SWidget> SRetargetSourceWindow::OnGetContextMenuContent() const
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, NULL);
|
|
|
|
MenuBuilder.BeginSection("RetargetSourceAction", LOCTEXT( "New", "New" ) );
|
|
{
|
|
FUIAction Action = FUIAction( FExecuteAction::CreateSP( const_cast<SRetargetSourceWindow*>(this), &SRetargetSourceWindow::OnAddRetargetSource ) );
|
|
const FText Label = LOCTEXT("AddRetargetSourceActionLabel", "Add...");
|
|
const FText ToolTipText = LOCTEXT("AddRetargetSourceActionTooltip", "Add new retarget source.");
|
|
MenuBuilder.AddMenuEntry( Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("RetargetSourceAction", LOCTEXT( "Selected", "Selected Item Actions" ) );
|
|
{
|
|
FUIAction Action = FUIAction( FExecuteAction::CreateSP( const_cast<SRetargetSourceWindow*>(this), &SRetargetSourceWindow::OnRenameRetargetSource ),
|
|
FCanExecuteAction::CreateSP( this, &SRetargetSourceWindow::CanPerformRename ) );
|
|
const FText Label = LOCTEXT("RenameRetargetSourceActionLabel", "Rename");
|
|
const FText ToolTipText = LOCTEXT("RenameRetargetSourceActionTooltip", "Rename the selected retarget source.");
|
|
MenuBuilder.AddMenuEntry( Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
{
|
|
FUIAction Action = FUIAction( FExecuteAction::CreateSP( const_cast<SRetargetSourceWindow*>(this), &SRetargetSourceWindow::OnDeleteRetargetSource ),
|
|
FCanExecuteAction::CreateSP( this, &SRetargetSourceWindow::CanPerformDelete ) );
|
|
const FText Label = LOCTEXT("DeleteRetargetSourceActionLabel", "Delete");
|
|
const FText ToolTipText = LOCTEXT("DeleteRetargetSourceActionTooltip", "Deletes the selected retarget sources.");
|
|
MenuBuilder.AddMenuEntry( Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
{
|
|
FUIAction Action = FUIAction( FExecuteAction::CreateSP( const_cast<SRetargetSourceWindow*>(this), &SRetargetSourceWindow::OnRefreshRetargetSource, false ),
|
|
FCanExecuteAction::CreateSP( this, &SRetargetSourceWindow::CanPerformRefresh ) );
|
|
const FText Label = LOCTEXT("RefreshRetargetSourceActionLabel", "Update");
|
|
const FText ToolTipText = LOCTEXT("RefreshRetargetSourceActionTooltip", "Updates the selected retarget sources from source mesh.");
|
|
MenuBuilder.AddMenuEntry( Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
/** @TODO: FIXME: Item to rename. Only valid for adding and this is so hacky, I know**/
|
|
//TSharedPtr<FDisplayedBasePoseInfo> ItemToRename;
|
|
|
|
void SRetargetSourceWindow::CreateRetargetSourceList( const FString& SearchText, const FName NewName )
|
|
{
|
|
RetargetSourceList.Empty();
|
|
bool bDoFiltering = !SearchText.IsEmpty();
|
|
|
|
const USkeleton& Skeleton = EditableSkeletonPtr.Pin()->GetSkeleton();
|
|
for (auto Iter=Skeleton.AnimRetargetSources.CreateConstIterator(); Iter; ++Iter)
|
|
{
|
|
const FName& Name = Iter.Key();
|
|
const FReferencePose& RefPose = Iter.Value();
|
|
const FString SourceMeshName = RefPose.SourceReferenceMesh.ToString();
|
|
|
|
if ( bDoFiltering )
|
|
{
|
|
if (!Name.ToString().Contains( SearchText ) && !SourceMeshName.Contains(SearchText))
|
|
{
|
|
continue; // Skip items that don't match our filter
|
|
}
|
|
}
|
|
|
|
USkeletalMesh* LoadedMesh = RefPose.SourceReferenceMesh.Get();
|
|
bool bDirty = false;
|
|
if (LoadedMesh)
|
|
{
|
|
TArray<FTransform> TransformArray;
|
|
FAnimationRuntime::MakeSkeletonRefPoseFromMesh(LoadedMesh, &Skeleton, TransformArray);
|
|
bDirty = (RefPose.ReferencePose.Num() != TransformArray.Num()) || (FMemory::Memcmp(TransformArray.GetData(), RefPose.ReferencePose.GetData(), RefPose.ReferencePose.GetAllocatedSize()) != 0);
|
|
}
|
|
|
|
TSharedRef<FDisplayedRetargetSourceInfo> Info = FDisplayedRetargetSourceInfo::Make( Name, RefPose.SourceReferenceMesh, bDirty);
|
|
|
|
if (Name == NewName)
|
|
{
|
|
ItemToRename = Info;
|
|
}
|
|
|
|
RetargetSourceList.Add( Info );
|
|
}
|
|
|
|
RetargetSourceListView->RequestListRefresh();
|
|
}
|
|
|
|
int32 SRetargetSourceWindow::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
|
|
{
|
|
const int32 Result = SCompoundWidget::OnPaint( Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled );
|
|
|
|
// I need to do this after first paint
|
|
if (ItemToRename.IsValid())
|
|
{
|
|
ItemToRename->RequestRename();
|
|
ItemToRename = NULL;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void SRetargetSourceWindow::AddRetargetSource( const FName Name, USkeletalMesh * ReferenceMesh )
|
|
{
|
|
EditableSkeletonPtr.Pin()->AddRetargetSource(Name, ReferenceMesh);
|
|
|
|
FAssetNotifications::SkeletonNeedsToBeSaved(&EditableSkeletonPtr.Pin()->GetSkeleton());
|
|
|
|
// clear search filter
|
|
NameFilterBox->SetText(FText::GetEmpty());
|
|
CreateRetargetSourceList( NameFilterBox->GetText().ToString(), Name );
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnAddRetargetSource()
|
|
{
|
|
// show list of skeletalmeshes that they can choose from
|
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
|
|
|
|
FAssetPickerConfig AssetPickerConfig;
|
|
AssetPickerConfig.Filter.ClassNames.Add(USkeletalMesh::StaticClass()->GetFName());
|
|
AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateSP(this, &SRetargetSourceWindow::OnAssetSelectedFromMeshPicker);
|
|
AssetPickerConfig.bAllowNullSelection = false;
|
|
AssetPickerConfig.InitialAssetViewType = EAssetViewType::Tile;
|
|
|
|
const USkeleton& Skeleton = EditableSkeletonPtr.Pin()->GetSkeleton();
|
|
|
|
FString SkeletonString = FAssetData(&Skeleton).GetExportTextName();
|
|
AssetPickerConfig.Filter.TagsAndValues.Add(USkeletalMesh::GetSkeletonMemberName(), SkeletonString);
|
|
|
|
TSharedRef<SWidget> Widget = SNew(SBox)
|
|
.WidthOverride(384)
|
|
.HeightOverride(768)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderBackgroundColor(FLinearColor(0.25f, 0.25f, 0.25f, 1.f))
|
|
.Padding( 2 )
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") )
|
|
.Padding( 8 )
|
|
[
|
|
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig)
|
|
]
|
|
]
|
|
];
|
|
|
|
FSlateApplication::Get().PushMenu(
|
|
AsShared(),
|
|
FWidgetPath(),
|
|
Widget,
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect( FPopupTransitionEffect::TopMenu )
|
|
);
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnAssetSelectedFromMeshPicker(const FAssetData& AssetData)
|
|
{
|
|
// make sure you don't have any more retarget sources from the same mesh
|
|
const USkeleton& Skeleton = EditableSkeletonPtr.Pin()->GetSkeleton();
|
|
const FString AssetFullPath = AssetData.ToSoftObjectPath().ToString();
|
|
for (auto Iter=Skeleton.AnimRetargetSources.CreateConstIterator(); Iter; ++Iter)
|
|
{
|
|
const FName& Name = Iter.Key();
|
|
const FReferencePose& RefPose = Iter.Value();
|
|
|
|
if (RefPose.SourceReferenceMesh.ToString() == AssetFullPath)
|
|
{
|
|
// make ask users if they'd like to create new source when there is existing source.
|
|
// they could update existing source
|
|
|
|
// redundant source exists
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("SkeletalMeshName"), FText::FromString(AssetFullPath));
|
|
Args.Add(TEXT("ExistingSourceName"), FText::FromName(RefPose.PoseName));
|
|
FNotificationInfo Info(FText::Format(LOCTEXT("RetargetSourceAlreadyExists", "Retarget Source for {SkeletalMeshName} already exists : {ExistingSourceName}"), Args));
|
|
Info.ExpireDuration = 5.0f;
|
|
Info.bUseLargeFont = false;
|
|
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
|
|
if (Notification.IsValid())
|
|
{
|
|
Notification->SetCompletionState(SNotificationItem::CS_Fail);
|
|
}
|
|
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
return;
|
|
}
|
|
}
|
|
|
|
USkeletalMesh * SelectedMesh = CastChecked<USkeletalMesh>(AssetData.GetAsset());
|
|
// give temporary name, and make it editable first time
|
|
AddRetargetSource(FName(*SelectedMesh->GetName()), SelectedMesh);
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
}
|
|
|
|
bool SRetargetSourceWindow::CanPerformDelete() const
|
|
{
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
return SelectedRows.Num() > 0;
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnDeleteRetargetSource()
|
|
{
|
|
TArray<FName> SelectedNames;
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
for (int RowIndex = 0; RowIndex < SelectedRows.Num(); ++RowIndex)
|
|
{
|
|
SelectedNames.Add(SelectedRows[RowIndex]->Name);
|
|
}
|
|
|
|
EditableSkeletonPtr.Pin()->DeleteRetargetSources(SelectedNames);
|
|
|
|
FAssetNotifications::SkeletonNeedsToBeSaved(&EditableSkeletonPtr.Pin()->GetSkeleton());
|
|
|
|
CreateRetargetSourceList( NameFilterBox->GetText().ToString() );
|
|
}
|
|
|
|
bool SRetargetSourceWindow::CanPerformRename() const
|
|
{
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
return SelectedRows.Num() == 1;
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnRenameRetargetSource()
|
|
{
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
|
|
if (ensure (SelectedRows.Num() == 1))
|
|
{
|
|
int32 RowIndex = 0;
|
|
const USkeleton& Skeleton = EditableSkeletonPtr.Pin()->GetSkeleton();
|
|
const FReferencePose* PoseFound = Skeleton.AnimRetargetSources.Find(SelectedRows[RowIndex]->Name);
|
|
if(PoseFound)
|
|
{
|
|
// we used to verify if there is any animation referencing and warn them, but that doesn't really help
|
|
// because you can rename by just double click as well, and it slows process, so removed it
|
|
// request rename
|
|
SelectedRows[RowIndex]->RequestRename();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SRetargetSourceWindow::CanPerformRefresh() const
|
|
{
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
return SelectedRows.Num() > 0;
|
|
}
|
|
|
|
void SRetargetSourceWindow::OnRefreshRetargetSource(bool bAll)
|
|
{
|
|
TArray<FName> SelectedNames;
|
|
if (bAll)
|
|
{
|
|
for (int RowIndex = 0; RowIndex < RetargetSourceList.Num(); ++RowIndex)
|
|
{
|
|
SelectedNames.Add(RetargetSourceList[RowIndex]->Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TArray< TSharedPtr< FDisplayedRetargetSourceInfo > > SelectedRows = RetargetSourceListView->GetSelectedItems();
|
|
for (int RowIndex = 0; RowIndex < SelectedRows.Num(); ++RowIndex)
|
|
{
|
|
SelectedNames.Add(SelectedRows[RowIndex]->Name);
|
|
}
|
|
}
|
|
|
|
EditableSkeletonPtr.Pin()->RefreshRetargetSources(SelectedNames);
|
|
|
|
FAssetNotifications::SkeletonNeedsToBeSaved(&EditableSkeletonPtr.Pin()->GetSkeleton());
|
|
}
|
|
|
|
void SRetargetSourceWindow::PostUndo()
|
|
{
|
|
CreateRetargetSourceList();
|
|
}
|
|
|
|
FReply SRetargetSourceWindow::OnAddRetargetSourceButtonClicked()
|
|
{
|
|
OnAddRetargetSource();
|
|
return FReply::Handled();
|
|
}
|
|
|
|
FReply SRetargetSourceWindow::OnUpdateAllRetargetSourceButtonClicked()
|
|
{
|
|
OnRefreshRetargetSource(true);
|
|
return FReply::Handled();
|
|
}
|
|
#undef LOCTEXT_NAMESPACE
|
|
|