Refactoring
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "FunnyGirlEditorModule.h"
|
||||
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "AssetTypeActions/AssetTypeActions_DirectorPrompt.h"
|
||||
#include "AssetTypeActions/AssetTypeActions_Identity.h"
|
||||
#include "AssetTypeActions/AssetTypeActions_LLMProvider.h"
|
||||
@@ -11,12 +13,16 @@
|
||||
#include "AssetToolsModule.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "IAssetTypeActions.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Morph/Editor/LipSyncMappingAssetTypeActions.h"
|
||||
#include "Morph/Editor/MorphPresetAssetTypeActions.h"
|
||||
#include "Morph/MorphPresetDataAsset.h"
|
||||
#include "Morph/Editor/MorphPresetDetailsCustomization.h"
|
||||
#include "PropertyEditorModule.h"
|
||||
#include "ToolMenus.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FunnyGirlEditor"
|
||||
|
||||
@@ -44,10 +50,18 @@ void FFunnyGirlEditorModule::StartupModule()
|
||||
FOnGetDetailCustomizationInstance::CreateStatic(&FMorphPresetDetailsCustomization::MakeInstance));
|
||||
PropertyEditorModule.NotifyCustomizationModuleChanged();
|
||||
bMorphPresetCustomizationRegistered = true;
|
||||
|
||||
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FFunnyGirlEditorModule::RegisterMenus));
|
||||
}
|
||||
|
||||
void FFunnyGirlEditorModule::ShutdownModule()
|
||||
{
|
||||
if (UObjectInitialized() && FModuleManager::Get().IsModuleLoaded(TEXT("ToolMenus")))
|
||||
{
|
||||
UToolMenus::UnRegisterStartupCallback(this);
|
||||
UToolMenus::UnregisterOwner(this);
|
||||
}
|
||||
|
||||
if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetTools")))
|
||||
{
|
||||
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
@@ -88,6 +102,91 @@ void FFunnyGirlEditorModule::RegisterAssetTypeAction(const TSharedRef<IAssetType
|
||||
RegisteredAssetTypeActions.Add(AssetTypeAction);
|
||||
}
|
||||
|
||||
void FFunnyGirlEditorModule::RegisterMenus()
|
||||
{
|
||||
FToolMenuOwnerScoped OwnerScoped(this);
|
||||
|
||||
UToolMenu* ToolsMenu = UToolMenus::Get()->ExtendMenu(TEXT("LevelEditor.MainMenu.Tools"));
|
||||
if (!ToolsMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FToolMenuSection& Section = ToolsMenu->FindOrAddSection(TEXT("FunnyGirl"));
|
||||
Section.Label = LOCTEXT("FunnyGirlToolsSection", "Funny Girl");
|
||||
Section.AddMenuEntry(
|
||||
TEXT("ExportMorphPresetDescriptions"),
|
||||
LOCTEXT("ExportMorphPresetDescriptionsLabel", "Export Morph Preset Descriptions"),
|
||||
LOCTEXT("ExportMorphPresetDescriptionsTooltip", "Export all Morph Preset assets to Saved/Exports/MorphPresetDescriptions.tsv."),
|
||||
FSlateIcon(),
|
||||
FUIAction(FExecuteAction::CreateRaw(this, &FFunnyGirlEditorModule::ExportMorphPresetDescriptions)));
|
||||
}
|
||||
|
||||
void FFunnyGirlEditorModule::ExportMorphPresetDescriptions()
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||||
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
|
||||
AssetRegistry.SearchAllAssets(true);
|
||||
|
||||
FARFilter Filter;
|
||||
Filter.ClassPaths.Add(UMorphPresetDataAsset::StaticClass()->GetClassPathName());
|
||||
Filter.bRecursiveClasses = true;
|
||||
|
||||
TArray<FAssetData> PresetAssets;
|
||||
AssetRegistry.GetAssets(Filter, PresetAssets);
|
||||
PresetAssets.Sort([](const FAssetData& A, const FAssetData& B)
|
||||
{
|
||||
return A.GetSoftObjectPath().ToString() < B.GetSoftObjectPath().ToString();
|
||||
});
|
||||
|
||||
FString Output;
|
||||
Output += TEXT("AssetName\tPresetName\tDescription\n");
|
||||
|
||||
for (const FAssetData& AssetData : PresetAssets)
|
||||
{
|
||||
const UMorphPresetDataAsset* Preset = Cast<UMorphPresetDataAsset>(AssetData.GetAsset());
|
||||
if (!Preset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FString AssetName = AssetData.AssetName.ToString();
|
||||
FString PresetName = Preset->DisplayName.ToString().TrimStartAndEnd();
|
||||
FString Description = Preset->Description.ToString().TrimStartAndEnd();
|
||||
|
||||
if (PresetName.IsEmpty())
|
||||
{
|
||||
PresetName = AssetName;
|
||||
}
|
||||
|
||||
AssetName.ReplaceInline(TEXT("\t"), TEXT(" "));
|
||||
PresetName.ReplaceInline(TEXT("\t"), TEXT(" "));
|
||||
Description.ReplaceInline(TEXT("\t"), TEXT(" "));
|
||||
Description.ReplaceInline(TEXT("\r\n"), TEXT(" "));
|
||||
Description.ReplaceInline(TEXT("\n"), TEXT(" "));
|
||||
Description.ReplaceInline(TEXT("\r"), TEXT(" "));
|
||||
|
||||
Output += FString::Printf(TEXT("%s\t%s\t%s\n"), *AssetName, *PresetName, *Description);
|
||||
}
|
||||
|
||||
const FString ExportDir = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Exports"));
|
||||
IFileManager::Get().MakeDirectory(*ExportDir, true);
|
||||
const FString ExportPath = FPaths::Combine(ExportDir, TEXT("MorphPresetDescriptions.tsv"));
|
||||
|
||||
if (!FFileHelper::SaveStringToFile(Output, *ExportPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
|
||||
{
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(
|
||||
LOCTEXT("ExportMorphPresetDescriptionsFailed", "Failed to write Morph Preset descriptions to:\n{0}"),
|
||||
FText::FromString(ExportPath)));
|
||||
return;
|
||||
}
|
||||
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(
|
||||
LOCTEXT("ExportMorphPresetDescriptionsSuccess", "Exported {0} Morph Preset assets to:\n{1}"),
|
||||
FText::AsNumber(PresetAssets.Num()),
|
||||
FText::FromString(ExportPath)));
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FFunnyGirlEditorModule, FunnyGirlEditor)
|
||||
|
||||
@@ -24,4 +24,6 @@ private:
|
||||
bool bMorphPresetCustomizationRegistered = false;
|
||||
|
||||
void RegisterAssetTypeAction(const TSharedRef<IAssetTypeActions>& AssetTypeAction);
|
||||
void RegisterMenus();
|
||||
void ExportMorphPresetDescriptions();
|
||||
};
|
||||
|
||||
@@ -60,6 +60,10 @@ namespace
|
||||
TargetPreset.DisplayName = SourcePreset.DisplayName;
|
||||
TargetPreset.Description = SourcePreset.Description;
|
||||
TargetPreset.MorphTargets = SourcePreset.MorphTargets;
|
||||
TargetPreset.Montage = SourcePreset.Montage;
|
||||
TargetPreset.MontagePlayRate = SourcePreset.MontagePlayRate;
|
||||
TargetPreset.MontageStartTime = SourcePreset.MontageStartTime;
|
||||
TargetPreset.PreviewIKRetargeter = SourcePreset.PreviewIKRetargeter;
|
||||
TargetPreset.Duration = SourcePreset.Duration;
|
||||
TargetPreset.bLoop = SourcePreset.bLoop;
|
||||
TargetPreset.LoopDelay = SourcePreset.LoopDelay;
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "Morph/Editor/MorphPresetEditorController.h"
|
||||
|
||||
#include "Animation/AnimData/IAnimationDataController.h"
|
||||
#include "Animation/AnimCompositeBase.h"
|
||||
#include "Animation/AnimMontage.h"
|
||||
#include "Animation/AnimSingleNodeInstance.h"
|
||||
#include "AnimPreviewInstance.h"
|
||||
#include "Animation/AnimSequence.h"
|
||||
#include "Animation/DebugSkelMeshComponent.h"
|
||||
@@ -18,8 +21,10 @@
|
||||
#include "Morph/LipSyncMappingDataAsset.h"
|
||||
#include "Morph/MorphControllerComponent.h"
|
||||
#include "Morph/MorphPresetDataAsset.h"
|
||||
#include "Morph/Editor/MorphPresetRetargetPreviewAnimInstance.h"
|
||||
#include "PersonaModule.h"
|
||||
#include "ReferenceSkeleton.h"
|
||||
#include "Retargeter/IKRetargeter.h"
|
||||
#include "Sound/SoundBase.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogMorphPresetEditorPreview, Log, All);
|
||||
@@ -31,6 +36,7 @@ namespace
|
||||
IAnimationDataController& Controller = AnimSequence.GetController();
|
||||
Controller.SetNumberOfFrames(FMath::Max(Controller.ConvertSecondsToFrameNumber(PlayLength).Value, 1), bShouldTransact);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FMorphPresetEditorController::FMorphPresetEditorController(UMorphPresetDataAsset* InPreset)
|
||||
@@ -45,6 +51,7 @@ FMorphPresetEditorController::~FMorphPresetEditorController()
|
||||
{
|
||||
StopLipSyncPreviewAudio();
|
||||
DestroyLipSyncPreviewComponents();
|
||||
DestroyAnimationPreviewMesh();
|
||||
|
||||
if (PersonaToolkit.IsValid())
|
||||
{
|
||||
@@ -76,6 +83,36 @@ USkeletalMesh* FMorphPresetEditorController::GetPreviewMesh() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USkeletalMesh* FMorphPresetEditorController::GetAnimationPreviewMesh() const
|
||||
{
|
||||
if (UIKRetargeter* Retargeter = GetPreviewIKRetargeter())
|
||||
{
|
||||
return Retargeter->GetPreviewMesh(ERetargetSourceOrTarget::Source);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UAnimMontage* FMorphPresetEditorController::GetPreviewMontage() const
|
||||
{
|
||||
if (const UMorphPresetDataAsset* PresetAsset = Preset.Get())
|
||||
{
|
||||
return PresetAsset->Montage.LoadSynchronous();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIKRetargeter* FMorphPresetEditorController::GetPreviewIKRetargeter() const
|
||||
{
|
||||
if (const UMorphPresetDataAsset* PresetAsset = Preset.Get())
|
||||
{
|
||||
return PresetAsset->PreviewIKRetargeter.LoadSynchronous();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FMorphPresetEditorController::SetPreviewMesh(USkeletalMesh* InPreviewMesh)
|
||||
{
|
||||
UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
@@ -93,7 +130,71 @@ bool FMorphPresetEditorController::SetPreviewMesh(USkeletalMesh* InPreviewMesh)
|
||||
PresetAsset->PreviewMesh = InPreviewMesh;
|
||||
PresetAsset->MarkPackageDirty();
|
||||
|
||||
RebuildPreviewScene();
|
||||
if (!InPreviewMesh || !GetPreviewScene().IsValid())
|
||||
{
|
||||
RebuildPreviewScene();
|
||||
PresetChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene())
|
||||
{
|
||||
StopLipSyncPreviewAudio();
|
||||
DestroyLipSyncPreviewComponents();
|
||||
DestroyAnimationPreviewMesh();
|
||||
PreviewScene->SetPreviewMesh(InPreviewMesh, false);
|
||||
ActivePreviewMesh = InPreviewMesh;
|
||||
RebuildPreviewAnimation();
|
||||
RebuildAnimationPreviewMesh();
|
||||
RebuildLipSyncPreviewComponents();
|
||||
SetCurrentTime(CurrentTime);
|
||||
PresetChanged.Broadcast();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FMorphPresetEditorController::SetPreviewMontage(UAnimMontage* InMontage)
|
||||
{
|
||||
UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
if (!PresetAsset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PresetAsset->Montage.Get() == InMontage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PresetAsset->Modify();
|
||||
PresetAsset->Montage = InMontage;
|
||||
PresetAsset->MarkPackageDirty();
|
||||
|
||||
RebuildAnimationPreviewMesh();
|
||||
PlaybackStateChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FMorphPresetEditorController::SetPreviewIKRetargeter(UIKRetargeter* InRetargeter)
|
||||
{
|
||||
UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
if (!PresetAsset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PresetAsset->PreviewIKRetargeter.Get() == InRetargeter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PresetAsset->Modify();
|
||||
PresetAsset->PreviewIKRetargeter = InRetargeter;
|
||||
PresetAsset->MarkPackageDirty();
|
||||
|
||||
ConfigureRetargetPreview();
|
||||
PlaybackStateChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,7 +213,12 @@ float FMorphPresetEditorController::GetTimelineLength() const
|
||||
{
|
||||
if (const UMorphPresetDataAsset* PresetAsset = Preset.Get())
|
||||
{
|
||||
return FMath::Max(PresetAsset->GetTimelineLength(), 0.1f);
|
||||
float TimelineLength = FMath::Max(PresetAsset->GetTimelineLength(), 0.1f);
|
||||
if (const UAnimMontage* Montage = PresetAsset->GetMontage())
|
||||
{
|
||||
TimelineLength = FMath::Max(TimelineLength, FMath::Max(PresetAsset->MontageStartTime, 0.0f) + Montage->GetPlayLength());
|
||||
}
|
||||
return FMath::Max(TimelineLength, 0.1f);
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
@@ -134,8 +240,11 @@ void FMorphPresetEditorController::SetCurrentTime(float InTime)
|
||||
if (PreviewMeshComponent->PreviewInstance)
|
||||
{
|
||||
PreviewMeshComponent->PreviewInstance->SetPlaying(false);
|
||||
PreviewMeshComponent->PreviewInstance->SetPosition(CurrentTime, false);
|
||||
RefreshPreviewAnimationPose();
|
||||
if (!HasRetargetPreview())
|
||||
{
|
||||
PreviewMeshComponent->PreviewInstance->SetPosition(CurrentTime, false);
|
||||
RefreshPreviewAnimationPose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +333,12 @@ void FMorphPresetEditorController::NotifyPresetEdited(bool bBroadcastPresetChang
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveAnimationPreviewMesh.Get() != GetAnimationPreviewMesh() ||
|
||||
ActivePreviewIKRetargeter.Get() != GetPreviewIKRetargeter())
|
||||
{
|
||||
RebuildAnimationPreviewMesh();
|
||||
}
|
||||
|
||||
if (!PreviewAnimation.IsValid())
|
||||
{
|
||||
RebuildPreviewAnimation();
|
||||
@@ -261,6 +376,12 @@ void FMorphPresetEditorController::TogglePlaying()
|
||||
|
||||
void FMorphPresetEditorController::Tick(float DeltaTime)
|
||||
{
|
||||
if (LastTickFrameNumber == GFrameCounter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LastTickFrameNumber = GFrameCounter;
|
||||
|
||||
if (bIsPlaying)
|
||||
{
|
||||
float NewTime = CurrentTime + DeltaTime;
|
||||
@@ -422,8 +543,12 @@ void FMorphPresetEditorController::RebuildPreviewScene()
|
||||
{
|
||||
StopLipSyncPreviewAudio();
|
||||
DestroyLipSyncPreviewComponents();
|
||||
DestroyAnimationPreviewMesh();
|
||||
PersonaToolkit.Reset();
|
||||
PreviewAnimation.Reset();
|
||||
AnimationPreviewMeshComponent.Reset();
|
||||
ActiveAnimationPreviewMesh.Reset();
|
||||
ActivePreviewIKRetargeter.Reset();
|
||||
|
||||
USkeletalMesh* PreviewMesh = GetPreviewMesh();
|
||||
ActivePreviewMesh = PreviewMesh;
|
||||
@@ -438,6 +563,7 @@ void FMorphPresetEditorController::RebuildPreviewScene()
|
||||
PersonaToolkit = PersonaModule.CreatePersonaToolkit(PreviewMesh, ToolkitArgs);
|
||||
|
||||
RebuildPreviewAnimation();
|
||||
RebuildAnimationPreviewMesh();
|
||||
RebuildLipSyncPreviewComponents();
|
||||
SetCurrentTime(CurrentTime);
|
||||
PreviewSceneChanged.Broadcast();
|
||||
@@ -483,6 +609,205 @@ void FMorphPresetEditorController::RebuildPreviewAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::RebuildAnimationPreviewMesh()
|
||||
{
|
||||
TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene();
|
||||
if (!PreviewScene.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DestroyAnimationPreviewMesh();
|
||||
|
||||
UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
USkeletalMesh* AnimationMesh = GetAnimationPreviewMesh();
|
||||
ActiveAnimationPreviewMesh = AnimationMesh;
|
||||
PreviewStatusText = FText::GetEmpty();
|
||||
|
||||
if (!PresetAsset || !AnimationMesh)
|
||||
{
|
||||
ConfigureRetargetPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
AActor* PreviewActor = PreviewScene->GetActor();
|
||||
if (!PreviewActor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USkeletalMeshComponent* AnimationMeshComponent = NewObject<USkeletalMeshComponent>(PreviewActor, NAME_None, RF_Transient);
|
||||
if (!AnimationMeshComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AnimationMeshComponent->SetSkeletalMesh(AnimationMesh);
|
||||
AnimationMeshComponent->SetAnimationMode(EAnimationMode::AnimationSingleNode);
|
||||
AnimationMeshComponent->bSelectable = false;
|
||||
AnimationMeshComponent->SetVisibility(false, true);
|
||||
AnimationMeshComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
|
||||
const FVector SourceMeshOffset = GetPreviewIKRetargeter() ? GetPreviewIKRetargeter()->SourceMeshOffset : FVector(0.0f, 80.0f, 0.0f);
|
||||
PreviewScene->AddComponent(AnimationMeshComponent, FTransform(SourceMeshOffset));
|
||||
AnimationPreviewMeshComponent = AnimationMeshComponent;
|
||||
|
||||
if (UAnimMontage* Montage = GetPreviewMontage())
|
||||
{
|
||||
float MontageAssetTime = 0.0f;
|
||||
float MontageAssetLength = Montage->GetPlayLength();
|
||||
UAnimationAsset* PreviewAsset = ResolveMontagePreviewAnimation(MontageAssetTime, MontageAssetLength);
|
||||
AnimationMeshComponent->SetAnimation(PreviewAsset ? PreviewAsset : Montage);
|
||||
AnimationMeshComponent->SetPlayRate(1.0f);
|
||||
AnimationMeshComponent->SetPosition(FMath::Clamp(MontageAssetTime, 0.0f, MontageAssetLength), false);
|
||||
if (UAnimSingleNodeInstance* SingleNodeInstance = AnimationMeshComponent->GetSingleNodeInstance())
|
||||
{
|
||||
SingleNodeInstance->SetLooping(PresetAsset->bLoop);
|
||||
SingleNodeInstance->SetPlaying(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PreviewStatusText = FText::FromString(TEXT("Animation Preview Mesh is set, but this preset has no Montage assigned."));
|
||||
}
|
||||
|
||||
ConfigureRetargetPreview();
|
||||
|
||||
RefreshAnimationPreviewPose();
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::DestroyAnimationPreviewMesh()
|
||||
{
|
||||
ClearRetargetPreview(false);
|
||||
|
||||
if (TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene())
|
||||
{
|
||||
if (USkeletalMeshComponent* ExistingComponent = AnimationPreviewMeshComponent.Get())
|
||||
{
|
||||
PreviewScene->RemoveComponent(ExistingComponent);
|
||||
ExistingComponent->DestroyComponent();
|
||||
}
|
||||
}
|
||||
|
||||
AnimationPreviewMeshComponent.Reset();
|
||||
ActiveAnimationPreviewMesh.Reset();
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::ConfigureRetargetPreview()
|
||||
{
|
||||
TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene();
|
||||
if (!PreviewScene.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UDebugSkelMeshComponent* PreviewMeshComponent = PreviewScene->GetPreviewMeshComponent();
|
||||
USkeletalMeshComponent* SourceMeshComponent = AnimationPreviewMeshComponent.Get();
|
||||
UIKRetargeter* Retargeter = GetPreviewIKRetargeter();
|
||||
if (!PreviewMeshComponent || !SourceMeshComponent || !Retargeter)
|
||||
{
|
||||
ClearRetargetPreview(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearRetargetPreview(false);
|
||||
|
||||
const FName InstanceName = MakeUniqueObjectName(PreviewMeshComponent, UMorphPresetRetargetPreviewAnimInstance::StaticClass(), TEXT("MorphPresetTargetRetargetPreviewInstance"));
|
||||
UMorphPresetRetargetPreviewAnimInstance* RetargetAnimInstance = NewObject<UMorphPresetRetargetPreviewAnimInstance>(PreviewMeshComponent, InstanceName, RF_Transient);
|
||||
if (!RetargetAnimInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PreviewMeshComponent->SetAnimationMode(EAnimationMode::AnimationCustomMode);
|
||||
PreviewMeshComponent->AnimScriptInstance = RetargetAnimInstance;
|
||||
PreviewMeshComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
|
||||
PreviewMeshComponent->SetProcessRootMotionMode(EProcessRootMotionMode::Ignore);
|
||||
RetargetAnimInstance->Configure(Retargeter, SourceMeshComponent);
|
||||
RetargetAnimInstance->InitializeAnimation();
|
||||
RetargetPreviewInstance = RetargetAnimInstance;
|
||||
ActivePreviewIKRetargeter = Retargeter;
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::ClearRetargetPreview(bool bRestorePreviewAnimation)
|
||||
{
|
||||
TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene();
|
||||
UDebugSkelMeshComponent* PreviewMeshComponent = PreviewScene.IsValid() ? PreviewScene->GetPreviewMeshComponent() : nullptr;
|
||||
UMorphPresetRetargetPreviewAnimInstance* RetargetAnimInstance = RetargetPreviewInstance.Get();
|
||||
|
||||
if (PreviewMeshComponent && RetargetAnimInstance && PreviewMeshComponent->AnimScriptInstance == RetargetAnimInstance)
|
||||
{
|
||||
PreviewMeshComponent->ClearAnimScriptInstance();
|
||||
}
|
||||
|
||||
RetargetPreviewInstance.Reset();
|
||||
if (ActivePreviewIKRetargeter.IsValid())
|
||||
{
|
||||
ActivePreviewIKRetargeter.Reset();
|
||||
if (bRestorePreviewAnimation)
|
||||
{
|
||||
RebuildPreviewAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UAnimationAsset* FMorphPresetEditorController::ResolveMontagePreviewAnimation(float& OutAssetTime, float& OutAssetLength) const
|
||||
{
|
||||
OutAssetTime = 0.0f;
|
||||
OutAssetLength = 0.0f;
|
||||
|
||||
const UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
UAnimMontage* Montage = GetPreviewMontage();
|
||||
if (!PresetAsset || !Montage)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const float MontageTrackTime = FMath::Clamp(CurrentTime - FMath::Max(PresetAsset->MontageStartTime, 0.0f), 0.0f, Montage->GetPlayLength());
|
||||
|
||||
const FAnimSegment* FirstSegment = nullptr;
|
||||
const FAnimSegment* LastSegment = nullptr;
|
||||
for (const FSlotAnimationTrack& SlotTrack : Montage->SlotAnimTracks)
|
||||
{
|
||||
for (const FAnimSegment& Segment : SlotTrack.AnimTrack.AnimSegments)
|
||||
{
|
||||
if (!Segment.GetAnimReference())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FirstSegment)
|
||||
{
|
||||
FirstSegment = &Segment;
|
||||
}
|
||||
LastSegment = &Segment;
|
||||
if (Segment.IsInRange(MontageTrackTime))
|
||||
{
|
||||
OutAssetTime = Segment.ConvertTrackPosToAnimPos(MontageTrackTime);
|
||||
OutAssetLength = Segment.GetAnimReference()->GetPlayLength();
|
||||
return Segment.GetAnimReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FAnimSegment* FallbackSegment = MontageTrackTime <= 0.0f ? FirstSegment : LastSegment;
|
||||
if (FallbackSegment && FallbackSegment->GetAnimReference())
|
||||
{
|
||||
const float SegmentTrackTime = FMath::Clamp(MontageTrackTime, FallbackSegment->StartPos, FallbackSegment->GetEndPos());
|
||||
OutAssetTime = FallbackSegment->ConvertTrackPosToAnimPos(SegmentTrackTime);
|
||||
OutAssetLength = FallbackSegment->GetAnimReference()->GetPlayLength();
|
||||
return FallbackSegment->GetAnimReference();
|
||||
}
|
||||
|
||||
OutAssetLength = Montage->GetPlayLength();
|
||||
OutAssetTime = MontageTrackTime;
|
||||
return Montage;
|
||||
}
|
||||
|
||||
bool FMorphPresetEditorController::HasRetargetPreview() const
|
||||
{
|
||||
return ActivePreviewIKRetargeter.IsValid() && AnimationPreviewMeshComponent.IsValid() && RetargetPreviewInstance.IsValid();
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::RefreshPreviewAnimationPose()
|
||||
{
|
||||
TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene();
|
||||
@@ -503,6 +828,36 @@ void FMorphPresetEditorController::RefreshPreviewAnimationPose()
|
||||
PreviewScene->InvalidateViews();
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::RefreshAnimationPreviewPose()
|
||||
{
|
||||
const UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
TSharedPtr<IPersonaPreviewScene> PreviewScene = GetPreviewScene();
|
||||
USkeletalMeshComponent* AnimationMeshComponent = AnimationPreviewMeshComponent.Get();
|
||||
if (!PresetAsset || !PreviewScene.IsValid() || !AnimationMeshComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UAnimSingleNodeInstance* SingleNodeInstance = AnimationMeshComponent->GetSingleNodeInstance())
|
||||
{
|
||||
float MontageAssetTime = 0.0f;
|
||||
float MontageAssetLength = 0.0f;
|
||||
UAnimationAsset* PreviewAsset = ResolveMontagePreviewAnimation(MontageAssetTime, MontageAssetLength);
|
||||
if (PreviewAsset && SingleNodeInstance->GetCurrentAsset() != PreviewAsset)
|
||||
{
|
||||
SingleNodeInstance->SetAnimationAsset(PreviewAsset, PresetAsset->bLoop);
|
||||
}
|
||||
|
||||
SingleNodeInstance->SetPlaying(false);
|
||||
SingleNodeInstance->SetPosition(FMath::Clamp(MontageAssetTime, 0.0f, MontageAssetLength), false);
|
||||
}
|
||||
|
||||
AnimationMeshComponent->TickAnimation(0.0f, false);
|
||||
AnimationMeshComponent->RefreshBoneTransforms();
|
||||
AnimationMeshComponent->MarkRenderStateDirty();
|
||||
PreviewScene->InvalidateViews();
|
||||
}
|
||||
|
||||
void FMorphPresetEditorController::ApplyPresetToPreview()
|
||||
{
|
||||
const UMorphPresetDataAsset* PresetAsset = Preset.Get();
|
||||
@@ -538,6 +893,7 @@ void FMorphPresetEditorController::ApplyPresetToPreview()
|
||||
PreviewMeshComponent->SetMorphTarget(Pair.Key, Pair.Value, false);
|
||||
}
|
||||
|
||||
RefreshAnimationPreviewPose();
|
||||
RefreshPreviewAnimationPose();
|
||||
}
|
||||
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "Morph/Editor/MorphPresetRetargetPreviewAnimInstance.h"
|
||||
|
||||
#include "Retargeter/IKRetargeter.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(MorphPresetRetargetPreviewAnimInstance)
|
||||
|
||||
FMorphPresetRetargetPreviewAnimInstanceProxy::FMorphPresetRetargetPreviewAnimInstanceProxy(
|
||||
UAnimInstance* InAnimInstance,
|
||||
FAnimNode_RetargetPoseFromMesh* InRetargetNode)
|
||||
: FAnimInstanceProxy(InAnimInstance)
|
||||
, RetargetNode(InRetargetNode)
|
||||
{
|
||||
}
|
||||
|
||||
void FMorphPresetRetargetPreviewAnimInstanceProxy::Initialize(UAnimInstance* InAnimInstance)
|
||||
{
|
||||
FAnimInstanceProxy::Initialize(InAnimInstance);
|
||||
|
||||
FAnimationInitializeContext InitContext(this);
|
||||
RetargetNode->Initialize_AnyThread(InitContext);
|
||||
}
|
||||
|
||||
void FMorphPresetRetargetPreviewAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
|
||||
{
|
||||
FAnimInstanceProxy::PreUpdate(InAnimInstance, DeltaSeconds);
|
||||
|
||||
if (RetargetNode && RetargetNode->HasPreUpdate())
|
||||
{
|
||||
RetargetNode->PreUpdate(InAnimInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void FMorphPresetRetargetPreviewAnimInstanceProxy::CacheBones()
|
||||
{
|
||||
if (bBoneCachesInvalidated && RetargetNode)
|
||||
{
|
||||
FAnimationCacheBonesContext Context(this);
|
||||
RetargetNode->CacheBones_AnyThread(Context);
|
||||
bBoneCachesInvalidated = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FMorphPresetRetargetPreviewAnimInstanceProxy::Evaluate(FPoseContext& Output)
|
||||
{
|
||||
FAnimInstanceProxy::Evaluate(Output);
|
||||
|
||||
if (RetargetNode)
|
||||
{
|
||||
RetargetNode->Evaluate_AnyThread(Output);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FMorphPresetRetargetPreviewAnimInstanceProxy::UpdateAnimationNode(const FAnimationUpdateContext& InContext)
|
||||
{
|
||||
FAnimInstanceProxy::UpdateAnimationNode(InContext);
|
||||
UpdateCounter.Increment();
|
||||
|
||||
if (RetargetNode)
|
||||
{
|
||||
RetargetNode->Update_AnyThread(InContext);
|
||||
}
|
||||
}
|
||||
|
||||
void FMorphPresetRetargetPreviewAnimInstanceProxy::Configure(
|
||||
UIKRetargeter* InRetargeter,
|
||||
TWeakObjectPtr<USkeletalMeshComponent> InSourceMeshComponent)
|
||||
{
|
||||
if (!RetargetNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RetargetNode->IKRetargeterAsset = InRetargeter;
|
||||
RetargetNode->RetargetFrom = ERetargetSourceMode::CustomSkeletalMeshComponent;
|
||||
RetargetNode->SourceMeshComponent = InSourceMeshComponent;
|
||||
RetargetNode->CustomRetargetProfile = FRetargetProfile();
|
||||
RetargetNode->bSuppressWarnings = false;
|
||||
|
||||
if (FIKRetargetProcessor* Processor = RetargetNode->GetRetargetProcessor())
|
||||
{
|
||||
Processor->SetNeedsInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
UMorphPresetRetargetPreviewAnimInstance::UMorphPresetRetargetPreviewAnimInstance(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
bUseMultiThreadedAnimationUpdate = false;
|
||||
}
|
||||
|
||||
void UMorphPresetRetargetPreviewAnimInstance::Configure(
|
||||
UIKRetargeter* InRetargeter,
|
||||
TWeakObjectPtr<USkeletalMeshComponent> InSourceMeshComponent)
|
||||
{
|
||||
FMorphPresetRetargetPreviewAnimInstanceProxy& Proxy = GetProxyOnGameThread<FMorphPresetRetargetPreviewAnimInstanceProxy>();
|
||||
Proxy.Configure(InRetargeter, InSourceMeshComponent);
|
||||
}
|
||||
|
||||
void UMorphPresetRetargetPreviewAnimInstance::NativeInitializeAnimation()
|
||||
{
|
||||
FMorphPresetRetargetPreviewAnimInstanceProxy& Proxy = GetProxyOnGameThread<FMorphPresetRetargetPreviewAnimInstanceProxy>();
|
||||
Proxy.Initialize(this);
|
||||
}
|
||||
|
||||
FAnimInstanceProxy* UMorphPresetRetargetPreviewAnimInstance::CreateAnimInstanceProxy()
|
||||
{
|
||||
return new FMorphPresetRetargetPreviewAnimInstanceProxy(this, &RetargetNode);
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Morph/MorphPresetDataAsset.h"
|
||||
#include "Morph/Editor/MorphPresetEditorController.h"
|
||||
#include "Morph/AnimTimeline/SAnimEditorBase.h"
|
||||
#include "Animation/AnimMontage.h"
|
||||
#include "Animation/MorphTarget.h"
|
||||
#include "Curves/RichCurve.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
@@ -159,6 +160,152 @@ namespace
|
||||
TWeakPtr<FMorphPresetEditorController> Controller;
|
||||
};
|
||||
|
||||
class SMorphPresetMontageLane : public SLeafWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SMorphPresetMontageLane) {}
|
||||
SLATE_ARGUMENT(TWeakPtr<FMorphPresetEditorController>, Controller)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs)
|
||||
{
|
||||
Controller = InArgs._Controller;
|
||||
}
|
||||
|
||||
virtual FVector2D ComputeDesiredSize(float) const override
|
||||
{
|
||||
return FVector2D(320.0f, 44.0f);
|
||||
}
|
||||
|
||||
virtual int32 OnPaint(const FPaintArgs&, const FGeometry& AllottedGeometry, const FSlateRect&, FSlateWindowElementList& OutDrawElements,
|
||||
int32 LayerId, const FWidgetStyle&, bool) const override
|
||||
{
|
||||
const TSharedPtr<FMorphPresetEditorController> PinnedController = Controller.Pin();
|
||||
const UMorphPresetDataAsset* Preset = PinnedController.IsValid() ? PinnedController->GetPreset() : nullptr;
|
||||
const UAnimMontage* Montage = Preset ? Preset->GetMontage() : nullptr;
|
||||
const float TimelineLength = PinnedController.IsValid() ? FMath::Max(PinnedController->GetTimelineLength(), 0.01f) : 1.0f;
|
||||
const float Width = AllottedGeometry.GetLocalSize().X;
|
||||
const float Height = AllottedGeometry.GetLocalSize().Y;
|
||||
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), FAppStyle::GetBrush("WhiteBrush"),
|
||||
ESlateDrawEffect::None, FLinearColor(0.075f, 0.08f, 0.09f, 1.0f));
|
||||
|
||||
if (Preset && Montage)
|
||||
{
|
||||
const float MontageDuration = Montage->GetPlayLength();
|
||||
const float StartTime = FMath::Max(Preset->MontageStartTime, 0.0f);
|
||||
const float StartX = Width * (StartTime / TimelineLength);
|
||||
const float EndX = Width * ((StartTime + MontageDuration) / TimelineLength);
|
||||
const float BlockWidth = FMath::Max(EndX - StartX, 2.0f);
|
||||
const FLinearColor BlockColor = bIsDragging ? FLinearColor(0.55f, 0.38f, 0.11f, 1.0f) : FLinearColor(0.36f, 0.22f, 0.48f, 1.0f);
|
||||
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 1,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(BlockWidth, Height - 6.0f), FSlateLayoutTransform(FVector2f(StartX, 3.0f))),
|
||||
FAppStyle::GetBrush("WhiteBrush"), ESlateDrawEffect::None, BlockColor);
|
||||
|
||||
const FString Label = FString::Printf(TEXT("%s %.2fs"), *Montage->GetName(), MontageDuration);
|
||||
FSlateDrawElement::MakeText(
|
||||
OutDrawElements,
|
||||
LayerId + 2,
|
||||
AllottedGeometry.ToPaintGeometry(FVector2f(FMath::Max(BlockWidth - 10.0f, 1.0f), Height), FSlateLayoutTransform(FVector2f(StartX + 6.0f, 12.0f))),
|
||||
Label,
|
||||
FAppStyle::GetFontStyle("SmallFont"),
|
||||
ESlateDrawEffect::None,
|
||||
FLinearColor::White);
|
||||
}
|
||||
|
||||
if (PinnedController.IsValid())
|
||||
{
|
||||
const float CurrentX = Width * (PinnedController->GetCurrentTime() / TimelineLength);
|
||||
TArray<FVector2D> Points{ FVector2D(CurrentX, 0.0f), FVector2D(CurrentX, Height) };
|
||||
FSlateDrawElement::MakeLines(OutDrawElements, LayerId + 3, AllottedGeometry.ToPaintGeometry(), Points,
|
||||
ESlateDrawEffect::None, FLinearColor(1.0f, 0.5f, 0.25f, 1.0f), true, 2.0f);
|
||||
}
|
||||
|
||||
return LayerId + 3;
|
||||
}
|
||||
|
||||
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override
|
||||
{
|
||||
if (MouseEvent.GetEffectingButton() != EKeys::LeftMouseButton)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
const TSharedPtr<FMorphPresetEditorController> PinnedController = Controller.Pin();
|
||||
UMorphPresetDataAsset* Preset = PinnedController.IsValid() ? PinnedController->GetPreset() : nullptr;
|
||||
if (!Preset || !Preset->GetMontage())
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
const float Width = FMath::Max(MyGeometry.GetLocalSize().X, 1.0f);
|
||||
const float TimelineLength = FMath::Max(PinnedController->GetTimelineLength(), 0.01f);
|
||||
const float LocalX = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).X;
|
||||
const float MontageDuration = Preset->GetMontage()->GetPlayLength();
|
||||
const float MontageStartX = Width * (FMath::Max(Preset->MontageStartTime, 0.0f) / TimelineLength);
|
||||
const float MontageEndX = Width * ((FMath::Max(Preset->MontageStartTime, 0.0f) + MontageDuration) / TimelineLength);
|
||||
|
||||
if (LocalX < MontageStartX || LocalX > MontageEndX)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
Preset->Modify();
|
||||
InitialStartTime = FMath::Max(Preset->MontageStartTime, 0.0f);
|
||||
InitialTimelineLength = TimelineLength;
|
||||
DragStartLocalX = LocalX;
|
||||
bIsDragging = true;
|
||||
PinnedController->SetPlaying(false);
|
||||
return FReply::Handled().CaptureMouse(AsShared()).PreventThrottling();
|
||||
}
|
||||
|
||||
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override
|
||||
{
|
||||
if (!bIsDragging || !HasMouseCapture())
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
const TSharedPtr<FMorphPresetEditorController> PinnedController = Controller.Pin();
|
||||
UMorphPresetDataAsset* Preset = PinnedController.IsValid() ? PinnedController->GetPreset() : nullptr;
|
||||
if (!Preset)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
const float Width = FMath::Max(MyGeometry.GetLocalSize().X, 1.0f);
|
||||
const float DeltaX = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).X - DragStartLocalX;
|
||||
const float DeltaTime = (DeltaX / Width) * InitialTimelineLength;
|
||||
Preset->MontageStartTime = FMath::Max(InitialStartTime + DeltaTime, 0.0f);
|
||||
PinnedController->NotifyPresetEdited(false);
|
||||
Invalidate(EInvalidateWidgetReason::Paint);
|
||||
return FReply::Handled().PreventThrottling();
|
||||
}
|
||||
|
||||
virtual FReply OnMouseButtonUp(const FGeometry&, const FPointerEvent& MouseEvent) override
|
||||
{
|
||||
if (MouseEvent.GetEffectingButton() != EKeys::LeftMouseButton || !bIsDragging)
|
||||
{
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
bIsDragging = false;
|
||||
if (const TSharedPtr<FMorphPresetEditorController> PinnedController = Controller.Pin())
|
||||
{
|
||||
PinnedController->NotifyPresetEdited(true);
|
||||
}
|
||||
return FReply::Handled().ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
private:
|
||||
TWeakPtr<FMorphPresetEditorController> Controller;
|
||||
float DragStartLocalX = 0.0f;
|
||||
float InitialStartTime = 0.0f;
|
||||
float InitialTimelineLength = 1.0f;
|
||||
bool bIsDragging = false;
|
||||
};
|
||||
|
||||
class SMorphPresetClipLane : public SLeafWidget
|
||||
{
|
||||
public:
|
||||
@@ -1000,6 +1147,29 @@ namespace
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(TimelineEdgePadding, 0.0f, TimelineEdgePadding, 6.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(FMargin(0.0f, 0.0f, 8.0f, 0.0f))
|
||||
[
|
||||
SNew(SBox)
|
||||
.WidthOverride(TimelineNameColumnWidth)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("HeaderMontage", "Montage"))
|
||||
]
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
[
|
||||
SNew(SMorphPresetMontageLane)
|
||||
.Controller(Controller)
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
[
|
||||
SAssignNew(LocalScrollBox, SScrollBox)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AdvancedPreviewScene.h"
|
||||
#include "Animation/AnimMontage.h"
|
||||
#include "Animation/DebugSkelMeshComponent.h"
|
||||
#include "EditorViewportClient.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "Morph/LipSyncMappingDataAsset.h"
|
||||
#include "Morph/Editor/MorphPresetEditorController.h"
|
||||
#include "PropertyCustomizationHelpers.h"
|
||||
#include "Retargeter/IKRetargeter.h"
|
||||
#include "SEditorViewport.h"
|
||||
#include "Sound/SoundBase.h"
|
||||
#include "ToolMenus.h"
|
||||
@@ -200,7 +202,6 @@ void SMorphPresetViewportPanel::Construct(const FArguments& InArgs)
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
Controller->OnPreviewSceneChanged().AddSP(this, &SMorphPresetViewportPanel::RebuildContent);
|
||||
Controller->OnPresetChanged().AddSP(this, &SMorphPresetViewportPanel::RebuildContent);
|
||||
}
|
||||
|
||||
ChildSlot
|
||||
@@ -218,7 +219,6 @@ SMorphPresetViewportPanel::~SMorphPresetViewportPanel()
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
Controller->OnPreviewSceneChanged().RemoveAll(this);
|
||||
Controller->OnPresetChanged().RemoveAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +268,51 @@ void SMorphPresetViewportPanel::RebuildContent()
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 0.0f, 0.0f, 8.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(0.0f, 0.0f, 8.0f, 0.0f)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("PreviewMontageLabel", "Montage"))
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
.Padding(0.0f, 0.0f, 8.0f, 0.0f)
|
||||
[
|
||||
SNew(SObjectPropertyEntryBox)
|
||||
.AllowedClass(UAnimMontage::StaticClass())
|
||||
.ObjectPath(this, &SMorphPresetViewportPanel::GetSelectedPreviewMontagePath)
|
||||
.OnObjectChanged(this, &SMorphPresetViewportPanel::OnPreviewMontageChanged)
|
||||
.DisplayUseSelected(true)
|
||||
.DisplayBrowse(true)
|
||||
.AllowClear(true)
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(12.0f, 0.0f, 8.0f, 0.0f)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(LOCTEXT("PreviewRetargeterLabel", "IK Retargeter"))
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
[
|
||||
SNew(SObjectPropertyEntryBox)
|
||||
.AllowedClass(UIKRetargeter::StaticClass())
|
||||
.ObjectPath(this, &SMorphPresetViewportPanel::GetSelectedPreviewIKRetargeterPath)
|
||||
.OnObjectChanged(this, &SMorphPresetViewportPanel::OnPreviewIKRetargeterChanged)
|
||||
.DisplayUseSelected(true)
|
||||
.DisplayBrowse(true)
|
||||
.AllowClear(true)
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(0.0f, 0.0f, 0.0f, 8.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
@@ -380,6 +425,22 @@ void SMorphPresetViewportPanel::OnPreviewMeshChanged(const FAssetData& AssetData
|
||||
}
|
||||
}
|
||||
|
||||
void SMorphPresetViewportPanel::OnPreviewMontageChanged(const FAssetData& AssetData)
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
Controller->SetPreviewMontage(Cast<UAnimMontage>(AssetData.GetAsset()));
|
||||
}
|
||||
}
|
||||
|
||||
void SMorphPresetViewportPanel::OnPreviewIKRetargeterChanged(const FAssetData& AssetData)
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
Controller->SetPreviewIKRetargeter(Cast<UIKRetargeter>(AssetData.GetAsset()));
|
||||
}
|
||||
}
|
||||
|
||||
void SMorphPresetViewportPanel::OnLipSyncSoundChanged(const FAssetData& AssetData)
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
@@ -409,6 +470,32 @@ FString SMorphPresetViewportPanel::GetSelectedPreviewMeshPath() const
|
||||
return FString();
|
||||
}
|
||||
|
||||
FString SMorphPresetViewportPanel::GetSelectedPreviewMontagePath() const
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
if (UAnimMontage* Montage = Controller->GetPreviewMontage())
|
||||
{
|
||||
return Montage->GetPathName();
|
||||
}
|
||||
}
|
||||
|
||||
return FString();
|
||||
}
|
||||
|
||||
FString SMorphPresetViewportPanel::GetSelectedPreviewIKRetargeterPath() const
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
{
|
||||
if (UIKRetargeter* Retargeter = Controller->GetPreviewIKRetargeter())
|
||||
{
|
||||
return Retargeter->GetPathName();
|
||||
}
|
||||
}
|
||||
|
||||
return FString();
|
||||
}
|
||||
|
||||
FString SMorphPresetViewportPanel::GetSelectedLipSyncSoundPath() const
|
||||
{
|
||||
if (Controller.IsValid())
|
||||
|
||||
Reference in New Issue
Block a user