1044 lines
32 KiB
C++
1044 lines
32 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "SAnimCurveMetadataEditor.h"
|
|
|
|
#include "AnimAssetFindReplaceCurves.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
#include "Framework/Commands/UICommandList.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Widgets/Input/SSpinBox.h"
|
|
#include "Animation/DebugSkelMeshComponent.h"
|
|
#include "Widgets/Input/SSearchBox.h"
|
|
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
|
#include "Widgets/Input/STextEntryPopup.h"
|
|
#include "Animation/AnimSingleNodeInstance.h"
|
|
#include "IEditableSkeleton.h"
|
|
#include "Framework/Commands/GenericCommands.h"
|
|
#include "CurveViewerCommands.h"
|
|
#include "PersonaTabs.h"
|
|
#include "SAnimAssetFindReplace.h"
|
|
#include "Animation/EditorAnimCurveBoneLinks.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "Filters/GenericFilter.h"
|
|
#include "Filters/SBasicFilterBar.h"
|
|
#include "Misc/ScopedSlowTask.h"
|
|
#include "SPositiveActionButton.h"
|
|
#include "Widgets/Docking/SDockTab.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SAnimCurveMetadataEditor"
|
|
|
|
namespace CurveMetadataEditorColumns
|
|
{
|
|
static const FName AnimCurveNameLabel( "Curve Name" );
|
|
static const FName AnimCurveTypeLabel("Type");
|
|
static const FName AnimCurveNumBoneLabel("Num Bones");
|
|
static const FName AnimCurveMaxLODLabel("Max LOD");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SAnimCurveMetadataEditorRow
|
|
|
|
typedef TSharedPtr< FAnimCurveMetadataEditorItem > FAnimCurveMetadataEditorItemPtr;
|
|
|
|
|
|
// This is a flag that is used to filter UI part
|
|
enum class EAnimCurveMetadataEditorFilterFlags : uint8
|
|
{
|
|
// Show all curves
|
|
None = 0,
|
|
// Show morph target curves
|
|
MorphTarget = 0x01,
|
|
// Show material curves
|
|
Material = 0x02,
|
|
};
|
|
|
|
ENUM_CLASS_FLAGS(EAnimCurveMetadataEditorFilterFlags);
|
|
|
|
class FAnimCurveMetadataEditorFilter : public FGenericFilter<EAnimCurveMetadataEditorFilterFlags>
|
|
{
|
|
public:
|
|
FAnimCurveMetadataEditorFilter(EAnimCurveMetadataEditorFilterFlags InFlags, const FString& InName, const FText& InDisplayName, const FText& InToolTipText, FLinearColor InColor, TSharedPtr<FFilterCategory> InCategory)
|
|
: FGenericFilter<EAnimCurveMetadataEditorFilterFlags>(InCategory, InName, InDisplayName, FGenericFilter<EAnimCurveMetadataEditorFilterFlags>::FOnItemFiltered())
|
|
, Flags(InFlags)
|
|
{
|
|
ToolTip = InToolTipText;
|
|
Color = InColor;
|
|
}
|
|
|
|
bool IsActive() const
|
|
{
|
|
return bIsActive;
|
|
}
|
|
|
|
EAnimCurveMetadataEditorFilterFlags GetFlags() const
|
|
{
|
|
return Flags;
|
|
}
|
|
|
|
private:
|
|
// FFilterBase interface
|
|
virtual void ActiveStateChanged(bool bActive) override
|
|
{
|
|
bIsActive = bActive;
|
|
}
|
|
|
|
virtual bool PassesFilter(EAnimCurveMetadataEditorFilterFlags InItem) const override
|
|
{
|
|
return EnumHasAnyFlags(InItem, Flags);
|
|
}
|
|
|
|
private:
|
|
EAnimCurveMetadataEditorFilterFlags Flags;
|
|
bool bIsActive = false;
|
|
};
|
|
|
|
void SAnimCurveMetadataEditorRow::Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, const TSharedRef<IPersonaPreviewScene>& InPreviewScene)
|
|
{
|
|
Item = InArgs._Item;
|
|
AnimCurveViewerPtr = InArgs._AnimCurveViewerPtr;
|
|
PreviewScenePtr = InPreviewScene;
|
|
|
|
check( Item.IsValid() );
|
|
|
|
SMultiColumnTableRow< TSharedPtr<FAnimCurveMetadataEditorItem> >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
TSharedRef< SWidget > SAnimCurveMetadataEditorRow::GenerateWidgetForColumn( const FName& ColumnName )
|
|
{
|
|
if ( ColumnName == CurveMetadataEditorColumns::AnimCurveNameLabel )
|
|
{
|
|
TSharedPtr<SAnimCurveMetadataEditor> AnimCurveViewer = AnimCurveViewerPtr.Pin();
|
|
if (AnimCurveViewer.IsValid())
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
.ToolTipText(this, &SAnimCurveMetadataEditorRow::GetItemName)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.Padding(4)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SAssignNew(Item->EditableText, SInlineEditableTextBlock)
|
|
.OnTextCommitted(AnimCurveViewer.Get(), &SAnimCurveMetadataEditor::OnNameCommitted, Item)
|
|
.Font(FAppStyle::Get().GetFontStyle("SmallFont"))
|
|
.IsSelected(this, &SAnimCurveMetadataEditorRow::IsSelected)
|
|
.Text(this, &SAnimCurveMetadataEditorRow::GetItemName)
|
|
.HighlightText(this, &SAnimCurveMetadataEditorRow::GetFilterText)
|
|
];
|
|
}
|
|
}
|
|
else if (ColumnName == CurveMetadataEditorColumns::AnimCurveTypeLabel)
|
|
{
|
|
TSharedPtr<SAnimCurveMetadataEditor> AnimCurveViewer = AnimCurveViewerPtr.Pin();
|
|
if (AnimCurveViewer.IsValid())
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
.ToolTipText(LOCTEXT("AnimCurveTypeTooltip", "The type of the curve (e.g. morph target, material)."))
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
GetCurveTypeWidget()
|
|
];
|
|
}
|
|
}
|
|
else if(ColumnName == CurveMetadataEditorColumns::AnimCurveNumBoneLabel)
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
.ToolTipText(LOCTEXT("AnimCurveBonesTooltip", "The number of bones linked to this curve."))
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.Padding(0.0f, 1.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SAnimCurveMetadataEditorRow::GetNumConnectedBones)
|
|
.TextStyle(&FCoreStyle::Get().GetWidgetStyle<FTextBlockStyle>( "SmallText" ))
|
|
];
|
|
}
|
|
else if(ColumnName == CurveMetadataEditorColumns::AnimCurveMaxLODLabel)
|
|
{
|
|
return
|
|
SNew(SVerticalBox)
|
|
.ToolTipText(LOCTEXT("AnimCurveLODTooltip", "The max LOD this curve is used with."))
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
.Padding(0.0f, 1.0f)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SAnimCurveMetadataEditorRow::GetMaxLOD)
|
|
.TextStyle(&FCoreStyle::Get().GetWidgetStyle<FTextBlockStyle>( "SmallText" ))
|
|
];
|
|
}
|
|
|
|
return SNullWidget::NullWidget;
|
|
}
|
|
|
|
FText SAnimCurveMetadataEditorRow::GetNumConnectedBones() const
|
|
{
|
|
if(Item->AnimCurveMetaData.IsValid())
|
|
{
|
|
const FCurveMetaData* CurveMetaData = Item->AnimCurveMetaData->GetCurveMetaData(Item->CurveName);
|
|
if (CurveMetaData)
|
|
{
|
|
return FText::AsNumber(CurveMetaData->LinkedBones.Num());
|
|
}
|
|
}
|
|
|
|
return FText::AsNumber(0);
|
|
}
|
|
|
|
FText SAnimCurveMetadataEditorRow::GetMaxLOD() const
|
|
{
|
|
if(Item->AnimCurveMetaData.IsValid())
|
|
{
|
|
const FCurveMetaData* CurveMetaData = Item->AnimCurveMetaData->GetCurveMetaData(Item->CurveName);
|
|
if (CurveMetaData)
|
|
{
|
|
return FText::AsNumber(CurveMetaData->MaxLOD);
|
|
}
|
|
}
|
|
|
|
return FText::AsNumber(0);
|
|
}
|
|
|
|
TSharedRef< SWidget > SAnimCurveMetadataEditorRow::GetCurveTypeWidget()
|
|
{
|
|
return SNew(SHorizontalBox)
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(0.f, 1.f, 1.f, 1.f)
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SCheckBox)
|
|
.OnCheckStateChanged(this, &SAnimCurveMetadataEditorRow::OnAnimCurveTypeBoxChecked, true)
|
|
.IsChecked(this, &SAnimCurveMetadataEditorRow::IsAnimCurveTypeBoxChangedChecked, true)
|
|
.CheckedImage(FAppStyle::GetBrush("AnimCurveViewer.MorphTargetOn"))
|
|
.CheckedPressedImage(FAppStyle::GetBrush("AnimCurveViewer.MorphTargetOn"))
|
|
.UncheckedImage(FAppStyle::GetBrush("AnimCurveViewer.MorphTargetOff"))
|
|
.CheckedHoveredImage(FAppStyle::GetBrush("AnimCurveViewer.MorphTargetOn"))
|
|
.UncheckedHoveredImage(FAppStyle::GetBrush("AnimCurveViewer.MorphTargetOff"))
|
|
.ToolTipText(LOCTEXT("CurveTypeMorphTarget_Tooltip", "MorphTarget"))
|
|
.ForegroundColor(FAppStyle::GetSlateColor("DefaultForeground"))
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(0.f, 1.f, 1.f, 1.f)
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SCheckBox)
|
|
.OnCheckStateChanged(this, &SAnimCurveMetadataEditorRow::OnAnimCurveTypeBoxChecked, false)
|
|
.IsChecked(this, &SAnimCurveMetadataEditorRow::IsAnimCurveTypeBoxChangedChecked, false)
|
|
.CheckedImage(FAppStyle::GetBrush("AnimCurveViewer.MaterialOn"))
|
|
.CheckedPressedImage(FAppStyle::GetBrush("AnimCurveViewer.MaterialOn"))
|
|
.UncheckedImage(FAppStyle::GetBrush("AnimCurveViewer.MaterialOff"))
|
|
.CheckedHoveredImage(FAppStyle::GetBrush("AnimCurveViewer.MaterialOn"))
|
|
.UncheckedHoveredImage(FAppStyle::GetBrush("AnimCurveViewer.MaterialOff"))
|
|
.ToolTipText(LOCTEXT("CurveTypeMaterial_Tooltip", "Material"))
|
|
.ForegroundColor(FAppStyle::GetSlateColor("DefaultForeground"))
|
|
];
|
|
|
|
}
|
|
|
|
void SAnimCurveMetadataEditorRow::OnAnimCurveTypeBoxChecked(ECheckBoxState InState, bool bMorphTarget)
|
|
{
|
|
if(Item->AnimCurveMetaData.IsValid())
|
|
{
|
|
bool bNewData = (InState == ECheckBoxState::Checked);
|
|
if (bMorphTarget)
|
|
{
|
|
Item->AnimCurveMetaData->SetCurveMetaDataMorphTarget(Item->CurveName, bNewData);
|
|
}
|
|
else
|
|
{
|
|
Item->AnimCurveMetaData->SetCurveMetaDataMaterial(Item->CurveName, bNewData);
|
|
}
|
|
}
|
|
|
|
UAnimInstance* AnimInstance = PreviewScenePtr.Pin()->GetPreviewMeshComponent()->GetAnimInstance();
|
|
if (AnimInstance)
|
|
{
|
|
AnimInstance->RecalcRequiredCurves(UE::Anim::FCurveFilterSettings());
|
|
}
|
|
|
|
TSharedPtr<SAnimCurveMetadataEditor> AnimCurveViewer = AnimCurveViewerPtr.Pin();
|
|
if (AnimCurveViewer.IsValid())
|
|
{
|
|
AnimCurveViewer->RefreshCurveList(false);
|
|
}
|
|
}
|
|
|
|
ECheckBoxState SAnimCurveMetadataEditorRow::IsAnimCurveTypeBoxChangedChecked(bool bMorphTarget) const
|
|
{
|
|
bool bData = false;
|
|
|
|
if(Item->AnimCurveMetaData.IsValid())
|
|
{
|
|
const FCurveMetaData* CurveMetaData = Item->AnimCurveMetaData->GetCurveMetaData(Item->CurveName);
|
|
|
|
if (CurveMetaData)
|
|
{
|
|
if (bMorphTarget)
|
|
{
|
|
bData = CurveMetaData->Type.bMorphtarget != 0;
|
|
}
|
|
else
|
|
{
|
|
bData = CurveMetaData->Type.bMaterial != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (bData)? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
FText SAnimCurveMetadataEditorRow::GetItemName() const
|
|
{
|
|
return FText::FromName(Item->CurveName);
|
|
}
|
|
|
|
FText SAnimCurveMetadataEditorRow::GetFilterText() const
|
|
{
|
|
TSharedPtr<SAnimCurveMetadataEditor> AnimCurveViewer = AnimCurveViewerPtr.Pin();
|
|
if (AnimCurveViewer.IsValid())
|
|
{
|
|
return AnimCurveViewer->GetFilterText();
|
|
}
|
|
else
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SAnimCurveMetadataEditor
|
|
|
|
void SAnimCurveMetadataEditor::Construct(const FArguments& InArgs, UObject* InAnimCurveMetaDataHost, const TSharedRef<IPersonaPreviewScene>& InPreviewScene, FOnObjectsSelected InOnObjectsSelected)
|
|
{
|
|
AnimCurveMetaDataHost = Cast<IInterface_AssetUserData>(InAnimCurveMetaDataHost);
|
|
|
|
// If the host doesnt already have it's asset user data set up
|
|
if(AnimCurveMetaDataHost.IsValid() && AnimCurveMetaDataHost->GetAssetUserData<UAnimCurveMetaData>() == nullptr)
|
|
{
|
|
UAnimCurveMetaData* NewMetadata = NewObject<UAnimCurveMetaData>(InAnimCurveMetaDataHost, NAME_None, RF_Transactional);
|
|
AnimCurveMetaDataHost->AddAssetUserData(NewMetadata);
|
|
}
|
|
|
|
// Disable the widget if metadata is no longer hosted
|
|
SetEnabled(MakeAttributeLambda([this]()
|
|
{
|
|
return GetAnimCurveMetaData() != nullptr;
|
|
}));
|
|
|
|
OnObjectsSelected = InOnObjectsSelected;
|
|
|
|
EditorObjectTracker.SetAllowOnePerClass(false);
|
|
|
|
PreviewScenePtr = InPreviewScene;
|
|
|
|
InPreviewScene->RegisterOnPreviewMeshChanged(FOnPreviewMeshChanged::CreateSP(this, &SAnimCurveMetadataEditor::OnPreviewMeshChanged));
|
|
InPreviewScene->RegisterOnAnimChanged(FOnAnimChanged::CreateSP(this, &SAnimCurveMetadataEditor::OnPreviewAssetChanged));
|
|
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
CurveMetaDataChangedHandle = AnimCurveMetaData->RegisterOnCurveMetaDataChanged(FSimpleMulticastDelegate::FDelegate::CreateSP(this, &SAnimCurveMetadataEditor::HandleCurveMetaDataChange));
|
|
}
|
|
|
|
// Register and bind all our menu commands
|
|
FCurveViewerCommands::Register();
|
|
BindCommands();
|
|
|
|
CurrentCurveFlag = EAnimCurveMetadataEditorFilterFlags::None;
|
|
|
|
TSharedPtr<FFilterCategory> FilterCategory = MakeShared<FFilterCategory>(LOCTEXT("CurveFiltersLabel", "Curve Filters"), LOCTEXT("CurveFiltersToolTip", "Filter what kind fo curves can be displayed."));
|
|
|
|
Filters.Add(MakeShared<FAnimCurveMetadataEditorFilter>(
|
|
EAnimCurveMetadataEditorFilterFlags::MorphTarget,
|
|
"MorphTarget",
|
|
LOCTEXT("MorphTargetLabel", "Morph Target"),
|
|
LOCTEXT("MorphTargetTooltip", "Show morph target curves"),
|
|
FLinearColor::Red,
|
|
FilterCategory
|
|
));
|
|
|
|
Filters.Add(MakeShared<FAnimCurveMetadataEditorFilter>(
|
|
EAnimCurveMetadataEditorFilterFlags::Material,
|
|
"Material",
|
|
LOCTEXT("MaterialLabel", "Material"),
|
|
LOCTEXT("MaterialTooltip", "Show material curves"),
|
|
FLinearColor::Green,
|
|
FilterCategory
|
|
));
|
|
|
|
TSharedRef<SBasicFilterBar<EAnimCurveMetadataEditorFilterFlags>> FilterBar = SNew(SBasicFilterBar<EAnimCurveMetadataEditorFilterFlags>)
|
|
.CustomFilters(Filters)
|
|
.UseSectionsForCategories(true)
|
|
.OnFilterChanged_Lambda([this]()
|
|
{
|
|
CurrentCurveFlag = EAnimCurveMetadataEditorFilterFlags::None;
|
|
|
|
for(const TSharedRef<FFilterBase<EAnimCurveMetadataEditorFilterFlags>>& Filter : Filters)
|
|
{
|
|
TSharedRef<FAnimCurveMetadataEditorFilter> AnimCurveFilter = StaticCastSharedRef<FAnimCurveMetadataEditorFilter>(Filter);
|
|
if(AnimCurveFilter->IsActive())
|
|
{
|
|
CurrentCurveFlag |= AnimCurveFilter->GetFlags();
|
|
}
|
|
}
|
|
|
|
RefreshCurveList(false);
|
|
});
|
|
|
|
ChildSlot
|
|
[
|
|
SNew( SVerticalBox )
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2.0f)
|
|
[
|
|
SNew(SPositiveActionButton)
|
|
.Text(LOCTEXT("AddCurveButton", "Add Curve"))
|
|
.ToolTipText(LOCTEXT("AddCurveButtonToolTip", "Add a new curve metadata entry"))
|
|
.OnClicked_Lambda([this]()
|
|
{
|
|
OnAddClicked();
|
|
return FReply::Handled();
|
|
})
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2.0f)
|
|
[
|
|
SNew(SSimpleButton)
|
|
.Text(LOCTEXT("FindReplaceCurvesButton", "Find/Replace Curves..."))
|
|
.ToolTipText(LOCTEXT("FindReplaceCurvesButtonTooltip", "Find and replace curves across multiple assets"))
|
|
.Icon(FAppStyle::GetBrush("Kismet.Tabs.FindResults"))
|
|
.OnClicked_Lambda([this]()
|
|
{
|
|
FindReplaceCurves();
|
|
return FReply::Handled();
|
|
})
|
|
]
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0,2)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(2.0f,0.0f)
|
|
[
|
|
SBasicFilterBar<EAnimCurveMetadataEditorFilterFlags>::MakeAddFilterButton(FilterBar)
|
|
]
|
|
// Filter entry
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f,0.0f)
|
|
[
|
|
SAssignNew( NameFilterBox, SSearchBox )
|
|
.SelectAllTextWhenFocused( true )
|
|
.OnTextChanged( this, &SAnimCurveMetadataEditor::OnFilterTextChanged )
|
|
.OnTextCommitted( this, &SAnimCurveMetadataEditor::OnFilterTextCommitted )
|
|
]
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
FilterBar
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
[
|
|
SAssignNew( AnimCurveListView, SListView< TSharedPtr<FAnimCurveMetadataEditorItem> > )
|
|
.ListItemsSource( &AnimCurveList )
|
|
.OnGenerateRow( this, &SAnimCurveMetadataEditor::GenerateAnimCurveRow )
|
|
.OnContextMenuOpening( this, &SAnimCurveMetadataEditor::OnGetContextMenuContent )
|
|
.SelectionMode(ESelectionMode::Multi)
|
|
.OnSelectionChanged( this, &SAnimCurveMetadataEditor::OnSelectionChanged )
|
|
.HeaderRow
|
|
(
|
|
SNew( SHeaderRow )
|
|
+ SHeaderRow::Column(CurveMetadataEditorColumns::AnimCurveNameLabel)
|
|
.FillWidth(0.65f)
|
|
.DefaultLabel( LOCTEXT( "AnimCurveNameLabel", "Curve Name" ) )
|
|
|
|
+ SHeaderRow::Column(CurveMetadataEditorColumns::AnimCurveTypeLabel)
|
|
.FixedWidth(48.0f)
|
|
.DefaultLabel(LOCTEXT("AnimCurveTypeLabel", "Type"))
|
|
|
|
+ SHeaderRow::Column(CurveMetadataEditorColumns::AnimCurveNumBoneLabel)
|
|
.FillWidth(0.17f)
|
|
.DefaultLabel(LOCTEXT("AnimCurveNumBoneLabel", "Bones"))
|
|
|
|
+SHeaderRow::Column(CurveMetadataEditorColumns::AnimCurveMaxLODLabel)
|
|
.FillWidth(0.17f)
|
|
.DefaultLabel(LOCTEXT("AnimCurveMaxLODLabel", "Max LOD"))
|
|
)
|
|
]
|
|
];
|
|
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
SAnimCurveMetadataEditor::~SAnimCurveMetadataEditor()
|
|
{
|
|
if (PreviewScenePtr.IsValid() )
|
|
{
|
|
PreviewScenePtr.Pin()->UnregisterOnPreviewMeshChanged(this);
|
|
PreviewScenePtr.Pin()->UnregisterOnAnimChanged(this);
|
|
}
|
|
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
AnimCurveMetaData->UnregisterOnCurveMetaDataChanged(CurveMetaDataChangedHandle);
|
|
}
|
|
}
|
|
|
|
FReply SAnimCurveMetadataEditor::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
|
{
|
|
if (UICommandList.IsValid() && UICommandList->ProcessCommandBindings(InKeyEvent))
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::BindCommands()
|
|
{
|
|
// This should not be called twice on the same instance
|
|
check(!UICommandList.IsValid());
|
|
|
|
UICommandList = MakeShareable(new FUICommandList);
|
|
|
|
FUICommandList& CommandList = *UICommandList;
|
|
|
|
// Grab the list of menu commands to bind...
|
|
const FCurveViewerCommands& MenuActions = FCurveViewerCommands::Get();
|
|
|
|
// ...and bind them all
|
|
|
|
CommandList.MapAction(
|
|
FGenericCommands::Get().Rename,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnRenameClicked),
|
|
FCanExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::CanRename));
|
|
|
|
CommandList.MapAction(
|
|
FGenericCommands::Get().Delete,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnDeleteNameClicked),
|
|
FCanExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::CanDelete));
|
|
|
|
CommandList.MapAction(
|
|
FGenericCommands::Get().Copy,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnCopyClicked),
|
|
FCanExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::CanCopy));
|
|
|
|
CommandList.MapAction(
|
|
FGenericCommands::Get().Paste,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnPasteClicked),
|
|
FCanExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::CanPaste));
|
|
|
|
CommandList.MapAction(
|
|
MenuActions.AddCurve,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnAddClicked),
|
|
FCanExecuteAction());
|
|
|
|
CommandList.MapAction(
|
|
MenuActions.FindCurveUses,
|
|
FExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::OnFindCurveUsesClicked),
|
|
FCanExecuteAction::CreateSP(this, &SAnimCurveMetadataEditor::CanFindCurveUses));
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnPreviewMeshChanged(class USkeletalMesh* OldPreviewMesh, class USkeletalMesh* NewPreviewMesh)
|
|
{
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnFilterTextChanged( const FText& SearchText )
|
|
{
|
|
FilterText = SearchText;
|
|
|
|
RefreshCurveList(false);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnCurvesChanged()
|
|
{
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnFilterTextCommitted( const FText& SearchText, ETextCommit::Type CommitInfo )
|
|
{
|
|
// Just do the same as if the user typed in the box
|
|
OnFilterTextChanged( SearchText );
|
|
}
|
|
|
|
TSharedRef<ITableRow> SAnimCurveMetadataEditor::GenerateAnimCurveRow(TSharedPtr<FAnimCurveMetadataEditorItem> InInfo, const TSharedRef<STableViewBase>& OwnerTable)
|
|
{
|
|
check( InInfo.IsValid() );
|
|
|
|
return
|
|
SNew( SAnimCurveMetadataEditorRow, OwnerTable, PreviewScenePtr.Pin().ToSharedRef() )
|
|
.Item( InInfo )
|
|
.AnimCurveViewerPtr( SharedThis(this) );
|
|
}
|
|
|
|
TSharedPtr<SWidget> SAnimCurveMetadataEditor::OnGetContextMenuContent() const
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, UICommandList);
|
|
|
|
const FCurveViewerCommands& Actions = FCurveViewerCommands::Get();
|
|
|
|
MenuBuilder.BeginSection("AnimCurveAction", LOCTEXT( "CurveAction", "Curve Actions" ) );
|
|
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename, NAME_None, LOCTEXT("RenameSmartNameLabel", "Rename Curve"), LOCTEXT("RenameSmartNameToolTip", "Rename the selected curve"));
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Delete, NAME_None, LOCTEXT("DeleteSmartNameLabel", "Delete Curve"), LOCTEXT("DeleteSmartNameToolTip", "Delete the selected curve"));
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Copy);
|
|
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Paste);
|
|
MenuBuilder.AddMenuEntry(Actions.AddCurve);
|
|
MenuBuilder.AddMenuEntry(Actions.FindCurveUses);
|
|
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnRenameClicked()
|
|
{
|
|
TArray< TSharedPtr<FAnimCurveMetadataEditorItem> > SelectedItems = AnimCurveListView->GetSelectedItems();
|
|
|
|
SelectedItems[0]->EditableText->EnterEditingMode();
|
|
}
|
|
|
|
bool SAnimCurveMetadataEditor::CanRename()
|
|
{
|
|
return AnimCurveListView->GetNumItemsSelected() == 1;
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnAddClicked()
|
|
{
|
|
TSharedRef<STextEntryPopup> TextEntry =
|
|
SNew(STextEntryPopup)
|
|
.Label(LOCTEXT("NewSmartnameLabel", "New Name"))
|
|
.OnTextCommitted(this, &SAnimCurveMetadataEditor::CreateNewNameEntry);
|
|
|
|
FSlateApplication& SlateApp = FSlateApplication::Get();
|
|
SlateApp.PushMenu(
|
|
AsShared(),
|
|
FWidgetPath(),
|
|
TextEntry,
|
|
SlateApp.GetCursorPos(),
|
|
FPopupTransitionEffect::TypeInPopup
|
|
);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnFindCurveUsesClicked()
|
|
{
|
|
FindReplaceCurves();
|
|
}
|
|
|
|
bool SAnimCurveMetadataEditor::CanFindCurveUses()
|
|
{
|
|
return AnimCurveListView->GetNumItemsSelected() == 1;
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::FindReplaceCurves()
|
|
{
|
|
FName CurveName = NAME_None;
|
|
bool bMorphTarget = false;
|
|
bool bMaterial = false;
|
|
TArray<TSharedPtr<FAnimCurveMetadataEditorItem>> SelectedItems = AnimCurveListView->GetSelectedItems();
|
|
if(SelectedItems.Num() > 0)
|
|
{
|
|
CurveName = SelectedItems[0]->CurveName;
|
|
bMorphTarget = EnumHasAnyFlags(SelectedItems[0]->Flags, EAnimCurveMetadataEditorFilterFlags::MorphTarget);
|
|
bMaterial = EnumHasAnyFlags(SelectedItems[0]->Flags, EAnimCurveMetadataEditorFilterFlags::Material);
|
|
}
|
|
|
|
if(TSharedPtr<SDockTab> ActiveTab = FGlobalTabmanager::Get()->GetActiveTab())
|
|
{
|
|
if(TSharedPtr<FTabManager> TabManager = ActiveTab->GetTabManagerPtr())
|
|
{
|
|
if(TSharedPtr<SDockTab> Tab = TabManager->TryInvokeTab(FPersonaTabs::FindReplaceID))
|
|
{
|
|
TSharedRef<IAnimAssetFindReplace> FindReplaceWidget = StaticCastSharedRef<IAnimAssetFindReplace>(Tab->GetContent());
|
|
FindReplaceWidget->SetCurrentProcessor(UAnimAssetFindReplaceCurves::StaticClass());
|
|
if(CurveName != NAME_None)
|
|
{
|
|
if(UAnimAssetFindReplaceCurves* Processor = FindReplaceWidget->GetProcessor<UAnimAssetFindReplaceCurves>())
|
|
{
|
|
Processor->SetFindString(CurveName.ToString());
|
|
Processor->SetFindWholeWord(true);
|
|
Processor->SetSearchMaterials(bMaterial);
|
|
Processor->SetSearchMorphTargets(bMorphTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::CreateNewNameEntry(const FText& CommittedText, ETextCommit::Type CommitType)
|
|
{
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
if (!CommittedText.IsEmpty() && CommitType == ETextCommit::OnEnter)
|
|
{
|
|
FName NewName = FName(*CommittedText.ToString());
|
|
if (AnimCurveMetaData->AddCurveMetaData(NewName))
|
|
{
|
|
// Successfully added
|
|
RefreshCurveList(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UAnimInstance* SAnimCurveMetadataEditor::GetAnimInstance() const
|
|
{
|
|
return PreviewScenePtr.Pin()->GetPreviewMeshComponent()->GetAnimInstance();
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::CreateAnimCurveList( const FString& SearchText, bool bInFullRefresh )
|
|
{
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
bool bDirty = bInFullRefresh;
|
|
|
|
AnimCurveList.Reset();
|
|
|
|
if(bInFullRefresh)
|
|
{
|
|
AllSeenAnimCurvesMap.Reset();
|
|
}
|
|
|
|
auto AddCurve = [this, AnimCurveMetaData](FName InCurveName, EAnimCurveMetadataEditorFilterFlags InFlags)
|
|
{
|
|
// Only add if the curve doesnt exist
|
|
TSharedPtr<FAnimCurveMetadataEditorItem>* ExistingItem = AllSeenAnimCurvesMap.Find(InCurveName);
|
|
if(ExistingItem == nullptr)
|
|
{
|
|
UEditorAnimCurveBoneLinks* EditorMirrorObj = Cast<UEditorAnimCurveBoneLinks> (EditorObjectTracker.GetEditorObjectForClass(UEditorAnimCurveBoneLinks::StaticClass()));
|
|
EditorMirrorObj->Initialize(AnimCurveMetaData, InCurveName, FOnAnimCurveBonesChange::CreateSP(this, &SAnimCurveMetadataEditor::ApplyCurveBoneLinks));
|
|
TSharedRef<FAnimCurveMetadataEditorItem> NewInfo = FAnimCurveMetadataEditorItem::Make(AnimCurveMetaData, InCurveName, InFlags, EditorMirrorObj);
|
|
|
|
AllSeenAnimCurvesMap.Add(InCurveName, NewInfo);
|
|
}
|
|
else
|
|
{
|
|
(*ExistingItem)->Flags = InFlags;
|
|
}
|
|
};
|
|
|
|
// Add curve items from metadata
|
|
AnimCurveMetaData->ForEachCurveMetaData([&AddCurve](FName InCurveName, const FCurveMetaData& InCurveMetaData)
|
|
{
|
|
EAnimCurveMetadataEditorFilterFlags Flags = EAnimCurveMetadataEditorFilterFlags::None;
|
|
if(InCurveMetaData.Type.bMaterial)
|
|
{
|
|
Flags |= EAnimCurveMetadataEditorFilterFlags::Material;
|
|
}
|
|
if(InCurveMetaData.Type.bMorphtarget)
|
|
{
|
|
Flags |= EAnimCurveMetadataEditorFilterFlags::MorphTarget;
|
|
}
|
|
AddCurve(InCurveName, Flags);
|
|
});
|
|
|
|
// Iterate through all curves that have been seen
|
|
for (const TPair<FName, TSharedPtr<FAnimCurveMetadataEditorItem>>& CurveNameValuePair : AllSeenAnimCurvesMap)
|
|
{
|
|
TSharedPtr<FAnimCurveMetadataEditorItem> Item = CurveNameValuePair.Value;
|
|
|
|
bool bAddToList = true;
|
|
|
|
// See if we pass the search filter
|
|
if (!FilterText.IsEmpty())
|
|
{
|
|
if (!CurveNameValuePair.Key.ToString().Contains(*FilterText.ToString()))
|
|
{
|
|
bAddToList = false;
|
|
}
|
|
}
|
|
|
|
if(CurrentCurveFlag != EAnimCurveMetadataEditorFilterFlags::None)
|
|
{
|
|
bAddToList = EnumHasAnyFlags(Item->Flags, CurrentCurveFlag);
|
|
}
|
|
|
|
if(Item->bShown != bAddToList)
|
|
{
|
|
Item->bShown = bAddToList;
|
|
bDirty = true;
|
|
}
|
|
|
|
// If we still want to add
|
|
if (bAddToList)
|
|
{
|
|
AnimCurveList.Add(Item);
|
|
}
|
|
}
|
|
|
|
if(bDirty)
|
|
{
|
|
// Sort final list
|
|
struct FSortSmartNamesAlphabetically
|
|
{
|
|
bool operator()(const TSharedPtr<FAnimCurveMetadataEditorItem>& A, const TSharedPtr<FAnimCurveMetadataEditorItem>& B) const
|
|
{
|
|
return (A.Get()->CurveName.Compare(B.Get()->CurveName) < 0);
|
|
}
|
|
};
|
|
|
|
AnimCurveList.Sort(FSortSmartNamesAlphabetically());
|
|
|
|
AnimCurveListView->RequestListRefresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::PostUndoRedo()
|
|
{
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnPreviewAssetChanged(class UAnimationAsset* NewAsset)
|
|
{
|
|
OverrideCurves.Empty();
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::RefreshCurveList(bool bInFullRefresh)
|
|
{
|
|
CreateAnimCurveList(FilterText.ToString(), bInFullRefresh);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnNameCommitted(const FText& InNewName, ETextCommit::Type, TSharedPtr<FAnimCurveMetadataEditorItem> Item)
|
|
{
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
FName NewName(*InNewName.ToString());
|
|
if (NewName == Item->CurveName)
|
|
{
|
|
// Do nothing if trying to rename to existing name...
|
|
}
|
|
else if (NewName != NAME_None)
|
|
{
|
|
if(!AnimCurveMetaData->RenameCurveMetaData(Item->CurveName, NewName))
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("InvalidName"), FText::FromName(NewName) );
|
|
FNotificationInfo Info(FText::Format(LOCTEXT("AnimCurveRenamed", "The name \"{InvalidName}\" is invalid or already used."), Args));
|
|
|
|
Info.bUseLargeFont = false;
|
|
Info.ExpireDuration = 5.0f;
|
|
|
|
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
|
|
if (Notification.IsValid())
|
|
{
|
|
Notification->SetCompletionState(SNotificationItem::CS_Fail);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AllSeenAnimCurvesMap.Remove(Item->CurveName);
|
|
AnimCurveList.Remove(Item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnDeleteNameClicked()
|
|
{
|
|
TArray< TSharedPtr<FAnimCurveMetadataEditorItem> > SelectedItems = AnimCurveListView->GetSelectedItems();
|
|
TArray<FName> SelectedNames;
|
|
|
|
for (TSharedPtr<FAnimCurveMetadataEditorItem> Item : SelectedItems)
|
|
{
|
|
SelectedNames.Add(Item->CurveName);
|
|
}
|
|
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
AnimCurveMetaData->RemoveCurveMetaData(SelectedNames);
|
|
}
|
|
}
|
|
|
|
bool SAnimCurveMetadataEditor::CanDelete()
|
|
{
|
|
return AnimCurveListView->GetNumItemsSelected() > 0;
|
|
}
|
|
|
|
static const TCHAR* ClipboardHeader = TEXT("AnimCurveViewer");
|
|
|
|
void SAnimCurveMetadataEditor::OnCopyClicked()
|
|
{
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
TArray<TSharedPtr<FAnimCurveMetadataEditorItem>> SelectedItems = AnimCurveListView->GetSelectedItems();
|
|
|
|
FAnimCurveMetadataEditorClipboard Clipboard;
|
|
|
|
for (TSharedPtr<FAnimCurveMetadataEditorItem> Item : SelectedItems)
|
|
{
|
|
FAnimCurveMetadataEditorClipboardEntry Entry;
|
|
Entry.CurveName = Item->CurveName;
|
|
|
|
if(const FCurveMetaData* CurveMetaData = AnimCurveMetaData->GetCurveMetaData(Item->CurveName))
|
|
{
|
|
Entry.MetaData = *CurveMetaData;
|
|
}
|
|
|
|
Clipboard.Entries.Add(Entry);
|
|
}
|
|
|
|
FString ClipboardString = ClipboardHeader;
|
|
FAnimCurveMetadataEditorClipboard::StaticStruct()->ExportText(ClipboardString, &Clipboard, nullptr, nullptr, PPF_None, nullptr);
|
|
FPlatformApplicationMisc::ClipboardCopy(*ClipboardString);
|
|
}
|
|
}
|
|
|
|
bool SAnimCurveMetadataEditor::CanCopy() const
|
|
{
|
|
return AnimCurveListView->GetNumItemsSelected() > 0;
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnPasteClicked()
|
|
{
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("PasteCurves", "Paste Curves"));
|
|
|
|
FString TextToImport;
|
|
FPlatformApplicationMisc::ClipboardPaste(TextToImport);
|
|
TextToImport.RemoveFromStart(ClipboardHeader);
|
|
|
|
FAnimCurveMetadataEditorClipboard Clipboard;
|
|
FAnimCurveMetadataEditorClipboard::StaticStruct()->ImportText(*TextToImport, &Clipboard, nullptr, PPF_None, GLog, FAnimCurveMetadataEditorClipboard::StaticStruct()->GetName());
|
|
|
|
for(const FAnimCurveMetadataEditorClipboardEntry& Entry : Clipboard.Entries)
|
|
{
|
|
AnimCurveMetaData->AddCurveMetaData(Entry.CurveName);
|
|
AnimCurveMetaData->SetCurveMetaDataBoneLinks(Entry.CurveName, Entry.MetaData.LinkedBones, Entry.MetaData.MaxLOD, GetSkeleton());
|
|
AnimCurveMetaData->SetCurveMetaDataMaterial(Entry.CurveName, Entry.MetaData.Type.bMaterial);
|
|
AnimCurveMetaData->SetCurveMetaDataMorphTarget(Entry.CurveName, Entry.MetaData.Type.bMorphtarget);
|
|
|
|
RefreshCurveList(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SAnimCurveMetadataEditor::CanPaste() const
|
|
{
|
|
FString TextToImport;
|
|
FPlatformApplicationMisc::ClipboardPaste(TextToImport);
|
|
return TextToImport.StartsWith(ClipboardHeader);
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::OnSelectionChanged(TSharedPtr<FAnimCurveMetadataEditorItem> InItem, ESelectInfo::Type SelectInfo)
|
|
{
|
|
if(SelectInfo != ESelectInfo::Direct)
|
|
{
|
|
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
// make sure the currently selected ones are refreshed if it's first time
|
|
TArray<UObject*> SelectedObjects;
|
|
|
|
TArray< TSharedPtr< FAnimCurveMetadataEditorItem > > SelectedRows = AnimCurveListView->GetSelectedItems();
|
|
for (auto ItemIt = SelectedRows.CreateIterator(); ItemIt; ++ItemIt)
|
|
{
|
|
TSharedPtr< FAnimCurveMetadataEditorItem > RowItem = (*ItemIt);
|
|
UEditorAnimCurveBoneLinks* EditorMirrorObj = RowItem->EditorMirrorObject;
|
|
if (RowItem == InItem)
|
|
{
|
|
// first time selected, refresh
|
|
TArray<FBoneReference> BoneLinks;
|
|
FName CurrentName = RowItem->CurveName;
|
|
const FCurveMetaData* CurveMetaData = AnimCurveMetaData->GetCurveMetaData(CurrentName);
|
|
uint8 MaxLOD = 0xFF;
|
|
if (CurveMetaData)
|
|
{
|
|
BoneLinks = CurveMetaData->LinkedBones;
|
|
MaxLOD = CurveMetaData->MaxLOD;
|
|
}
|
|
|
|
EditorMirrorObj->Refresh(CurrentName, BoneLinks, MaxLOD);
|
|
}
|
|
|
|
SelectedObjects.Add(EditorMirrorObj);
|
|
}
|
|
|
|
OnObjectsSelected.ExecuteIfBound(SelectedObjects);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::ApplyCurveBoneLinks(UEditorAnimCurveBoneLinks* EditorObj)
|
|
{
|
|
if(UAnimCurveMetaData* AnimCurveMetaData = GetAnimCurveMetaData())
|
|
{
|
|
if (EditorObj && !GIsTransacting)
|
|
{
|
|
AnimCurveMetaData->SetCurveMetaDataBoneLinks(EditorObj->CurveName, EditorObj->ConnectedBones, EditorObj->MaxLOD, GetSkeleton());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAnimCurveMetadataEditor::HandleCurveMetaDataChange()
|
|
{
|
|
AnimCurveList.Empty();
|
|
RefreshCurveList(true);
|
|
}
|
|
|
|
UAnimCurveMetaData* SAnimCurveMetadataEditor::GetAnimCurveMetaData() const
|
|
{
|
|
if(IInterface_AssetUserData* AssetUserData = AnimCurveMetaDataHost.Get())
|
|
{
|
|
return AssetUserData->GetAssetUserData<UAnimCurveMetaData>();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
USkeleton* SAnimCurveMetadataEditor::GetSkeleton() const
|
|
{
|
|
if(USkeleton* Skeleton = Cast<USkeleton>(AnimCurveMetaDataHost.Get()))
|
|
{
|
|
return Skeleton;
|
|
}
|
|
else if(USkeletalMesh* SkeletalMesh = Cast<USkeletalMesh>(AnimCurveMetaDataHost.Get()))
|
|
{
|
|
return SkeletalMesh->GetSkeleton();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
|