Files
UnrealEngineUWP/Engine/Source/Editor/Persona/Private/SAnimationScrubPanel.cpp
fred kimberley 7fbfaf57c8 Require explicit constructors/casts when converting between FVector, FVector3d, and FVector3f.
#jira UE-122078
#rb Andrew.Davidson, Colin.McGinley
#preflight standard build

#ROBOMERGE-AUTHOR: fred.kimberley
#ROBOMERGE-SOURCE: CL 18817999 in //UE5/Release-5.0/... via CL 18818012 via CL 18822871
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v910-18824042)

[CL 18824721 by fred kimberley in ue5-main branch]
2022-02-02 07:59:31 -05:00

612 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SAnimationScrubPanel.h"
#include "Widgets/SBoxPanel.h"
#include "Animation/AnimSequenceBase.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimBlueprintGeneratedClass.h"
#include "Animation/AnimBlueprint.h"
#include "AnimPreviewInstance.h"
#include "SScrubControlPanel.h"
#include "ScopedTransaction.h"
#include "Animation/BlendSpace.h"
#include "AnimationEditorPreviewScene.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "Animation/AnimSequenceHelpers.h"
#define LOCTEXT_NAMESPACE "AnimationScrubPanel"
void SAnimationScrubPanel::Construct( const SAnimationScrubPanel::FArguments& InArgs, const TSharedRef<IPersonaPreviewScene>& InPreviewScene)
{
bSliderBeingDragged = false;
LockedSequence = InArgs._LockedSequence;
OnSetInputViewRange = InArgs._OnSetInputViewRange;
PreviewScenePtr = InPreviewScene;
this->ChildSlot
[
SNew(SHorizontalBox)
.AddMetaData<FTagMetaData>(TEXT("AnimScrub.Scrub"))
+SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.FillWidth(1)
.Padding(0.0f)
[
SAssignNew(ScrubControlPanel, SScrubControlPanel)
.IsEnabled(true)//this, &SAnimationScrubPanel::DoesSyncViewport)
.Value(this, &SAnimationScrubPanel::GetScrubValue)
.NumOfKeys(this, &SAnimationScrubPanel::GetNumberOfKeys)
.SequenceLength(this, &SAnimationScrubPanel::GetSequenceLength)
.DisplayDrag(this, &SAnimationScrubPanel::GetDisplayDrag)
.OnValueChanged(this, &SAnimationScrubPanel::OnValueChanged)
.OnBeginSliderMovement(this, &SAnimationScrubPanel::OnBeginSliderMovement)
.OnEndSliderMovement(this, &SAnimationScrubPanel::OnEndSliderMovement)
.OnClickedForwardPlay(this, &SAnimationScrubPanel::OnClick_Forward)
.OnClickedForwardStep(this, &SAnimationScrubPanel::OnClick_Forward_Step)
.OnClickedForwardEnd(this, &SAnimationScrubPanel::OnClick_Forward_End)
.OnClickedBackwardPlay(this, &SAnimationScrubPanel::OnClick_Backward)
.OnClickedBackwardStep(this, &SAnimationScrubPanel::OnClick_Backward_Step)
.OnClickedBackwardEnd(this, &SAnimationScrubPanel::OnClick_Backward_End)
.OnClickedToggleLoop(this, &SAnimationScrubPanel::OnClick_ToggleLoop)
.OnClickedRecord(this, &SAnimationScrubPanel::OnClick_Record)
.OnGetLooping(this, &SAnimationScrubPanel::IsLoopStatusOn)
.OnGetPlaybackMode(this, &SAnimationScrubPanel::GetPlaybackMode)
.OnGetRecording(this, &SAnimationScrubPanel::IsRecording)
.ViewInputMin(InArgs._ViewInputMin)
.ViewInputMax(InArgs._ViewInputMax)
.bDisplayAnimScrubBarEditing(InArgs._bDisplayAnimScrubBarEditing)
.OnSetInputViewRange(InArgs._OnSetInputViewRange)
.OnCropAnimSequence( this, &SAnimationScrubPanel::OnCropAnimSequence )
.OnAddAnimSequence( this, &SAnimationScrubPanel::OnInsertAnimSequence )
.OnAppendAnimSequence(this, &SAnimationScrubPanel::OnAppendAnimSequence )
.OnReZeroAnimSequence( this, &SAnimationScrubPanel::OnReZeroAnimSequence )
.bAllowZoom(InArgs._bAllowZoom)
.IsRealtimeStreamingMode(this, &SAnimationScrubPanel::IsRealtimeStreamingMode)
]
];
}
FReply SAnimationScrubPanel::OnClick_Forward_Step()
{
UDebugSkelMeshComponent* SMC = GetPreviewScene()->GetPreviewMeshComponent();
if (UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance())
{
bool bShouldStepCloth = FMath::Abs(PreviewInstance->GetLength() - PreviewInstance->GetCurrentTime()) > SMALL_NUMBER;
PreviewInstance->SetPlaying(false);
PreviewInstance->StepForward();
if(SMC && bShouldStepCloth)
{
SMC->bPerformSingleClothingTick = true;
}
}
else if (SMC)
{
// BlendSpaces and Animation Blueprints combine animations so there's no such thing as a frame. However, 1/30 is a sensible/common rate.
const float FixedFrameRate = 30.0f;
// Advance a single frame, leaving it paused afterwards
SMC->GlobalAnimRateScale = 1.0f;
GetPreviewScene()->Tick(1.0f / FixedFrameRate);
SMC->GlobalAnimRateScale = 0.0f;
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Forward_End()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance)
{
PreviewInstance->SetPlaying(false);
PreviewInstance->SetPosition(PreviewInstance->GetLength(), false);
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Backward_Step()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
UDebugSkelMeshComponent* SMC = GetPreviewScene()->GetPreviewMeshComponent();
if (PreviewInstance)
{
bool bShouldStepCloth = PreviewInstance->GetCurrentTime() > SMALL_NUMBER;
PreviewInstance->SetPlaying(false);
PreviewInstance->StepBackward();
if(SMC && bShouldStepCloth)
{
SMC->bPerformSingleClothingTick = true;
}
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Backward_End()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance)
{
PreviewInstance->SetPlaying(false);
PreviewInstance->SetPosition(0.f, false);
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Forward()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
UDebugSkelMeshComponent* SMC = GetPreviewScene()->GetPreviewMeshComponent();
if (PreviewInstance)
{
bool bIsReverse = PreviewInstance->IsReverse();
bool bIsPlaying = PreviewInstance->IsPlaying();
// if current bIsReverse and bIsPlaying, we'd like to just turn off reverse
if (bIsReverse && bIsPlaying)
{
PreviewInstance->SetReverse(false);
}
// already playing, simply pause
else if (bIsPlaying)
{
PreviewInstance->SetPlaying(false);
if(SMC && SMC->bPauseClothingSimulationWithAnim)
{
SMC->SuspendClothingSimulation();
}
}
// if not playing, play forward
else
{
//if we're at the end of the animation, jump back to the beginning before playing
if ( GetScrubValue() >= GetSequenceLength() )
{
PreviewInstance->SetPosition(0.0f, false);
}
PreviewInstance->SetReverse(false);
PreviewInstance->SetPlaying(true);
if(SMC && SMC->bPauseClothingSimulationWithAnim)
{
SMC->ResumeClothingSimulation();
}
}
}
else if(SMC)
{
SMC->GlobalAnimRateScale = (SMC->GlobalAnimRateScale > 0.0f) ? 0.0f : 1.0f;
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Backward()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance)
{
bool bIsReverse = PreviewInstance->IsReverse();
bool bIsPlaying = PreviewInstance->IsPlaying();
// if currently playing forward, just simply turn on reverse
if (!bIsReverse && bIsPlaying)
{
PreviewInstance->SetReverse(true);
}
else if (bIsPlaying)
{
PreviewInstance->SetPlaying(false);
}
else
{
//if we're at the beginning of the animation, jump back to the end before playing
if ( GetScrubValue() <= 0.0f )
{
PreviewInstance->SetPosition(GetSequenceLength(), false);
}
PreviewInstance->SetPlaying(true);
PreviewInstance->SetReverse(true);
}
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_ToggleLoop()
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance)
{
bool bIsLooping = PreviewInstance->IsLooping();
PreviewInstance->SetLooping(!bIsLooping);
}
return FReply::Handled();
}
FReply SAnimationScrubPanel::OnClick_Record()
{
StaticCastSharedRef<FAnimationEditorPreviewScene>(GetPreviewScene())->RecordAnimation();
return FReply::Handled();
}
bool SAnimationScrubPanel::IsLoopStatusOn() const
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
return (PreviewInstance && PreviewInstance->IsLooping());
}
EPlaybackMode::Type SAnimationScrubPanel::GetPlaybackMode() const
{
if (UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance())
{
if (PreviewInstance->IsPlaying())
{
return PreviewInstance->IsReverse() ? EPlaybackMode::PlayingReverse : EPlaybackMode::PlayingForward;
}
return EPlaybackMode::Stopped;
}
else if (UDebugSkelMeshComponent* SMC = GetPreviewScene()->GetPreviewMeshComponent())
{
return (SMC->GlobalAnimRateScale > 0.0f) ? EPlaybackMode::PlayingForward : EPlaybackMode::Stopped;
}
return EPlaybackMode::Stopped;
}
bool SAnimationScrubPanel::IsRecording() const
{
return StaticCastSharedRef<FAnimationEditorPreviewScene>(GetPreviewScene())->IsRecording();
}
bool SAnimationScrubPanel::IsRealtimeStreamingMode() const
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
return ( ! (PreviewInstance && PreviewInstance->GetCurrentAsset()) );
}
void SAnimationScrubPanel::OnValueChanged(float NewValue)
{
if (UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance())
{
PreviewInstance->SetPosition(NewValue);
}
else
{
UAnimInstance* Instance;
FAnimBlueprintDebugData* DebugData;
if (GetAnimBlueprintDebugData(/*out*/ Instance, /*out*/ DebugData))
{
DebugData->SetSnapshotIndexByTime(Instance, NewValue);
}
}
}
// make sure viewport is freshes
void SAnimationScrubPanel::OnBeginSliderMovement()
{
bSliderBeingDragged = true;
if (UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance())
{
PreviewInstance->SetPlaying(false);
}
}
void SAnimationScrubPanel::OnEndSliderMovement(float NewValue)
{
bSliderBeingDragged = false;
}
uint32 SAnimationScrubPanel::GetNumberOfKeys() const
{
if (DoesSyncViewport())
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
float Length = PreviewInstance->GetLength();
// if anim sequence, use correct num frames
int32 NumKeys = (int32) (Length/0.0333f);
if (PreviewInstance->GetCurrentAsset())
{
if (PreviewInstance->GetCurrentAsset()->IsA(UAnimSequenceBase::StaticClass()))
{
NumKeys = CastChecked<UAnimSequenceBase>(PreviewInstance->GetCurrentAsset())->GetNumberOfSampledKeys();
}
else if(PreviewInstance->GetCurrentAsset()->IsA(UBlendSpace::StaticClass()))
{
// Blendspaces dont display frame notches, so just return 0 here
NumKeys = 0;
}
}
return NumKeys;
}
else if (LockedSequence)
{
return LockedSequence->GetNumberOfSampledKeys();
}
else
{
UAnimInstance* Instance;
FAnimBlueprintDebugData* DebugData;
if (GetAnimBlueprintDebugData(/*out*/ Instance, /*out*/ DebugData))
{
return DebugData->GetSnapshotLengthInFrames();
}
}
return 1;
}
float SAnimationScrubPanel::GetSequenceLength() const
{
if (DoesSyncViewport())
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
return PreviewInstance->GetLength();
}
else if (LockedSequence)
{
return LockedSequence->GetPlayLength();
}
else
{
UAnimInstance* Instance;
FAnimBlueprintDebugData* DebugData;
if (GetAnimBlueprintDebugData(/*out*/ Instance, /*out*/ DebugData))
{
return Instance->LifeTimer;
}
}
return 0.f;
}
bool SAnimationScrubPanel::DoesSyncViewport() const
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
return (( LockedSequence==NULL && PreviewInstance ) || ( LockedSequence && PreviewInstance && PreviewInstance->GetCurrentAsset() == LockedSequence ));
}
void SAnimationScrubPanel::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
if (bSliderBeingDragged)
{
GetPreviewScene()->InvalidateViews();
}
}
class UAnimSingleNodeInstance* SAnimationScrubPanel::GetPreviewInstance() const
{
UDebugSkelMeshComponent* PreviewMeshComponent = GetPreviewScene()->GetPreviewMeshComponent();
return PreviewMeshComponent && PreviewMeshComponent->IsPreviewOn()? PreviewMeshComponent->PreviewInstance : NULL;
}
float SAnimationScrubPanel::GetScrubValue() const
{
if (DoesSyncViewport())
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance)
{
return PreviewInstance->GetCurrentTime();
}
}
else
{
UAnimInstance* Instance;
FAnimBlueprintDebugData* DebugData;
if (GetAnimBlueprintDebugData(/*out*/ Instance, /*out*/ DebugData))
{
return Instance->CurrentLifeTimerScrubPosition;
}
}
return 0.f;
}
void SAnimationScrubPanel::ReplaceLockedSequence(class UAnimSequenceBase* NewLockedSequence)
{
LockedSequence = NewLockedSequence;
}
UAnimInstance* SAnimationScrubPanel::GetAnimInstanceWithBlueprint() const
{
if (UDebugSkelMeshComponent* DebugComponent = GetPreviewScene()->GetPreviewMeshComponent())
{
UAnimInstance* Instance = DebugComponent->GetAnimInstance();
if ((Instance != NULL) && (Instance->GetClass()->ClassGeneratedBy != NULL))
{
return Instance;
}
}
return NULL;
}
bool SAnimationScrubPanel::GetAnimBlueprintDebugData(UAnimInstance*& Instance, FAnimBlueprintDebugData*& DebugInfo) const
{
Instance = GetAnimInstanceWithBlueprint();
if (Instance != NULL)
{
// Avoid updating the instance if we're replaying the past
if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(Instance->GetClass()))
{
if (UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy))
{
if (Blueprint->GetObjectBeingDebugged() == Instance)
{
DebugInfo = &(AnimBlueprintClass->GetAnimBlueprintDebugData());
return true;
}
}
}
}
return false;
}
void SAnimationScrubPanel::OnCropAnimSequence( bool bFromStart, float CurrentTime )
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if(PreviewInstance)
{
float Length = PreviewInstance->GetLength();
if (PreviewInstance->GetCurrentAsset())
{
UAnimSequence* AnimSequence = Cast<UAnimSequence>( PreviewInstance->GetCurrentAsset() );
if( AnimSequence )
{
const FScopedTransaction Transaction( LOCTEXT("CropAnimSequence", "Crop Animation Sequence") );
//Call modify to restore slider position
PreviewInstance->Modify();
//Call modify to restore anim sequence current state
AnimSequence->Modify();
const float TrimStart = bFromStart ? 0.f : CurrentTime;
const float TrimEnd = bFromStart ? CurrentTime : AnimSequence->GetPlayLength();
// Trim off the user-selected part of the raw anim data.
UE::Anim::AnimationData::Trim(AnimSequence, TrimStart, TrimEnd);
//Resetting slider position to the first frame
PreviewInstance->SetPosition( 0.0f, false );
OnSetInputViewRange.ExecuteIfBound(0, AnimSequence->GetPlayLength());
}
}
}
}
void SAnimationScrubPanel::OnAppendAnimSequence( bool bFromStart, int32 NumOfFrames )
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if(PreviewInstance && PreviewInstance->GetCurrentAsset())
{
UAnimSequence* AnimSequence = Cast<UAnimSequence>(PreviewInstance->GetCurrentAsset());
if(AnimSequence)
{
const FScopedTransaction Transaction(LOCTEXT("InsertAnimSequence", "Insert Animation Sequence"));
//Call modify to restore slider position
PreviewInstance->Modify();
//Call modify to restore anim sequence current state
AnimSequence->Modify();
// Crop the raw anim data.
int32 StartFrame = (bFromStart)? 0 : AnimSequence->GetDataModel()->GetNumberOfFrames() - 1;
int32 EndFrame = StartFrame + NumOfFrames;
int32 CopyFrame = StartFrame;
UE::Anim::AnimationData::DuplicateKeys(AnimSequence, StartFrame, NumOfFrames, CopyFrame);
OnSetInputViewRange.ExecuteIfBound(0, AnimSequence->GetPlayLength());
}
}
}
void SAnimationScrubPanel::OnInsertAnimSequence( bool bBefore, int32 CurrentFrame )
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if(PreviewInstance && PreviewInstance->GetCurrentAsset())
{
UAnimSequence* AnimSequence = Cast<UAnimSequence>(PreviewInstance->GetCurrentAsset());
if(AnimSequence)
{
const FScopedTransaction Transaction(LOCTEXT("InsertAnimSequence", "Insert Animation Sequence"));
//Call modify to restore slider position
PreviewInstance->Modify();
//Call modify to restore anim sequence current state
AnimSequence->Modify();
// Duplicate specified key
const int32 StartFrame = (bBefore)? CurrentFrame : CurrentFrame + 1;
UE::Anim::AnimationData::DuplicateKeys(AnimSequence, StartFrame, 1, CurrentFrame);
OnSetInputViewRange.ExecuteIfBound(0, AnimSequence->GetPlayLength());
}
}
}
void SAnimationScrubPanel::OnReZeroAnimSequence(int32 FrameIndex)
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if(PreviewInstance)
{
UDebugSkelMeshComponent* PreviewSkelComp = GetPreviewScene()->GetPreviewMeshComponent();
if (PreviewInstance->GetCurrentAsset() && PreviewSkelComp )
{
UAnimSequence* AnimSequence = Cast<UAnimSequence>( PreviewInstance->GetCurrentAsset() );
if( AnimSequence )
{
const FScopedTransaction Transaction( LOCTEXT("ReZeroAnimation", "ReZero Animation Sequence") );
//Call modify to restore anim sequence current state
AnimSequence->Modify();
// As above, animations don't have any idea of hierarchy, so we don't know for sure if track 0 is the root bone's track.
const FBoneAnimationTrack& AnimationTrack = AnimSequence->GetDataModel()->GetBoneTrackByIndex(0);
FRawAnimSequenceTrack RawTrack = AnimationTrack.InternalTrackData;
// Find vector that would translate current root bone location onto origin.
FVector FrameTransform = FVector::ZeroVector;
if (FrameIndex == INDEX_NONE)
{
// Use current transform
FrameTransform = PreviewSkelComp->GetComponentSpaceTransforms()[0].GetLocation();
}
else if(RawTrack.PosKeys.IsValidIndex(FrameIndex))
{
// Use transform at frame
FrameTransform = (FVector)RawTrack.PosKeys[FrameIndex];
}
FVector ApplyTranslation = -1.f * FrameTransform;
// Convert into world space
FVector WorldApplyTranslation = PreviewSkelComp->GetComponentTransform().TransformVector(ApplyTranslation);
ApplyTranslation = PreviewSkelComp->GetComponentTransform().InverseTransformVector(WorldApplyTranslation);
for(int32 i=0; i<RawTrack.PosKeys.Num(); i++)
{
RawTrack.PosKeys[i] += (FVector3f)ApplyTranslation;
}
IAnimationDataController& Controller = AnimSequence->GetController();
Controller.SetBoneTrackKeys(AnimationTrack.Name, RawTrack.PosKeys, RawTrack.RotKeys, RawTrack.ScaleKeys);
AnimSequence->MarkPackageDirty();
}
}
}
}
bool SAnimationScrubPanel::GetDisplayDrag() const
{
UAnimSingleNodeInstance* PreviewInstance = GetPreviewInstance();
if (PreviewInstance && PreviewInstance->GetCurrentAsset())
{
return true;
}
return false;
}
#undef LOCTEXT_NAMESPACE