Files
UnrealEngineUWP/Engine/Source/Editor/DetailCustomizations/Private/AnimSequenceDetails.cpp
thomas sarkanen e832a0d616 Correctly tag custom rows in anim sequence and skeletal mesh details customizations
#rb Jurre.deBaare
#preflight 6246d295323cb7b9911fe6cb

#ROBOMERGE-AUTHOR: thomas.sarkanen
#ROBOMERGE-SOURCE: CL 19586446 via CL 19586824 via CL 19586833 via CL 19586842
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v938-19570697)

[CL 19587520 by thomas sarkanen in ue5-main branch]
2022-04-01 09:41:25 -04:00

666 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimSequenceDetails.h"
#include "Animation/AnimSequence.h"
#include "SlateOptMacros.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/SViewport.h"
#include "Animation/DebugSkelMeshComponent.h"
#include "Viewports.h"
#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
#include "AnimMontageSegmentDetails.h"
#include "AnimPreviewInstance.h"
#include "Slate/SceneViewport.h"
#define LOCTEXT_NAMESPACE "AnimSequenceDetails"
// default name for retarget source
#define DEFAULT_RETARGET_SOURCE_NAME TEXT("Default")
TSharedRef<IDetailCustomization> FAnimSequenceDetails::MakeInstance()
{
return MakeShareable(new FAnimSequenceDetails);
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FAnimSequenceDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
/////////////////////////////////////////////////////////////////////////////////
// retarget source handler in Animation
/////////////////////////////////////////////////////////////////////////////////
IDetailCategoryBuilder& AnimationCategory = DetailBuilder.EditCategory("Animation");
RetargetSourceNameHandler = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, RetargetSource));
RetargetSourceAssetHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, RetargetSourceAsset));
// first create profile combo list
RetargetSourceComboList.Empty();
// first one is default one
RetargetSourceComboList.Add( MakeShareable( new FString (DEFAULT_RETARGET_SOURCE_NAME) ));
// find skeleton
TSharedPtr<IPropertyHandle> SkeletonHanlder = DetailBuilder.GetProperty(TEXT("Skeleton"));
FName CurrentPoseName;
ensure (RetargetSourceNameHandler->GetValue(CurrentPoseName) != FPropertyAccess::Fail);
// Check if we use only one skeleton
USkeleton* Skeleton = NULL;
SelectedAnimSequences.Reset();
TArray< TWeakObjectPtr<UObject> > SelectedObjectsList = DetailBuilder.GetSelectedObjects();
for (auto SelectionIt = SelectedObjectsList.CreateIterator(); SelectionIt; ++SelectionIt)
{
if (UAnimSequence* TestAnimSequence = Cast<UAnimSequence>(SelectionIt->Get()))
{
SelectedAnimSequences.Add(TestAnimSequence);
}
}
// do it in separate loop since before it only cared AnimSequence
for (auto& It : SelectedAnimSequences)
{
// we should only have one selected anim sequence
if(Skeleton && Skeleton != It->GetSkeleton())
{
// Multiple different skeletons
Skeleton = NULL;
break;
}
Skeleton = It->GetSkeleton();
}
// set target skeleton. It can be null
TargetSkeleton = Skeleton;
// find what is initial selection is
TSharedPtr<FString> InitialSelected;
if (TargetSkeleton.IsValid())
{
RegisterRetargetSourceChanged();
// go through profile and see if it has mine
for (auto Iter = TargetSkeleton->AnimRetargetSources.CreateConstIterator(); Iter; ++Iter)
{
RetargetSourceComboList.Add( MakeShareable( new FString ( Iter.Key().ToString() )));
if (Iter.Key() == CurrentPoseName)
{
InitialSelected = RetargetSourceComboList.Last();
}
}
}
// add widget for editing retarget source
AnimationCategory
.AddCustomRow(RetargetSourceNameHandler->GetPropertyDisplayName())
.RowTag(RetargetSourceNameHandler->GetProperty()->GetFName())
.NameContent()
[
RetargetSourceNameHandler->CreatePropertyNameWidget()
]
.ValueContent()
[
SAssignNew(RetargetSourceComboBox, SSearchableComboBox)
.OptionsSource(&RetargetSourceComboList)
.OnGenerateWidget(this, &FAnimSequenceDetails::MakeRetargetSourceComboWidget)
.OnSelectionChanged(this, &FAnimSequenceDetails::OnRetargetSourceChanged)
.OnComboBoxOpening(this, &FAnimSequenceDetails::OnRetargetSourceComboOpening)
.InitiallySelectedItem(InitialSelected)
.IsEnabled( FSlateApplication::Get().GetNormalExecutionAttribute() )
.ContentPadding(0)
.Content()
[
SNew( STextBlock )
.Text(this, &FAnimSequenceDetails::GetRetargetSourceComboBoxContent)
.Font( IDetailLayoutBuilder::GetDetailFont() )
.ToolTipText(this, &FAnimSequenceDetails::GetRetargetSourceComboBoxToolTip)
]
];
AnimationCategory
.AddCustomRow(RetargetSourceAssetHandle->GetPropertyDisplayName())
.RowTag(RetargetSourceAssetHandle->GetProperty()->GetFName())
.NameContent()
[
RetargetSourceAssetHandle->CreatePropertyNameWidget()
]
.ValueContent()
.HAlign(EHorizontalAlignment::HAlign_Fill)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.HAlign(EHorizontalAlignment::HAlign_Left)
[
RetargetSourceAssetHandle->CreatePropertyValueWidget()
]
+SVerticalBox::Slot()
.AutoHeight()
.HAlign(EHorizontalAlignment::HAlign_Left)
[
SNew(SButton)
.Text(LOCTEXT("UpdateRetargetSourceAssetDataButton", "Update"))
.ToolTipText(LOCTEXT("UpdateRetargetSourceAssetDataButtonToolTip", "Updates retargeting data for RetargetSourceAsset. This is updated automatically at save, but you can click here to update without saving."))
.Visibility(this, &FAnimSequenceDetails::UpdateRetargetSourceAssetDataVisibility)
.OnClicked(this, &FAnimSequenceDetails::UpdateRetargetSourceAssetData)
]
];
DetailBuilder.HideProperty(RetargetSourceNameHandler);
DetailBuilder.HideProperty(RetargetSourceAssetHandle);
/////////////////////////////////////////////////////////////////////////////
// Additive settings category
/////////////////////////////////////////////////////////////////////////////
// now customize to combo box
IDetailCategoryBuilder& AdditiveSettingsCategory = DetailBuilder.EditCategory("AdditiveSettings");
// hide all properties for additive anim and replace them with custom additive settings setup
AdditiveAnimTypeHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, AdditiveAnimType));
RefPoseTypeHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, RefPoseType));
RefPoseSeqHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, RefPoseSeq));
RefFrameIndexHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimSequence, RefFrameIndex));
CreateOverridenProperty(DetailBuilder, AdditiveSettingsCategory, AdditiveAnimTypeHandle, TAttribute<EVisibility>( EVisibility::Visible ));
CreateOverridenProperty(DetailBuilder, AdditiveSettingsCategory, RefPoseTypeHandle, TAttribute<EVisibility>( this, &FAnimSequenceDetails::ShouldShowRefPoseType ));
DetailBuilder.HideProperty(RefPoseSeqHandle);
AdditiveSettingsCategory.AddCustomRow(RefPoseSeqHandle->GetPropertyDisplayName())
.Visibility(TAttribute<EVisibility>( this, &FAnimSequenceDetails::ShouldShowRefAnimInfo ))
.NameContent()
[
RefPoseSeqHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
RefPoseSeqHandle->CreatePropertyValueWidget()
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SAnimationRefPoseViewport)
.Skeleton(TargetSkeleton.Get())
.AnimRefPropertyHandle(RefPoseSeqHandle)
.RefPoseTypeHandle(RefPoseTypeHandle)
.RefFrameIndexPropertyHandle(RefFrameIndexHandle)
]
];
CreateOverridenProperty(DetailBuilder, AdditiveSettingsCategory, RefFrameIndexHandle, TAttribute<EVisibility>( this, &FAnimSequenceDetails::ShouldShowRefFrameIndex ));
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FAnimSequenceDetails::CreateOverridenProperty(IDetailLayoutBuilder& DetailBuilder, IDetailCategoryBuilder& AdditiveSettingsCategory, TSharedPtr<IPropertyHandle> PropertyHandle, TAttribute<EVisibility> VisibilityAttribute)
{
DetailBuilder.HideProperty(PropertyHandle);
AdditiveSettingsCategory.AddCustomRow(PropertyHandle->GetPropertyDisplayName())
.Visibility(VisibilityAttribute)
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
PropertyHandle->CreatePropertyValueWidget()
];
}
EVisibility FAnimSequenceDetails::ShouldShowRefPoseType() const
{
uint8 AdditiveAnimType = AAT_None;
AdditiveAnimTypeHandle->GetValue(AdditiveAnimType);
return AdditiveAnimType != AAT_None? EVisibility::Visible : EVisibility::Hidden;
}
EVisibility FAnimSequenceDetails::ShouldShowRefAnimInfo() const
{
uint8 AdditiveAnimType = AAT_None;
uint8 RefPoseType = ABPT_None;
AdditiveAnimTypeHandle->GetValue(AdditiveAnimType);
RefPoseTypeHandle->GetValue(RefPoseType);
return TargetSkeleton.IsValid() && AdditiveAnimType != AAT_None && (RefPoseType == ABPT_AnimScaled || RefPoseType == ABPT_AnimFrame)? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility FAnimSequenceDetails::ShouldShowRefFrameIndex() const
{
uint8 AdditiveAnimType = AAT_None;
uint8 RefPoseType = ABPT_None;
AdditiveAnimTypeHandle->GetValue(AdditiveAnimType);
RefPoseTypeHandle->GetValue(RefPoseType);
return TargetSkeleton.IsValid() && AdditiveAnimType != AAT_None && (RefPoseType == ABPT_AnimFrame || RefPoseType == ABPT_LocalAnimFrame)? EVisibility::Visible : EVisibility::Collapsed;
}
TSharedRef<SWidget> FAnimSequenceDetails::MakeRetargetSourceComboWidget( TSharedPtr<FString> InItem )
{
return SNew(STextBlock) .Text( FText::FromString(*InItem) ) .Font( IDetailLayoutBuilder::GetDetailFont() );
}
void FAnimSequenceDetails::DelegateRetargetSourceChanged()
{
if (TargetSkeleton.IsValid())
{
// first create profile combo list
RetargetSourceComboList.Empty();
// first one is default one
RetargetSourceComboList.Add( MakeShareable( new FString (DEFAULT_RETARGET_SOURCE_NAME) ));
// go through profile and see if it has mine
for (auto Iter = TargetSkeleton->AnimRetargetSources.CreateConstIterator(); Iter; ++Iter)
{
RetargetSourceComboList.Add( MakeShareable( new FString ( Iter.Key().ToString() )));
}
RetargetSourceComboBox->RefreshOptions();
}
}
void FAnimSequenceDetails::RegisterRetargetSourceChanged()
{
if (TargetSkeleton.IsValid() && !OnDelegateRetargetSourceChanged.IsBound())
{
OnDelegateRetargetSourceChanged = USkeleton::FOnRetargetSourceChanged::CreateSP( this, &FAnimSequenceDetails::DelegateRetargetSourceChanged );
OnDelegateRetargetSourceChangedDelegateHandle = TargetSkeleton->RegisterOnRetargetSourceChanged(OnDelegateRetargetSourceChanged);
}
}
FAnimSequenceDetails::~FAnimSequenceDetails()
{
if (TargetSkeleton.IsValid() && OnDelegateRetargetSourceChanged.IsBound())
{
TargetSkeleton->UnregisterOnRetargetSourceChanged(OnDelegateRetargetSourceChangedDelegateHandle);
}
}
void FAnimSequenceDetails::OnRetargetSourceComboOpening()
{
FName RetargetSourceName;
if (RetargetSourceNameHandler->GetValue(RetargetSourceName) != FPropertyAccess::Result::MultipleValues)
{
TSharedPtr<FString> ComboStringPtr = GetRetargetSourceString(RetargetSourceName);
if( ComboStringPtr.IsValid() )
{
RetargetSourceComboBox->SetSelectedItem(ComboStringPtr);
}
}
}
void FAnimSequenceDetails::OnRetargetSourceChanged( TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo )
{
// if it's set from code, we did that on purpose
if (SelectInfo != ESelectInfo::Direct)
{
FString NewValue = *NewSelection.Get();
if (NewValue == DEFAULT_RETARGET_SOURCE_NAME)
{
NewValue = TEXT("");
}
// set profile set up
ensure ( RetargetSourceNameHandler->SetValue(NewValue) == FPropertyAccess::Result::Success );
}
}
FText FAnimSequenceDetails::GetRetargetSourceComboBoxContent() const
{
FName RetargetSourceName;
if (RetargetSourceNameHandler->GetValue(RetargetSourceName) == FPropertyAccess::Result::MultipleValues)
{
return LOCTEXT("MultipleValues", "Multiple Values");
}
return FText::FromString(*GetRetargetSourceString(RetargetSourceName).Get());
}
FText FAnimSequenceDetails::GetRetargetSourceComboBoxToolTip() const
{
return LOCTEXT("RetargetSourceComboToolTip", "When retargeting, this pose will be used as a base of animation");
}
TSharedPtr<FString> FAnimSequenceDetails::GetRetargetSourceString(FName RetargetSourceName) const
{
FString RetargetSourceString = RetargetSourceName.ToString();
// go through profile and see if it has mine
for (int32 Index=1; Index<RetargetSourceComboList.Num(); ++Index)
{
if (RetargetSourceString == *RetargetSourceComboList[Index])
{
return RetargetSourceComboList[Index];
}
}
return RetargetSourceComboList[0];
}
EVisibility FAnimSequenceDetails::UpdateRetargetSourceAssetDataVisibility() const
{
for (const TWeakObjectPtr<UAnimSequence>& WeakAnimSequence : SelectedAnimSequences)
{
if (UAnimSequence* AnimSequence = WeakAnimSequence.Get())
{
if (!AnimSequence->RetargetSourceAsset.IsNull())
{
return EVisibility::Visible;
}
}
}
return EVisibility::Collapsed;
}
FReply FAnimSequenceDetails::UpdateRetargetSourceAssetData()
{
RetargetSourceAssetHandle->NotifyPostChange(EPropertyChangeType::Unspecified);
return FReply::Handled();
}
////////////////////////////////////////////////
// based on SAnimationSegmentViewport
SAnimationRefPoseViewport::~SAnimationRefPoseViewport()
{
// clean up components
if (PreviewComponent)
{
for (int32 I=PreviewComponent->GetAttachChildren().Num()-1; I >= 0; --I) // Iterate backwards because CleanupComponent will remove from AttachChildren
{
// PreviewComponet will be cleaned up by PreviewScene,
// but if anything is attached, it won't be cleaned up,
// so we'll need to clean them up manually
CleanupComponent(PreviewComponent->GetAttachChildren()[I]);
}
check(PreviewComponent->GetAttachChildren().Num() == 0);
}
// Close viewport
if (LevelViewportClient.IsValid())
{
LevelViewportClient->Viewport = NULL;
}
}
void SAnimationRefPoseViewport::CleanupComponent(USceneComponent* Component)
{
if (Component)
{
for (int32 I = Component->GetAttachChildren().Num() - 1; I >= 0; --I) // Iterate backwards because CleanupComponent will remove from AttachChildren
{
CleanupComponent(Component->GetAttachChildren()[I]);
}
check(Component->GetAttachChildren().Num() == 0);
Component->DestroyComponent();
}
}
SAnimationRefPoseViewport::SAnimationRefPoseViewport()
: PreviewScene(FPreviewScene::ConstructionValues())
, PreviewComponent(nullptr)
{
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAnimationRefPoseViewport::Construct(const FArguments& InArgs)
{
TargetSkeleton = InArgs._Skeleton;
AnimRefPropertyHandle = InArgs._AnimRefPropertyHandle;
RefPoseTypeHandle = InArgs._RefPoseTypeHandle;
RefFrameIndexPropertyHandle = InArgs._RefFrameIndexPropertyHandle;
// Create the preview component
PreviewComponent = NewObject<UDebugSkelMeshComponent>();
PreviewComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
PreviewScene.AddComponent( PreviewComponent, FTransform::Identity );
this->ChildSlot
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(Description, STextBlock)
.Text(LOCTEXT("DefaultViewportLabel", "Default View"))
.AutoWrapText(true)
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SBorder)
.HAlign(HAlign_Center)
[
SAssignNew(ViewportWidget, SViewport)
.EnableGammaCorrection(false)
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SAnimationSegmentScrubPanel)
.ViewInputMin(this, &SAnimationRefPoseViewport::GetViewMinInput)
.ViewInputMax(this, &SAnimationRefPoseViewport::GetViewMaxInput)
.PreviewInstance(this, &SAnimationRefPoseViewport::GetPreviewInstance)
.DraggableBars(this, &SAnimationRefPoseViewport::GetBars)
.OnBarDrag(this, &SAnimationRefPoseViewport::OnBarDrag)
.OnTickPlayback(this, &SAnimationRefPoseViewport::OnTickPreview)
.bAllowZoom(true)
]
];
// Create the viewport
LevelViewportClient = MakeShareable( new FAnimationSegmentViewportClient( PreviewScene ) );
LevelViewportClient->ViewportType = LVT_Perspective;
LevelViewportClient->bSetListenerPosition = false;
LevelViewportClient->SetViewLocation( EditorViewportDefs::DefaultPerspectiveViewLocation );
LevelViewportClient->SetViewRotation( EditorViewportDefs::DefaultPerspectiveViewRotation );
SceneViewport = MakeShareable( new FSceneViewport( LevelViewportClient.Get(), ViewportWidget ) );
LevelViewportClient->Viewport = SceneViewport.Get();
LevelViewportClient->SetRealtime( true );
LevelViewportClient->VisibilityDelegate.BindSP( this, &SAnimationRefPoseViewport::IsVisible );
LevelViewportClient->SetViewMode( VMI_Lit );
ViewportWidget->SetViewportInterface( SceneViewport.ToSharedRef() );
InitSkeleton();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAnimationRefPoseViewport::InitSkeleton()
{
UObject *Object = nullptr;
AnimRefPropertyHandle->GetValue(Object);
PreviewAnimationSequence = Cast<UAnimSequence>(Object);
const USkeleton* Skeleton = nullptr;
if(PreviewAnimationSequence != nullptr)
{
Skeleton = PreviewAnimationSequence->GetSkeleton();
}
// if skeleton doesn't match with target skeleton, this is error, we can't support it
if (PreviewComponent && Skeleton && (Skeleton == TargetSkeleton))
{
UAnimSingleNodeInstance* PreviewAnimInstance = PreviewComponent->PreviewInstance;
USkeletalMesh* PreviewSkeletalMesh = [Skeleton, this]() -> USkeletalMesh*
{
// Try preview mesh on Anim Sequence
USkeletalMesh* Mesh = PreviewAnimationSequence->GetPreviewMesh();
// Otherwise try skeleton preview mesh
if (Mesh == nullptr)
{
Mesh = Skeleton->GetPreviewMesh();
}
// Last resort try to find a _any_ compatible mesh for the skeleton
if (Mesh == nullptr)
{
Mesh = Skeleton->FindCompatibleMesh();
}
return Mesh;
}();
const bool bInvalidPreviewInstance = PreviewAnimInstance == nullptr || PreviewAnimInstance->GetCurrentAsset() != PreviewAnimationSequence;
const bool bPreviewMeshMismatch = PreviewComponent->SkeletalMesh != PreviewSkeletalMesh;
if(bInvalidPreviewInstance || bPreviewMeshMismatch)
{
PreviewComponent->SetSkeletalMesh(PreviewSkeletalMesh);
PreviewComponent->EnablePreview(true, PreviewAnimationSequence);
PreviewComponent->PreviewInstance->SetLooping(true);
//Place the camera at a good viewer position
FVector NewPosition = LevelViewportClient->GetViewLocation();
NewPosition.Normalize();
if(PreviewSkeletalMesh)
{
NewPosition *= (PreviewSkeletalMesh->GetImportedBounds().SphereRadius*1.5f);
}
LevelViewportClient->SetViewLocation( NewPosition );
}
}
}
void SAnimationRefPoseViewport::OnTickPreview( double InCurrentTime, float InDeltaTime )
{
LevelViewportClient->Invalidate();
}
void SAnimationRefPoseViewport::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
class UDebugSkelMeshComponent* Component = PreviewComponent;
FString TargetSkeletonName = TargetSkeleton ? TargetSkeleton->GetName() : FName( NAME_None ).ToString();
if ( Component != NULL )
{
// Reinit the skeleton if the anim ref has changed
InitSkeleton();
if ( Component->IsPreviewOn() && PreviewAnimationSequence != NULL )
{
if ( PreviewComponent != NULL && PreviewComponent->PreviewInstance != NULL )
{
uint8 RefPoseType;
RefPoseTypeHandle->GetValue( RefPoseType );
if ( RefPoseType == ABPT_AnimFrame )
{
int RefFrameIndex;
RefFrameIndexPropertyHandle->GetValue( RefFrameIndex );
float Fraction = ( PreviewAnimationSequence->GetNumberOfSampledKeys() > 0 ) ? FMath::Clamp<float>( (float)RefFrameIndex / (float)PreviewAnimationSequence->GetNumberOfSampledKeys(), 0.f, 1.f ) : 0.f;
float RefTime = PreviewAnimationSequence->GetPlayLength() * Fraction;
PreviewComponent->PreviewInstance->SetPosition( RefTime, false );
PreviewComponent->PreviewInstance->SetPlaying( false );
LevelViewportClient->Invalidate();
}
}
Description->SetText( FText::Format( LOCTEXT( "Previewing", "Previewing {0}" ), FText::FromString( Component->GetPreviewText() ) ) );
}
else if ( Component->AnimClass )
{
Description->SetText( FText::Format( LOCTEXT( "Previewing", "Previewing {0}" ), FText::FromString( Component->AnimClass->GetName() ) ) );
}
else if ( PreviewAnimationSequence && !PreviewAnimationSequence->GetSkeleton()->IsCompatible(TargetSkeleton) )
{
Description->SetText( FText::Format( LOCTEXT( "IncorrectSkeleton", "The preview asset doesn't work for the skeleton '{0}'" ), FText::FromString( TargetSkeletonName ) ) );
}
else if ( Component->SkeletalMesh == NULL )
{
Description->SetText( FText::Format( LOCTEXT( "NoMeshFound", "No skeletal mesh found for skeleton '{0}'" ), FText::FromString( TargetSkeletonName ) ) );
}
else
{
Description->SetText( FText::Format( LOCTEXT( "SelectAnimation", "Select animation that works for skeleton '{0}'" ), FText::FromString( TargetSkeletonName ) ) );
}
Component->GetScene()->GetWorld()->Tick( LEVELTICK_All, InDeltaTime );
}
else
{
Description->SetText( FText::Format( LOCTEXT( "NoMeshFound", "No skeletal mesh found for skeleton '{0}'" ), FText::FromString( TargetSkeletonName ) ) );
}
}
void SAnimationRefPoseViewport::RefreshViewport()
{
}
bool SAnimationRefPoseViewport::IsVisible() const
{
return ViewportWidget.IsValid();
}
float SAnimationRefPoseViewport::GetViewMinInput() const
{
if (PreviewComponent != NULL)
{
if (PreviewComponent->PreviewInstance != NULL)
{
return 0.0f;
}
else if (PreviewComponent->GetAnimInstance() != NULL)
{
return FMath::Max<float>((float)(PreviewComponent->GetAnimInstance()->LifeTimer - 30.0), 0.0f);
}
}
return 0.f;
}
float SAnimationRefPoseViewport::GetViewMaxInput() const
{
if (PreviewComponent != NULL)
{
if (PreviewComponent->PreviewInstance != NULL)
{
return PreviewComponent->PreviewInstance->GetLength();
}
else if (PreviewComponent->GetAnimInstance() != NULL)
{
return PreviewComponent->GetAnimInstance()->LifeTimer;
}
}
return 0.f;
}
TArray<float> SAnimationRefPoseViewport::GetBars() const
{
TArray<float> Bars;
if (PreviewAnimationSequence)
{
int32 RefFrameIndex;
RefFrameIndexPropertyHandle->GetValue(RefFrameIndex);
float Fraction = (PreviewAnimationSequence->GetNumberOfSampledKeys() > 0)? FMath::Clamp<float>((float)RefFrameIndex/(float)PreviewAnimationSequence->GetNumberOfSampledKeys(), 0.f, 1.f) : 0.f;
Bars.Add(PreviewAnimationSequence->GetPlayLength() * Fraction);
}
else
{
Bars.Add(0.0f);
}
return Bars;
}
void SAnimationRefPoseViewport::OnBarDrag(int32 Index, float Position)
{
if (PreviewAnimationSequence)
{
int RefFrameIndex = FMath::Clamp<int>(PreviewAnimationSequence->GetPlayLength() > 0.0f? (int)(Position * (float)PreviewAnimationSequence->GetNumberOfSampledKeys() / PreviewAnimationSequence->GetPlayLength() + 0.5f) : 0.0f, 0, PreviewAnimationSequence->GetNumberOfSampledKeys() - 1);
RefFrameIndexPropertyHandle->SetValue(RefFrameIndex);
}
}
UAnimSingleNodeInstance* SAnimationRefPoseViewport::GetPreviewInstance() const
{
return PreviewComponent ? PreviewComponent->PreviewInstance : NULL;
}
#undef LOCTEXT_NAMESPACE