You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Skeleton compatibility is now bi-directional. Specifying a compatible skeleton A -> B now implies B -> A. Skeleton compatibility is now an editor-only concern. The runtime will attempt to do the 'best it can' via name -> name mappings. Only the editor will prevent assigning incompatible skeletons in (e.g.) asset pickers etc. Skeleton compatibility checks in editor can now be disabled in the editor preferences (and each asset picker now has a checkbox option in its view settings that allows for quick access to this). Moves FSkeletonRemapping to its own file (which is now private). Skeleton remappings are now generated on demand on worker threads just before animation decompression and stored in a registry, guarded by FRWScopeLock for thread-safety. Fixed some anim BP compiler edge cases where asset references on pins were not getting preloaded correctly, causing skeletons to be erroneously reported as missing. Exposed the current asset registry filter in SAssetView so that menu extensions can access it (and use it to provide context) #jira UE-166054 #jira UE-167355 #rb Jurre.deBaare,John.vanderBerg #preflight 635902602e6690262afa86f9 [CL 22878911 by Thomas Sarkanen in ue5-main branch]
416 lines
14 KiB
C++
416 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SkeletonEditor.h"
|
|
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Animation/DebugSkelMeshComponent.h"
|
|
#include "Toolkits/AssetEditorToolkit.h"
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "EdGraph/EdGraphSchema.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "EngineGlobals.h"
|
|
#include "ISkeletonTree.h"
|
|
#include "ISkeletonEditorModule.h"
|
|
#include "IPersonaToolkit.h"
|
|
#include "PersonaModule.h"
|
|
#include "SkeletonEditorMode.h"
|
|
#include "IAnimationSequenceBrowser.h"
|
|
#include "IPersonaPreviewScene.h"
|
|
#include "SkeletonEditorCommands.h"
|
|
#include "IAssetFamily.h"
|
|
#include "PersonaCommonCommands.h"
|
|
#include "IEditableSkeleton.h"
|
|
#include "ISkeletonTreeItem.h"
|
|
#include "Algo/Transform.h"
|
|
#include "PersonaToolMenuContext.h"
|
|
#include "ToolMenus.h"
|
|
#include "ToolMenuMisc.h"
|
|
#include "SkeletonToolMenuContext.h"
|
|
|
|
const FName SkeletonEditorAppIdentifier = FName(TEXT("SkeletonEditorApp"));
|
|
|
|
const FName SkeletonEditorModes::SkeletonEditorMode(TEXT("SkeletonEditorMode"));
|
|
|
|
const FName SkeletonEditorTabs::DetailsTab(TEXT("DetailsTab"));
|
|
const FName SkeletonEditorTabs::SkeletonTreeTab(TEXT("SkeletonTreeView"));
|
|
const FName SkeletonEditorTabs::ViewportTab(TEXT("Viewport"));
|
|
const FName SkeletonEditorTabs::AssetBrowserTab(TEXT("SequenceBrowser"));
|
|
const FName SkeletonEditorTabs::AnimNotifiesTab(TEXT("SkeletonAnimNotifies"));
|
|
const FName SkeletonEditorTabs::CurveNamesTab(TEXT("AnimCurveViewerTab"));
|
|
const FName SkeletonEditorTabs::AdvancedPreviewTab(TEXT("AdvancedPreviewTab"));
|
|
const FName SkeletonEditorTabs::RetargetManagerTab(TEXT("RetargetManager"));
|
|
const FName SkeletonEditorTabs::SlotNamesTab("SkeletonSlotNames");
|
|
|
|
DEFINE_LOG_CATEGORY(LogSkeletonEditor);
|
|
|
|
#define LOCTEXT_NAMESPACE "SkeletonEditor"
|
|
|
|
FSkeletonEditor::FSkeletonEditor()
|
|
{
|
|
UEditorEngine* Editor = Cast<UEditorEngine>(GEngine);
|
|
if (Editor != nullptr)
|
|
{
|
|
Editor->RegisterForUndo(this);
|
|
}
|
|
}
|
|
|
|
FSkeletonEditor::~FSkeletonEditor()
|
|
{
|
|
UEditorEngine* Editor = Cast<UEditorEngine>(GEngine);
|
|
if (Editor != nullptr)
|
|
{
|
|
Editor->UnregisterForUndo(this);
|
|
}
|
|
if (PersonaToolkit.IsValid())
|
|
{
|
|
constexpr bool bSetPreviewMeshInAsset = false;
|
|
PersonaToolkit->SetPreviewMesh(nullptr, bSetPreviewMeshInAsset);
|
|
}
|
|
}
|
|
|
|
void FSkeletonEditor::HandleOpenNewAsset(UObject* InNewAsset)
|
|
{
|
|
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(InNewAsset);
|
|
}
|
|
|
|
void FSkeletonEditor::RegisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
|
|
{
|
|
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_SkeletonEditor", "Skeleton Editor"));
|
|
|
|
FAssetEditorToolkit::RegisterTabSpawners( InTabManager );
|
|
}
|
|
|
|
void FSkeletonEditor::UnregisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
|
|
{
|
|
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
|
|
}
|
|
|
|
void FSkeletonEditor::InitSkeletonEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, USkeleton* InSkeleton)
|
|
{
|
|
Skeleton = InSkeleton;
|
|
|
|
FPersonaToolkitArgs PersonaToolkitArgs;
|
|
PersonaToolkitArgs.OnPreviewSceneSettingsCustomized = FOnPreviewSceneSettingsCustomized::FDelegate::CreateSP(this, &FSkeletonEditor::HandleOnPreviewSceneSettingsCustomized);
|
|
|
|
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
|
|
PersonaToolkit = PersonaModule.CreatePersonaToolkit(InSkeleton, PersonaToolkitArgs);
|
|
|
|
PersonaToolkit->GetPreviewScene()->SetDefaultAnimationMode(EPreviewSceneDefaultAnimationMode::ReferencePose);
|
|
|
|
PersonaModule.RecordAssetOpened(FAssetData(InSkeleton));
|
|
|
|
FSkeletonTreeArgs SkeletonTreeArgs;
|
|
SkeletonTreeArgs.OnSelectionChanged = FOnSkeletonTreeSelectionChanged::CreateSP(this, &FSkeletonEditor::HandleSelectionChanged);
|
|
SkeletonTreeArgs.PreviewScene = PersonaToolkit->GetPreviewScene();
|
|
SkeletonTreeArgs.ContextName = GetToolkitFName();
|
|
|
|
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::GetModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
|
|
SkeletonTree = SkeletonEditorModule.CreateSkeletonTree(PersonaToolkit->GetSkeleton(), SkeletonTreeArgs);
|
|
|
|
const bool bCreateDefaultStandaloneMenu = true;
|
|
const bool bCreateDefaultToolbar = true;
|
|
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, SkeletonEditorAppIdentifier, FTabManager::FLayout::NullLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, InSkeleton);
|
|
|
|
BindCommands();
|
|
|
|
AddApplicationMode(
|
|
SkeletonEditorModes::SkeletonEditorMode,
|
|
MakeShareable(new FSkeletonEditorMode(SharedThis(this), SkeletonTree.ToSharedRef())));
|
|
|
|
SetCurrentMode(SkeletonEditorModes::SkeletonEditorMode);
|
|
|
|
ExtendMenu();
|
|
ExtendToolbar();
|
|
RegenerateMenusAndToolbars();
|
|
|
|
PersonaToolkit->GetPreviewScene()->SetAllowMeshHitProxies(false);
|
|
}
|
|
|
|
FName FSkeletonEditor::GetToolkitFName() const
|
|
{
|
|
return FName("SkeletonEditor");
|
|
}
|
|
|
|
FText FSkeletonEditor::GetBaseToolkitName() const
|
|
{
|
|
return LOCTEXT("AppLabel", "SkeletonEditor");
|
|
}
|
|
|
|
FString FSkeletonEditor::GetWorldCentricTabPrefix() const
|
|
{
|
|
return LOCTEXT("WorldCentricTabPrefix", "SkeletonEditor ").ToString();
|
|
}
|
|
|
|
FLinearColor FSkeletonEditor::GetWorldCentricTabColorScale() const
|
|
{
|
|
return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f);
|
|
}
|
|
|
|
void FSkeletonEditor::InitToolMenuContext(FToolMenuContext& MenuContext)
|
|
{
|
|
FAssetEditorToolkit::InitToolMenuContext(MenuContext);
|
|
|
|
UPersonaToolMenuContext* PersonaContext = NewObject<UPersonaToolMenuContext>();
|
|
PersonaContext->SetToolkit(GetPersonaToolkit());
|
|
MenuContext.AddObject(PersonaContext);
|
|
|
|
USkeletonToolMenuContext* SkeletonContext = NewObject<USkeletonToolMenuContext>();
|
|
SkeletonContext->SkeletonEditor = SharedThis(this);
|
|
MenuContext.AddObject(SkeletonContext);
|
|
}
|
|
|
|
void FSkeletonEditor::BindCommands()
|
|
{
|
|
FSkeletonEditorCommands::Register();
|
|
|
|
ToolkitCommands->MapAction( FSkeletonEditorCommands::Get().RemoveUnusedBones,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::RemoveUnusedBones),
|
|
FCanExecuteAction::CreateSP(this, &FSkeletonEditor::CanRemoveBones));
|
|
|
|
ToolkitCommands->MapAction(FSkeletonEditorCommands::Get().TestSkeletonCurveNamesForUse,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::TestSkeletonCurveNamesForUse));
|
|
|
|
ToolkitCommands->MapAction(FSkeletonEditorCommands::Get().UpdateSkeletonRefPose,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::UpdateSkeletonRefPose));
|
|
|
|
ToolkitCommands->MapAction(FSkeletonEditorCommands::Get().AnimNotifyWindow,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::OnAnimNotifyWindow));
|
|
|
|
ToolkitCommands->MapAction(FSkeletonEditorCommands::Get().RetargetManager,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::OnRetargetManager));
|
|
|
|
ToolkitCommands->MapAction(FSkeletonEditorCommands::Get().ImportMesh,
|
|
FExecuteAction::CreateSP(this, &FSkeletonEditor::OnImportAsset));
|
|
|
|
ToolkitCommands->MapAction(FPersonaCommonCommands::Get().TogglePlay,
|
|
FExecuteAction::CreateRaw(&GetPersonaToolkit()->GetPreviewScene().Get(), &IPersonaPreviewScene::TogglePlayback));
|
|
}
|
|
|
|
TSharedPtr<FSkeletonEditor> FSkeletonEditor::GetSkeletonEditor(const FToolMenuContext& InMenuContext)
|
|
{
|
|
if (USkeletonToolMenuContext* Context = InMenuContext.FindContext<USkeletonToolMenuContext>())
|
|
{
|
|
if (Context->SkeletonEditor.IsValid())
|
|
{
|
|
return StaticCastSharedPtr<FSkeletonEditor>(Context->SkeletonEditor.Pin());
|
|
}
|
|
}
|
|
|
|
return TSharedPtr<FSkeletonEditor>();
|
|
}
|
|
|
|
void FSkeletonEditor::ExtendToolbar()
|
|
{
|
|
FToolMenuOwnerScoped OwnerScoped(this);
|
|
|
|
// Add in Editor Specific functionality
|
|
FName ParentName;
|
|
static const FName MenuName = GetToolMenuToolbarName(ParentName);
|
|
|
|
UToolMenu* ToolMenu = UToolMenus::Get()->ExtendMenu(MenuName);
|
|
const FToolMenuInsert SectionInsertLocation("Asset", EToolMenuInsertType::After);
|
|
|
|
{
|
|
ToolMenu->AddDynamicSection("Persona", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InToolMenu)
|
|
{
|
|
TSharedPtr<FSkeletonEditor> SkeletonEditor = GetSkeletonEditor(InToolMenu->Context);
|
|
if (SkeletonEditor.IsValid() && SkeletonEditor->PersonaToolkit.IsValid())
|
|
{
|
|
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
|
|
PersonaModule.AddCommonToolbarExtensions(InToolMenu);
|
|
}
|
|
}), SectionInsertLocation);
|
|
}
|
|
|
|
{
|
|
FToolMenuSection& SkeletonSection = ToolMenu->AddSection("Skeleton", LOCTEXT("ToolbarSkeletonSectionLabel", "Skeleton"), SectionInsertLocation);
|
|
SkeletonSection.AddEntry(FToolMenuEntry::InitToolBarButton(FSkeletonEditorCommands::Get().AnimNotifyWindow));
|
|
SkeletonSection.AddEntry(FToolMenuEntry::InitToolBarButton(FSkeletonEditorCommands::Get().RetargetManager, LOCTEXT("Toolbar_RetargetManager", "Retarget Manager")));
|
|
SkeletonSection.AddEntry(FToolMenuEntry::InitToolBarButton(FSkeletonEditorCommands::Get().ImportMesh));
|
|
}
|
|
|
|
// If the ToolbarExtender is valid, remove it before rebuilding it
|
|
if (ToolbarExtender.IsValid())
|
|
{
|
|
RemoveToolbarExtender(ToolbarExtender);
|
|
ToolbarExtender.Reset();
|
|
}
|
|
|
|
ToolbarExtender = MakeShareable(new FExtender);
|
|
|
|
AddToolbarExtender(ToolbarExtender);
|
|
|
|
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::GetModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
|
|
AddToolbarExtender(SkeletonEditorModule.GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
|
|
TArray<ISkeletonEditorModule::FSkeletonEditorToolbarExtender> ToolbarExtenderDelegates = SkeletonEditorModule.GetAllSkeletonEditorToolbarExtenders();
|
|
|
|
for (auto& ToolbarExtenderDelegate : ToolbarExtenderDelegates)
|
|
{
|
|
if (ToolbarExtenderDelegate.IsBound())
|
|
{
|
|
AddToolbarExtender(ToolbarExtenderDelegate.Execute(GetToolkitCommands(), SharedThis(this)));
|
|
}
|
|
}
|
|
|
|
ToolbarExtender->AddToolBarExtension(
|
|
"Asset",
|
|
EExtensionHook::After,
|
|
GetToolkitCommands(),
|
|
FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ToolbarBuilder)
|
|
{
|
|
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
|
|
TSharedRef<class IAssetFamily> AssetFamily = PersonaModule.CreatePersonaAssetFamily(Skeleton);
|
|
AddToolbarWidget(PersonaModule.CreateAssetFamilyShortcutWidget(SharedThis(this), AssetFamily));
|
|
}
|
|
));
|
|
}
|
|
|
|
void FSkeletonEditor::ExtendMenu()
|
|
{
|
|
MenuExtender = MakeShareable(new FExtender);
|
|
|
|
struct Local
|
|
{
|
|
static void ExtendMenu(FMenuBuilder& MenuBuilder)
|
|
{
|
|
// View
|
|
MenuBuilder.BeginSection("SkeletonEditor", LOCTEXT("SkeletonEditorAssetMenu_Skeleton", "Skeleton"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FSkeletonEditorCommands::Get().RemoveUnusedBones);
|
|
MenuBuilder.AddMenuEntry(FSkeletonEditorCommands::Get().UpdateSkeletonRefPose);
|
|
MenuBuilder.AddMenuEntry(FSkeletonEditorCommands::Get().TestSkeletonCurveNamesForUse);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
};
|
|
|
|
MenuExtender->AddMenuExtension(
|
|
"AssetEditorActions",
|
|
EExtensionHook::After,
|
|
GetToolkitCommands(),
|
|
FMenuExtensionDelegate::CreateStatic(&Local::ExtendMenu)
|
|
);
|
|
|
|
AddMenuExtender(MenuExtender);
|
|
|
|
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::GetModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
|
|
AddMenuExtender(SkeletonEditorModule.GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
}
|
|
|
|
UObject* FSkeletonEditor::HandleGetAsset()
|
|
{
|
|
return GetEditingObject();
|
|
}
|
|
|
|
void FSkeletonEditor::HandleObjectsSelected(const TArray<UObject*>& InObjects)
|
|
{
|
|
if (DetailsView.IsValid())
|
|
{
|
|
DetailsView->SetObjects(InObjects);
|
|
}
|
|
}
|
|
|
|
void FSkeletonEditor::HandleObjectSelected(UObject* InObject)
|
|
{
|
|
if (DetailsView.IsValid())
|
|
{
|
|
DetailsView->SetObject(InObject);
|
|
}
|
|
}
|
|
|
|
void FSkeletonEditor::HandleSelectionChanged(const TArrayView<TSharedPtr<ISkeletonTreeItem>>& InSelectedItems, ESelectInfo::Type InSelectInfo)
|
|
{
|
|
if (DetailsView.IsValid())
|
|
{
|
|
TArray<UObject*> Objects;
|
|
Algo::TransformIf(InSelectedItems, Objects, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject() != nullptr; }, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject(); });
|
|
DetailsView->SetObjects(Objects);
|
|
}
|
|
}
|
|
|
|
void FSkeletonEditor::PostUndo(bool bSuccess)
|
|
{
|
|
OnPostUndo.Broadcast();
|
|
}
|
|
|
|
void FSkeletonEditor::PostRedo(bool bSuccess)
|
|
{
|
|
OnPostUndo.Broadcast();
|
|
}
|
|
|
|
void FSkeletonEditor::Tick(float DeltaTime)
|
|
{
|
|
GetPersonaToolkit()->GetPreviewScene()->InvalidateViews();
|
|
}
|
|
|
|
TStatId FSkeletonEditor::GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FSkeletonEditor, STATGROUP_Tickables);
|
|
}
|
|
|
|
bool FSkeletonEditor::CanRemoveBones() const
|
|
{
|
|
UDebugSkelMeshComponent* PreviewMeshComponent = PersonaToolkit->GetPreviewMeshComponent();
|
|
return PreviewMeshComponent && PreviewMeshComponent->GetSkeletalMeshAsset();
|
|
}
|
|
|
|
void FSkeletonEditor::RemoveUnusedBones()
|
|
{
|
|
GetSkeletonTree()->GetEditableSkeleton()->RemoveUnusedBones();
|
|
}
|
|
|
|
void FSkeletonEditor::TestSkeletonCurveNamesForUse() const
|
|
{
|
|
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
|
|
PersonaModule.TestSkeletonCurveNamesForUse(GetSkeletonTree()->GetEditableSkeleton());
|
|
}
|
|
|
|
void FSkeletonEditor::UpdateSkeletonRefPose()
|
|
{
|
|
if (PersonaToolkit->GetPreviewMeshComponent()->GetSkeletalMeshAsset())
|
|
{
|
|
GetSkeletonTree()->GetEditableSkeleton()->UpdateSkeletonReferencePose(PersonaToolkit->GetPreviewMeshComponent()->GetSkeletalMeshAsset());
|
|
}
|
|
}
|
|
|
|
void FSkeletonEditor::OnAnimNotifyWindow()
|
|
{
|
|
InvokeTab(SkeletonEditorTabs::AnimNotifiesTab);
|
|
}
|
|
|
|
void FSkeletonEditor::OnRetargetManager()
|
|
{
|
|
InvokeTab(SkeletonEditorTabs::RetargetManagerTab);
|
|
}
|
|
|
|
void FSkeletonEditor::OnImportAsset()
|
|
{
|
|
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
|
|
PersonaModule.ImportNewAsset(Skeleton, FBXIT_SkeletalMesh);
|
|
}
|
|
|
|
void FSkeletonEditor::HandleOnPreviewSceneSettingsCustomized(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
DetailBuilder.HideCategory("Animation Blueprint");
|
|
}
|
|
|
|
void FSkeletonEditor::HandleDetailsCreated(const TSharedRef<IDetailsView>& InDetailsView)
|
|
{
|
|
DetailsView = InDetailsView;
|
|
}
|
|
|
|
void FSkeletonEditor::HandleAnimationSequenceBrowserCreated(const TSharedRef<IAnimationSequenceBrowser>& InSequenceBrowser)
|
|
{
|
|
SequenceBrowser = InSequenceBrowser;
|
|
}
|
|
|
|
IAnimationSequenceBrowser* FSkeletonEditor::GetAssetBrowser() const
|
|
{
|
|
return SequenceBrowser.Pin().Get();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|