// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimTimeline/AnimModel_AnimSequenceBase.h" #include "Animation/AnimSequence.h" #include "AnimTimeline/AnimTimelineTrack.h" #include "AnimTimeline/AnimTimelineTrack_Notifies.h" #include "AnimTimeline/AnimTimelineTrack_NotifiesPanel.h" #include "AnimTimeline/AnimTimelineTrack_Curves.h" #include "AnimTimeline/AnimTimelineTrack_Curve.h" #include "AnimTimeline/AnimTimelineTrack_CompoundCurve.h" #include "AnimTimeline/AnimTimelineTrack_FloatCurve.h" #include "AnimTimeline/AnimTimelineTrack_VectorCurve.h" #include "AnimTimeline/AnimTimelineTrack_TransformCurve.h" #include "AnimTimeline/AnimTimelineTrack_Attributes.h" #include "AnimTimeline/AnimTimelineTrack_PerBoneAttributes.h" #include "AnimTimeline/AnimTimelineTrack_Attribute.h" #include "AnimSequenceTimelineCommands.h" #include "Framework/Commands/UICommandList.h" #include "IAnimationEditor.h" #include "Preferences/PersonaOptions.h" #include "FrameNumberDisplayFormat.h" #include "Framework/Commands/GenericCommands.h" #include "IPersonaPreviewScene.h" #include "Animation/DebugSkelMeshComponent.h" #include "AnimPreviewInstance.h" #include "AnimTimelineClipboard.h" #include "HAL/PlatformApplicationMisc.h" #include "ScopedTransaction.h" #include "Animation/AnimCurveTypes.h" #define LOCTEXT_NAMESPACE "FAnimModel_AnimSequence" FAnimModel_AnimSequenceBase::FAnimModel_AnimSequenceBase(const TSharedRef& InPreviewScene, const TSharedRef& InEditableSkeleton, const TSharedRef& InCommandList, UAnimSequenceBase* InAnimSequenceBase) : FAnimModel(InPreviewScene, InEditableSkeleton, InCommandList) , AnimSequenceBase(InAnimSequenceBase) { SnapTypes.Add(FAnimModel::FSnapType::Frames.Type, FAnimModel::FSnapType::Frames); SnapTypes.Add(FAnimModel::FSnapType::Notifies.Type, FAnimModel::FSnapType::Notifies); UpdateRange(); // Clear display flags for(bool& bElementNodeDisplayFlag : NotifiesTimingElementNodeDisplayFlags) { bElementNodeDisplayFlag = false; } AnimSequenceBase->RegisterOnNotifyChanged(UAnimSequenceBase::FOnNotifyChanged::CreateRaw(this, &FAnimModel_AnimSequenceBase::RefreshSnapTimes)); AnimSequenceBase->GetDataModel()->GetModifiedEvent().AddRaw(this, &FAnimModel_AnimSequenceBase::OnDataModelChanged); } FAnimModel_AnimSequenceBase::~FAnimModel_AnimSequenceBase() { AnimSequenceBase->UnregisterOnNotifyChanged(this); AnimSequenceBase->GetDataModel()->GetModifiedEvent().RemoveAll(this); } void FAnimModel_AnimSequenceBase::Initialize() { TSharedRef CommandList = WeakCommandList.Pin().ToSharedRef(); const FAnimSequenceTimelineCommands& Commands = FAnimSequenceTimelineCommands::Get(); CommandList->MapAction( FGenericCommands::Get().Rename, FExecuteAction::CreateLambda([this] { SelectedTracks.Array()[0]->RequestRename(); }), FCanExecuteAction::CreateLambda([this] { return (SelectedTracks.Num() > 0) && (SelectedTracks.Array()[0]->CanRename()); }) ); CommandList->MapAction( Commands.EditSelectedCurves, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::EditSelectedCurves), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CanEditSelectedCurves)); CommandList->MapAction( Commands.CopySelectedCurveNames, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CopySelectedCurveNamesToClipboard)); CommandList->MapAction( Commands.DisplayFrames, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::SetDisplayFormat, EFrameNumberDisplayFormats::Frames), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsDisplayFormatChecked, EFrameNumberDisplayFormats::Frames)); CommandList->MapAction( Commands.DisplaySeconds, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::SetDisplayFormat, EFrameNumberDisplayFormats::Seconds), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsDisplayFormatChecked, EFrameNumberDisplayFormats::Seconds)); CommandList->MapAction( Commands.DisplayPercentage, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleDisplayPercentage), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsDisplayPercentageChecked)); CommandList->MapAction( Commands.DisplaySecondaryFormat, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleDisplaySecondary), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsDisplaySecondaryChecked)); CommandList->MapAction( Commands.SnapToFrames, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleSnap, FAnimModel::FSnapType::Frames.Type), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapChecked, FAnimModel::FSnapType::Frames.Type), FIsActionButtonVisible::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapAvailable, FAnimModel::FSnapType::Frames.Type)); CommandList->MapAction( Commands.SnapToNotifies, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleSnap, FAnimModel::FSnapType::Notifies.Type), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapChecked, FAnimModel::FSnapType::Notifies.Type), FIsActionButtonVisible::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapAvailable, FAnimModel::FSnapType::Notifies.Type)); CommandList->MapAction( Commands.SnapToCompositeSegments, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleSnap, FAnimModel::FSnapType::CompositeSegment.Type), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapChecked, FAnimModel::FSnapType::CompositeSegment.Type), FIsActionButtonVisible::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapAvailable, FAnimModel::FSnapType::CompositeSegment.Type)); CommandList->MapAction( Commands.SnapToMontageSections, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::ToggleSnap, FAnimModel::FSnapType::MontageSection.Type), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapChecked, FAnimModel::FSnapType::MontageSection.Type), FIsActionButtonVisible::CreateSP(this, &FAnimModel_AnimSequenceBase::IsSnapAvailable, FAnimModel::FSnapType::MontageSection.Type)); CommandList->MapAction( FGenericCommands::Get().Copy, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CopyToClipboard), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CanCopyToClipboard)); CommandList->MapAction( Commands.PasteDataIntoCurve, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::PasteDataFromClipboardToSelectedCurve), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CanPasteDataFromClipboardToSelectedCurve)); CommandList->MapAction( FGenericCommands::Get().Paste, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::PasteFromClipboard), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CanPasteFromClipboard)); CommandList->MapAction( FGenericCommands::Get().Cut, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CutToClipboard), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::CanCutToClipboard)); CommandList->MapAction( FGenericCommands::Get().Delete, FExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::RemoveSelectedCurves), FCanExecuteAction::CreateSP(this, &FAnimModel_AnimSequenceBase::AreAnyCurvesSelected)); } void FAnimModel_AnimSequenceBase::RefreshTracks() { ClearTrackSelection(); // Clear all tracks ClearRootTracks(); // Add notifies RefreshNotifyTracks(); // Add curves RefreshCurveTracks(); // Add attributes RefreshAttributeTracks(); // Snaps RefreshSnapTimes(); // Tell the UI to refresh OnTracksChangedDelegate.Broadcast(); UpdateRange(); } UAnimSequenceBase* FAnimModel_AnimSequenceBase::GetAnimSequenceBase() const { return AnimSequenceBase; } void FAnimModel_AnimSequenceBase::RefreshNotifyTracks() { if(!NotifyRoot.IsValid()) { // Add a root track for notifies & then the main 'panel' legacy track NotifyRoot = MakeShared(SharedThis(this)); } NotifyRoot->ClearChildren(); AddRootTrack(NotifyRoot.ToSharedRef()); if(!NotifyPanel.IsValid()) { NotifyPanel = MakeShared(SharedThis(this)); NotifyRoot->SetAnimNotifyPanel(NotifyPanel.ToSharedRef()); } NotifyRoot->AddChild(NotifyPanel.ToSharedRef()); } void FAnimModel_AnimSequenceBase::RefreshCurveTracks() { if(!CurveRoot.IsValid()) { // Add a root track for curves CurveRoot = MakeShared(SharedThis(this)); } CurveRoot->ClearChildren(); AddRootTrack(CurveRoot.ToSharedRef()); // Next add a track for each float curve const FAnimationCurveData& AnimationModelCurveData = AnimSequenceBase->GetDataModel()->GetCurveData(); const UPersonaOptions* PersonaOptions = GetDefault(); if (!PersonaOptions || !PersonaOptions->bUseTreeViewForAnimationCurves) { for (const FFloatCurve& FloatCurve : AnimationModelCurveData.FloatCurves) { CurveRoot->AddChild(MakeShared(&FloatCurve, SharedThis(this))); } } else { FAnimTimelineTrack_CompoundCurve::AddGroupedCurveTracks(AnimationModelCurveData.FloatCurves, *CurveRoot, SharedThis(this), PersonaOptions->AnimationCurveGroupingDelimiters); } UAnimSequence* AnimSequence = Cast(AnimSequenceBase); if (AnimSequence) { if(!AdditiveRoot.IsValid()) { // Add a root track for additive layers AdditiveRoot = MakeShared(LOCTEXT("AdditiveLayerTrackList_Title", "Additive Layer Tracks"), LOCTEXT("AdditiveLayerTrackList_Tooltip", "Additive modifications to bone transforms"), SharedThis(this), true); } AdditiveRoot->ClearChildren(); AddRootTrack(AdditiveRoot.ToSharedRef()); // Next add a track for each transform curve for(const FTransformCurve& TransformCurve : AnimationModelCurveData.TransformCurves) { TSharedRef TransformCurveTrack = MakeShared(&TransformCurve, SharedThis(this)); TransformCurveTrack->SetExpanded(false); AdditiveRoot->AddChild(TransformCurveTrack); FText TransformName = FText::FromName(TransformCurve.GetName()); FLinearColor TransformColor = TransformCurve.GetColor(); FLinearColor XColor = FLinearColor::Red; FLinearColor YColor = FLinearColor::Green; FLinearColor ZColor = FLinearColor::Blue; FText XName = LOCTEXT("VectorXTrackName", "X"); FText YName = LOCTEXT("VectorYTrackName", "Y"); FText ZName = LOCTEXT("VectorZTrackName", "Z"); FText VectorFormat = LOCTEXT("TransformVectorFormat", "{0}.{1}"); FText TranslationName = LOCTEXT("TransformTranslationTrackName", "Translation"); TSharedRef TranslationCurveTrack = MakeShared(&TransformCurve.TranslationCurve, TransformCurve.GetName(), 0, ERawCurveTrackTypes::RCT_Transform, TranslationName, FText::Format(VectorFormat, TransformName, TranslationName), TransformColor, SharedThis(this)); TranslationCurveTrack->SetExpanded(false); TransformCurveTrack->AddChild(TranslationCurveTrack); FText ComponentFormat = LOCTEXT("TransformComponentFormat", "{0}.{1}.{2}"); TranslationCurveTrack->AddChild(MakeShared(&TransformCurve.TranslationCurve.FloatCurves[0], TransformCurve.GetName(), 0, ERawCurveTrackTypes::RCT_Transform, XName, FText::Format(ComponentFormat, TransformName, TranslationName, XName), XColor, XColor, SharedThis(this))); TranslationCurveTrack->AddChild(MakeShared(&TransformCurve.TranslationCurve.FloatCurves[1], TransformCurve.GetName(), 1, ERawCurveTrackTypes::RCT_Transform, YName, FText::Format(ComponentFormat, TransformName, TranslationName, YName), YColor, YColor, SharedThis(this))); TranslationCurveTrack->AddChild(MakeShared(&TransformCurve.TranslationCurve.FloatCurves[2], TransformCurve.GetName(), 2, ERawCurveTrackTypes::RCT_Transform, ZName, FText::Format(ComponentFormat, TransformName, TranslationName, ZName), ZColor, ZColor, SharedThis(this))); FText RollName = LOCTEXT("RotationRollTrackName", "Roll"); FText PitchName = LOCTEXT("RotationPitchTrackName", "Pitch"); FText YawName = LOCTEXT("RotationYawTrackName", "Yaw"); FText RotationName = LOCTEXT("TransformRotationTrackName", "Rotation"); TSharedRef RotationCurveTrack = MakeShared(&TransformCurve.RotationCurve, TransformCurve.GetName(), 3, ERawCurveTrackTypes::RCT_Transform, RotationName, FText::Format(VectorFormat, TransformName, RotationName), TransformColor, SharedThis(this)); RotationCurveTrack->SetExpanded(false); TransformCurveTrack->AddChild(RotationCurveTrack); RotationCurveTrack->AddChild(MakeShared(&TransformCurve.RotationCurve.FloatCurves[0], TransformCurve.GetName(), 3, ERawCurveTrackTypes::RCT_Transform, RollName, FText::Format(ComponentFormat, TransformName, RotationName, RollName), XColor, XColor, SharedThis(this))); RotationCurveTrack->AddChild(MakeShared(&TransformCurve.RotationCurve.FloatCurves[1], TransformCurve.GetName(), 4, ERawCurveTrackTypes::RCT_Transform, PitchName, FText::Format(ComponentFormat, TransformName, RotationName, PitchName), YColor, YColor, SharedThis(this))); RotationCurveTrack->AddChild(MakeShared(&TransformCurve.RotationCurve.FloatCurves[2], TransformCurve.GetName(), 5, ERawCurveTrackTypes::RCT_Transform, YawName, FText::Format(ComponentFormat, TransformName, RotationName, YawName), ZColor, ZColor, SharedThis(this))); FText ScaleName = LOCTEXT("TransformScaleTrackName", "Scale"); TSharedRef ScaleCurveTrack = MakeShared(&TransformCurve.ScaleCurve, TransformCurve.GetName(), 6, ERawCurveTrackTypes::RCT_Transform, ScaleName, FText::Format(VectorFormat, TransformName, ScaleName), TransformColor, SharedThis(this)); ScaleCurveTrack->SetExpanded(false); TransformCurveTrack->AddChild(ScaleCurveTrack); ScaleCurveTrack->AddChild(MakeShared(&TransformCurve.ScaleCurve.FloatCurves[0], TransformCurve.GetName(), 6, ERawCurveTrackTypes::RCT_Transform, XName, FText::Format(ComponentFormat, TransformName, ScaleName, XName), XColor, XColor, SharedThis(this))); ScaleCurveTrack->AddChild(MakeShared(&TransformCurve.ScaleCurve.FloatCurves[1], TransformCurve.GetName(), 7, ERawCurveTrackTypes::RCT_Transform, YName, FText::Format(ComponentFormat, TransformName, ScaleName, YName), YColor, YColor, SharedThis(this))); ScaleCurveTrack->AddChild(MakeShared(&TransformCurve.ScaleCurve.FloatCurves[2], TransformCurve.GetName(), 8, ERawCurveTrackTypes::RCT_Transform, ZName, FText::Format(ComponentFormat, TransformName, ScaleName, ZName), ZColor, ZColor, SharedThis(this))); } } } void FAnimModel_AnimSequenceBase::RefreshAttributeTracks() { if (UAnimSequence* AnimSequence = Cast(AnimSequenceBase)) { if (!AttributesRoot.IsValid()) { // Add a root track for attributes AttributesRoot = MakeShared(SharedThis(this)); } AttributesRoot->ClearChildren(); AddRootTrack(AttributesRoot.ToSharedRef()); TMap> BoneTracks; // Next add a track for each attribute curve for (const FAnimatedBoneAttribute& BoneAttribute : AnimSequence->GetDataModel()->GetAttributes()) { TSharedPtr BoneTrack = nullptr; if (BoneTracks.Contains(BoneAttribute.Identifier.GetBoneName())) { BoneTrack = BoneTracks.FindChecked(BoneAttribute.Identifier.GetBoneName()); } else { TSharedRef BoneTrackRef = MakeShared(BoneAttribute.Identifier.GetBoneName(), SharedThis(this)); AttributesRoot->AddChild(BoneTrackRef); BoneTracks.Add(BoneAttribute.Identifier.GetBoneName(), BoneTrackRef); BoneTrack = BoneTrackRef; BoneTrackRef->SetExpanded(false); } TSharedRef AttributeTrack = MakeShared(BoneAttribute, SharedThis(this)); BoneTrack->AddChild(AttributeTrack); } } } void FAnimModel_AnimSequenceBase::OnDataModelChanged(const EAnimDataModelNotifyType& NotifyType, IAnimationDataModel* Model, const FAnimDataModelNotifPayload& PayLoad) { NotifyCollector.Handle(NotifyType); switch(NotifyType) { case EAnimDataModelNotifyType::CurveAdded: case EAnimDataModelNotifyType::CurveChanged: case EAnimDataModelNotifyType::CurveRemoved: case EAnimDataModelNotifyType::TrackAdded: case EAnimDataModelNotifyType::TrackChanged: case EAnimDataModelNotifyType::TrackRemoved: case EAnimDataModelNotifyType::AttributeAdded: case EAnimDataModelNotifyType::AttributeChanged: case EAnimDataModelNotifyType::AttributeRemoved: case EAnimDataModelNotifyType::SequenceLengthChanged: case EAnimDataModelNotifyType::FrameRateChanged: { if (NotifyCollector.IsNotWithinBracket()) { RefreshTracks(); } break; } case EAnimDataModelNotifyType::BracketClosed: { if (NotifyCollector.IsNotWithinBracket()) { RefreshTracks(); } break; } } } void FAnimModel_AnimSequenceBase::EditSelectedCurves() { TArray EditCurveInfo; for(TSharedRef& SelectedTrack : SelectedTracks) { if(SelectedTrack->IsA()) { TSharedRef CurveTrack = StaticCastSharedRef(SelectedTrack); const TArray& Curves = CurveTrack->GetCurves(); int32 NumCurves = Curves.Num(); for(int32 CurveIndex = 0; CurveIndex < NumCurves; ++CurveIndex) { if(CurveTrack->CanEditCurve(CurveIndex)) { FText FullName = CurveTrack->GetFullCurveName(CurveIndex); FLinearColor Color = CurveTrack->GetCurveColor(CurveIndex); FName Name; ERawCurveTrackTypes Type; int32 EditCurveIndex; CurveTrack->GetCurveEditInfo(CurveIndex, Name, Type, EditCurveIndex); FSimpleDelegate OnCurveChanged = FSimpleDelegate::CreateSP(&CurveTrack.Get(), &FAnimTimelineTrack_Curve::HandleCurveChanged); EditCurveInfo.AddUnique(IAnimationEditor::FCurveEditInfo(FullName, Color, Name, Type, EditCurveIndex, OnCurveChanged)); } } } } if(EditCurveInfo.Num() > 0) { OnEditCurves.ExecuteIfBound(AnimSequenceBase, EditCurveInfo, nullptr); } } bool FAnimModel_AnimSequenceBase::CanEditSelectedCurves() const { for(const TSharedRef& SelectedTrack : SelectedTracks) { if(SelectedTrack->IsA()) { TSharedRef CurveTrack = StaticCastSharedRef(SelectedTrack); const TArray& Curves = CurveTrack->GetCurves(); for(int32 CurveIndex = 0; CurveIndex < Curves.Num(); ++CurveIndex) { if(CurveTrack->CanEditCurve(CurveIndex)) { return true; } } } } return false; } void FAnimModel_AnimSequenceBase::RemoveSelectedCurves() { IAnimationDataController& Controller = AnimSequenceBase->GetController(); Controller.OpenBracket(LOCTEXT("CurvePanel_RemoveCurves", "Remove Curves")); bool bDeletedCurve = false; for(TSharedRef& SelectedTrack : SelectedTracks) { if(SelectedTrack->IsA()) { TSharedRef FloatCurveTrack = StaticCastSharedRef(SelectedTrack); FName CurveName = FloatCurveTrack->GetFName(); const FAnimationCurveIdentifier FloatCurveId(CurveName, ERawCurveTrackTypes::RCT_Float); if (AnimSequenceBase->GetDataModel()->FindCurve(FloatCurveId)) { Controller.RemoveCurve(FloatCurveId); bDeletedCurve = true; } } else if(SelectedTrack->IsA()) { TSharedRef TransformCurveTrack = StaticCastSharedRef(SelectedTrack); FName CurveName = TransformCurveTrack->GetFName(); const FAnimationCurveIdentifier TransformCurveId(CurveName, ERawCurveTrackTypes::RCT_Transform); if (AnimSequenceBase->GetDataModel()->FindTransformCurve(TransformCurveId)) { Controller.RemoveCurve(TransformCurveId); bDeletedCurve = true; } } else if (SelectedTrack->IsA()) { // Lowest priority for base class delete, ERawCurveTrackTypes::RCT_Transform must take priority TSharedRef CurveTrack = StaticCastSharedRef(SelectedTrack); // Find all editable curves in this track for (const FName TrackCurveName : CurveTrack->GetCurveNames()) { const FAnimationCurveIdentifier CurveId(TrackCurveName, ERawCurveTrackTypes::RCT_Float); if (AnimSequenceBase->GetDataModel()->FindCurve(CurveId)) { Controller.RemoveCurve(CurveId); bDeletedCurve = true; } } } } Controller.CloseBracket(); if(bDeletedCurve) { AnimSequenceBase->PostEditChange(); if (GetPreviewScene()->GetPreviewMeshComponent()->PreviewInstance != nullptr) { GetPreviewScene()->GetPreviewMeshComponent()->PreviewInstance->RefreshCurveBoneControllers(); } } } void FAnimModel_AnimSequenceBase::CopySelectedCurveNamesToClipboard() { TArray TrackNames; for(TSharedRef& SelectedTrack : SelectedTracks) { if(SelectedTrack->IsA()) { TSharedRef FloatCurveTrack = StaticCastSharedRef(SelectedTrack); TrackNames.Add(FloatCurveTrack->GetFName().ToString()); } else if(SelectedTrack->IsA()) { TSharedRef TransformCurveTrack = StaticCastSharedRef(SelectedTrack); TrackNames.Add(TransformCurveTrack->GetFName().ToString()); } } if (!TrackNames.IsEmpty()) { FPlatformApplicationMisc::ClipboardCopy(*FString::Join(TrackNames, TEXT("\n"))); } } void FAnimModel_AnimSequenceBase::SetDisplayFormat(EFrameNumberDisplayFormats InFormat) { GetMutableDefault()->TimelineDisplayFormat = InFormat; } bool FAnimModel_AnimSequenceBase::IsDisplayFormatChecked(EFrameNumberDisplayFormats InFormat) const { return GetDefault()->TimelineDisplayFormat == InFormat; } void FAnimModel_AnimSequenceBase::ToggleDisplayPercentage() { GetMutableDefault()->bTimelineDisplayPercentage = !GetDefault()->bTimelineDisplayPercentage; } bool FAnimModel_AnimSequenceBase::IsDisplayPercentageChecked() const { return GetDefault()->bTimelineDisplayPercentage; } void FAnimModel_AnimSequenceBase::ToggleDisplaySecondary() { GetMutableDefault()->bTimelineDisplayFormatSecondary = !GetDefault()->bTimelineDisplayFormatSecondary; } bool FAnimModel_AnimSequenceBase::IsDisplaySecondaryChecked() const { return GetDefault()->bTimelineDisplayFormatSecondary; } bool FAnimModel_AnimSequenceBase::AreAnyCurvesSelected() const { if (!SelectedTracks.IsEmpty()) { for (const TSharedRef& SelectedTrack : SelectedTracks) { if (SelectedTrack->IsA()) { return true; } } } return false; } void FAnimModel_AnimSequenceBase::CopyToClipboard() const { if (!SelectedTracks.IsEmpty()) { if (UAnimTimelineClipboardContent * ClipboardContent = UAnimTimelineClipboardContent::Create()) { FAnimTimelineClipboardUtilities::CopySelectedTracksToClipboard(SelectedTracks, ClipboardContent); FAnimTimelineClipboardUtilities::CopyContentToClipboard(ClipboardContent); } else { UE_LOG(LogAnimation, Warning, TEXT("Failed to get create valid clipboard object for Animation Timeline while attempting to copy data")); } } } bool FAnimModel_AnimSequenceBase::CanCopyToClipboard() { if (!SelectedTracks.IsEmpty()) { for (const TSharedRef & Track : SelectedTracks) { if (!Track->SupportsCopy()) { return false; } } } else { return false; } return true; } void FAnimModel_AnimSequenceBase::PasteDataFromClipboardToSelectedCurve() { if (const UAnimTimelineClipboardContent* ClipboardContent = FAnimTimelineClipboardUtilities::GetContentFromClipboard()) { const FScopedTransaction Transaction(LOCTEXT("AnimSequenceBase_PasteCurveData", "Paste Data To Selected Curve")); // Paste data FAnimTimelineClipboardUtilities::OverwriteSelectedCurveDataFromClipboard(ClipboardContent, SelectedTracks, AnimSequenceBase); } else { UE_LOG(LogAnimation, Warning, TEXT("Failed to get valid clipboard for Animation Timeline while attempting to paste data")); } } bool FAnimModel_AnimSequenceBase::CanPasteDataFromClipboardToSelectedCurve() { return FAnimTimelineClipboardUtilities::CanOverwriteSelectedCurveDataFromClipboard(SelectedTracks); } void FAnimModel_AnimSequenceBase::PasteFromClipboard() { if (const UAnimTimelineClipboardContent* ClipboardContent = FAnimTimelineClipboardUtilities::GetContentFromClipboard()) { const FScopedTransaction Transaction(LOCTEXT("AnimSequenceBase_PasteCurves", "Paste")); // Paste curves from clipboard FAnimTimelineClipboardUtilities::OverwriteOrAddCurvesFromClipboardContent(ClipboardContent, AnimSequenceBase); } else { UE_LOG(LogAnimation, Warning, TEXT("Failed to get valid clipboard for Animation Timeline while attempting to paste data")); } } bool FAnimModel_AnimSequenceBase::CanPasteFromClipboard() { const UAnimTimelineClipboardContent* ClipboardContent = FAnimTimelineClipboardUtilities::GetContentFromClipboard(); return ClipboardContent && !ClipboardContent->IsEmpty(); } void FAnimModel_AnimSequenceBase::CutToClipboard() { const FScopedTransaction Transaction(LOCTEXT("AnimSequenceBase_CutCurveSelection", "Cut Selection")); CopyToClipboard(); RemoveSelectedCurves(); } bool FAnimModel_AnimSequenceBase::CanCutToClipboard() { return CanCopyToClipboard(); } void FAnimModel_AnimSequenceBase::UpdateRange() { FAnimatedRange OldPlaybackRange = PlaybackRange; // update playback range PlaybackRange = FAnimatedRange(0.0, (double)AnimSequenceBase->GetPlayLength()); if (OldPlaybackRange != PlaybackRange) { // Update view/range if playback range changed SetViewRange(PlaybackRange); } } bool FAnimModel_AnimSequenceBase::IsNotifiesTimingElementDisplayEnabled(ETimingElementType::Type ElementType) const { return NotifiesTimingElementNodeDisplayFlags[ElementType]; } void FAnimModel_AnimSequenceBase::ToggleNotifiesTimingElementDisplayEnabled(ETimingElementType::Type ElementType) { NotifiesTimingElementNodeDisplayFlags[ElementType] = !NotifiesTimingElementNodeDisplayFlags[ElementType]; } bool FAnimModel_AnimSequenceBase::ClampToEndTime(float NewEndTime) { float SequenceLength = AnimSequenceBase->GetPlayLength(); //if we had a valid sequence length before and our new end time is shorter //then we need to clamp. return (SequenceLength > 0.f && NewEndTime < SequenceLength); } void FAnimModel_AnimSequenceBase::RefreshSnapTimes() { SnapTimes.Reset(); for(const FAnimNotifyEvent& Notify : AnimSequenceBase->Notifies) { SnapTimes.Emplace(FSnapType::Notifies.Type, (double)Notify.GetTime()); if(Notify.NotifyStateClass != nullptr) { SnapTimes.Emplace(FSnapType::Notifies.Type, (double)(Notify.GetTime() + Notify.GetDuration())); } } } #undef LOCTEXT_NAMESPACE