From 4f2e9924bd08551509585f1a18efd27e6f4b7ade Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Sat, 18 Dec 2021 18:47:30 +0100 Subject: [PATCH 1/8] Version Bump --- FicsItCam.uplugin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FicsItCam.uplugin b/FicsItCam.uplugin index 8644b09..d70827b 100644 --- a/FicsItCam.uplugin +++ b/FicsItCam.uplugin @@ -2,7 +2,7 @@ "FileVersion": 3, "Version": 0, "VersionName": "0.2", - "SemVersion": "0.2.0", + "SemVersion": "0.2.1", "FriendlyName": "FicsIt-Cam", "Description": "This mod allows you to create beautiful camera animations in-game.", "Category": "Modding", From d324ae873886a9864562a3b5a47c0c8f52e39f6d Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Sat, 29 Jan 2022 00:03:40 +0100 Subject: [PATCH 2/8] Fixes some issue with Multiplayer --- Source/FicsItCam/FICEditorCameraCharacter.cpp | 174 +++++++++--------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/Source/FicsItCam/FICEditorCameraCharacter.cpp b/Source/FicsItCam/FICEditorCameraCharacter.cpp index dc9e1cc..196e511 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.cpp +++ b/Source/FicsItCam/FICEditorCameraCharacter.cpp @@ -27,105 +27,107 @@ AFICEditorCameraCharacter::AFICEditorCameraCharacter() { void AFICEditorCameraCharacter::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); - bool bUseCinematic = EditorContext->GetAnimation()->bUseCinematic; - if (!IsValid(Camera) || Camera->IsA() != bUseCinematic) { - if (Camera) Camera->DestroyComponent(); - if (bUseCinematic) { - UCineCameraComponent* CineCamera = NewObject(this); - CineCamera->FocusSettings.FocusMethod = ECameraFocusMethod::Manual; - Camera = CineCamera; - } else { - Camera = NewObject(this); - } + if (GetController() == GetWorld()->GetFirstPlayerController()) { + bool bUseCinematic = EditorContext->GetAnimation()->bUseCinematic; + if (!IsValid(Camera) || Camera->IsA() != bUseCinematic) { + if (Camera) Camera->DestroyComponent(); + if (bUseCinematic) { + UCineCameraComponent* CineCamera = NewObject(this); + CineCamera->FocusSettings.FocusMethod = ECameraFocusMethod::Manual; + Camera = CineCamera; + } else { + Camera = NewObject(this); + } - Camera->AttachToComponent(GetCapsuleComponent(), FAttachmentTransformRules::KeepRelativeTransform); + Camera->AttachToComponent(GetCapsuleComponent(), FAttachmentTransformRules::KeepRelativeTransform); - AController* PController = Controller; - PController->UnPossess(); - PController->Possess(this); + AController* PController = Controller; + PController->UnPossess(); + PController->Possess(this); - Camera->SetActive(true); + Camera->SetActive(true); - UpdateValues(); - } - Cast(GetNetOwningPlayer())->Size = FVector2D(0.5,0.5); - - Camera->bConstrainAspectRatio = EditorContext->bForceResolution; - if (Camera->IsA()) { - UCineCameraComponent* CineCamera = Cast(Camera); - CineCamera->Filmback.SensorWidth = EditorContext->GetAnimation()->SensorWidth * EditorContext->SensorWidthAdjust; - CineCamera->Filmback.SensorHeight = EditorContext->GetAnimation()->SensorHeight * EditorContext->SensorWidthAdjust; - Camera->SetFieldOfView(EditorContext->FOV.GetValue()); - } else { - Camera->SetAspectRatio(EditorContext->GetAnimation()->ResolutionHeight / EditorContext->GetAnimation()->ResolutionWidth); - } - - GetCharacterMovement()->SetMovementMode(MOVE_Flying); - GetCharacterMovement()->MaxFlySpeed = bIsSprinting ? MaxFlySpeed * 10 : MaxFlySpeed; - GetCharacterMovement()->MaxAcceleration = 1000000; - - FRotator RotatorFix; - AController* MyController = GetController(); - if (MyController) { - RotatorFix = MyController->GetControlRotation(); - RotatorFix.Roll = RollRotationFixValue; - } + UpdateValues(); + } + Cast(GetNetOwningPlayer())->Size = FVector2D(0.5,0.5); + + Camera->bConstrainAspectRatio = EditorContext->bForceResolution; + if (Camera->IsA()) { + UCineCameraComponent* CineCamera = Cast(Camera); + CineCamera->Filmback.SensorWidth = EditorContext->GetAnimation()->SensorWidth * EditorContext->SensorWidthAdjust; + CineCamera->Filmback.SensorHeight = EditorContext->GetAnimation()->SensorHeight * EditorContext->SensorWidthAdjust; + Camera->SetFieldOfView(EditorContext->FOV.GetValue()); + } else { + Camera->SetAspectRatio(EditorContext->GetAnimation()->ResolutionHeight / EditorContext->GetAnimation()->ResolutionWidth); + } - if (EditorContext) { - if (EditorContext->bMoveCamera) { - CameraActor->SetActorTransform(GetActorTransform()); + GetCharacterMovement()->SetMovementMode(MOVE_Flying); + GetCharacterMovement()->MaxFlySpeed = bIsSprinting ? MaxFlySpeed * 10 : MaxFlySpeed; + GetCharacterMovement()->MaxAcceleration = 1000000; + + FRotator RotatorFix; + AController* MyController = GetController(); + if (MyController) { + RotatorFix = MyController->GetControlRotation(); + RotatorFix.Roll = RollRotationFixValue; } + + if (EditorContext) { + if (EditorContext->bMoveCamera) { + CameraActor->SetActorTransform(GetActorTransform()); + } - if (CameraActor) { - FVector Pos = CameraActor->GetActorLocation(); - FRotator RotNew = CameraActor->GetActorRotation(); - FRotator RotOld = FRotator(EditorContext->RotPitch.GetValue(), EditorContext->RotYaw.GetValue(), EditorContext->RotRoll.GetValue()); - FRotator RotOldN = RotOld; - while (RotOldN.Pitch < -180.0) RotOldN.Pitch += 360.0; - while (RotOldN.Pitch > 180.0) RotOldN.Pitch -= 360.0; - while (RotOldN.Yaw < -180.0) RotOldN.Yaw += 360.0; - while (RotOldN.Yaw > 180.0) RotOldN.Yaw -= 360.0; - while (RotOldN.Roll < -180.0) RotOldN.Roll += 360.0; - while (RotOldN.Roll > 180.0) RotOldN.Roll -= 360.0; - FRotator RotDiff = RotNew - RotOldN; - while (RotDiff.Pitch < -180.0) RotDiff.Pitch += 360.0; - while (RotDiff.Pitch > 180.0) RotDiff.Pitch -= 360.0; - while (RotDiff.Yaw < -180.0) RotDiff.Yaw += 360.0; - while (RotDiff.Yaw > 180.0) RotDiff.Yaw -= 360.0; - while (RotDiff.Roll < -180.0) RotDiff.Roll += 360.0; - while (RotDiff.Roll > 180.0) RotDiff.Roll -= 360.0; - RotNew = RotOld + RotDiff; + if (CameraActor) { + FVector Pos = CameraActor->GetActorLocation(); + FRotator RotNew = CameraActor->GetActorRotation(); + FRotator RotOld = FRotator(EditorContext->RotPitch.GetValue(), EditorContext->RotYaw.GetValue(), EditorContext->RotRoll.GetValue()); + FRotator RotOldN = RotOld; + while (RotOldN.Pitch < -180.0) RotOldN.Pitch += 360.0; + while (RotOldN.Pitch > 180.0) RotOldN.Pitch -= 360.0; + while (RotOldN.Yaw < -180.0) RotOldN.Yaw += 360.0; + while (RotOldN.Yaw > 180.0) RotOldN.Yaw -= 360.0; + while (RotOldN.Roll < -180.0) RotOldN.Roll += 360.0; + while (RotOldN.Roll > 180.0) RotOldN.Roll -= 360.0; + FRotator RotDiff = RotNew - RotOldN; + while (RotDiff.Pitch < -180.0) RotDiff.Pitch += 360.0; + while (RotDiff.Pitch > 180.0) RotDiff.Pitch -= 360.0; + while (RotDiff.Yaw < -180.0) RotDiff.Yaw += 360.0; + while (RotDiff.Yaw > 180.0) RotDiff.Yaw -= 360.0; + while (RotDiff.Roll < -180.0) RotDiff.Roll += 360.0; + while (RotDiff.Roll > 180.0) RotDiff.Roll -= 360.0; + RotNew = RotOld + RotDiff; - EditorContext->PosX.SetValue(Pos.X); - EditorContext->PosY.SetValue(Pos.Y); - EditorContext->PosZ.SetValue(Pos.Z); - EditorContext->RotPitch.SetValue(RotNew.Pitch); - EditorContext->RotYaw.SetValue(RotNew.Yaw); - EditorContext->RotRoll.SetValue(RotNew.Roll); - } + EditorContext->PosX.SetValue(Pos.X); + EditorContext->PosY.SetValue(Pos.Y); + EditorContext->PosZ.SetValue(Pos.Z); + EditorContext->RotPitch.SetValue(RotNew.Pitch); + EditorContext->RotYaw.SetValue(RotNew.Yaw); + EditorContext->RotRoll.SetValue(RotNew.Roll); + } - if (EditorContext->bMoveCamera) RotatorFix.Roll = EditorContext->RotRoll.GetValue(); - - // Draw Path - if (EditorContext->bShowPath) { - FVector PrevLoc = FVector::ZeroVector; - FRotator PrevRot = FRotator::ZeroRotator; - for (int64 Time = EditorContext->GetAnimation()->AnimationStart; Time <= EditorContext->GetAnimation()->AnimationEnd; ++Time) { - bool bIsKeyframe = EditorContext->Pos.GetKeyframe(Time).IsValid(); - FVector Loc = FVector(EditorContext->PosX.GetValue(Time), EditorContext->PosY.GetValue(Time), EditorContext->PosZ.GetValue(Time)); - if (bIsKeyframe || Loc != PrevLoc) GetWorld()->LineBatcher->DrawLine(Loc, Loc, bIsKeyframe ? FColor::Yellow : FColor::Blue, SDPG_World, 20); - if (PrevLoc != FVector::ZeroVector) { - GetWorld()->LineBatcher->DrawLine(PrevLoc, Loc, FColor::Red, SDPG_World, 5); + if (EditorContext->bMoveCamera) RotatorFix.Roll = EditorContext->RotRoll.GetValue(); + + // Draw Path + if (EditorContext->bShowPath) { + FVector PrevLoc = FVector::ZeroVector; + FRotator PrevRot = FRotator::ZeroRotator; + for (int64 Time = EditorContext->GetAnimation()->AnimationStart; Time <= EditorContext->GetAnimation()->AnimationEnd; ++Time) { + bool bIsKeyframe = EditorContext->Pos.GetKeyframe(Time).IsValid(); + FVector Loc = FVector(EditorContext->PosX.GetValue(Time), EditorContext->PosY.GetValue(Time), EditorContext->PosZ.GetValue(Time)); + if (bIsKeyframe || Loc != PrevLoc) GetWorld()->LineBatcher->DrawLine(Loc, Loc, bIsKeyframe ? FColor::Yellow : FColor::Blue, SDPG_World, 20); + if (PrevLoc != FVector::ZeroVector) { + GetWorld()->LineBatcher->DrawLine(PrevLoc, Loc, FColor::Red, SDPG_World, 5); + } + PrevLoc = Loc; } - PrevLoc = Loc; } - } - UGameplayStatics::SetGlobalTimeDilation(this, EditorContext->GetAnimation()->bBulletTime ? 0.00001 : 1); - CustomTimeDilation = 1.0f/UGameplayStatics::GetGlobalTimeDilation(this); - } + UGameplayStatics::SetGlobalTimeDilation(this, EditorContext->GetAnimation()->bBulletTime ? 0.00001 : 1); + CustomTimeDilation = 1.0f/UGameplayStatics::GetGlobalTimeDilation(this); + } - if (MyController) GetController()->SetControlRotation(RotatorFix); + if (MyController) GetController()->SetControlRotation(RotatorFix); + } } void AFICEditorCameraCharacter::BeginPlay() { From 0bba308189c29ae265a6ed78540409da8b6bc53d Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Sat, 29 Jan 2022 02:48:21 +0100 Subject: [PATCH 3/8] Disable fucking game key-bindings in Editor --- Source/FicsItCam/FICEditorCameraCharacter.cpp | 13 +++++++++++-- Source/FicsItCam/UI/FICEditor.cpp | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/FicsItCam/FICEditorCameraCharacter.cpp b/Source/FicsItCam/FICEditorCameraCharacter.cpp index 196e511..393df67 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.cpp +++ b/Source/FicsItCam/FICEditorCameraCharacter.cpp @@ -18,10 +18,13 @@ AFICEditorCameraCharacter::AFICEditorCameraCharacter() { bSimGravityDisabled = true; SetActorEnableCollision(false); + + bBlockInput = true; bUseControllerRotationPitch = true; bUseControllerRotationYaw = true; bUseControllerRotationRoll = true; + } void AFICEditorCameraCharacter::Tick(float DeltaSeconds) { @@ -150,10 +153,10 @@ void AFICEditorCameraCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason void AFICEditorCameraCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); - + UInputSettings* Settings = const_cast(GetDefault()); Settings->AddAxisMapping(FInputAxisKeyMapping("FicsItCam.Zoom", EKeys::MouseWheelAxis)); - + PlayerInputComponent->BindAxis("MoveForward", this, &AFICEditorCameraCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &AFICEditorCameraCharacter::MoveRight); PlayerInputComponent->BindAxis("FicsItCam.MoveUp", this, &AFICEditorCameraCharacter::FlyUp); @@ -184,12 +187,18 @@ void AFICEditorCameraCharacter::SetupPlayerInputComponent(UInputComponent* Playe void AFICEditorCameraCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); + if (NewController && (EditorContext->IsEditorShown || EditorContext->IsEditorShowing)) NewController->DisableInput(Cast(NewController)); } void AFICEditorCameraCharacter::UnPossessed() { + AController* OldController = Controller; Super::UnPossessed(); UGameplayStatics::SetGlobalTimeDilation(this, 1); CustomTimeDilation = 1; + if (OldController) OldController->EnableInput(Cast(OldController)); + if (OldController && EditorContext && EditorContext->IsEditorShown && !EditorContext->IsEditorShowing) { + EditorContext->HideEditor(); + } } void AFICEditorCameraCharacter::MoveForward(float Value) { diff --git a/Source/FicsItCam/UI/FICEditor.cpp b/Source/FicsItCam/UI/FICEditor.cpp index f4b4ced..32d29a1 100644 --- a/Source/FicsItCam/UI/FICEditor.cpp +++ b/Source/FicsItCam/UI/FICEditor.cpp @@ -194,7 +194,7 @@ void SFICEditor::OnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const } FReply SFICEditor::OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { - if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.ToggleCursor"))) { + if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.ToggleCursor")) || IsAction(Context, InKeyEvent, TEXT("PauseGame"))) { if (GameWidget->HasUserFocusedDescendants(InKeyEvent.GetUserIndex())) { FSlateApplication::Get().SetUserFocus(InKeyEvent.GetUserIndex(), SharedThis(this)); APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); From efaf63e4cee69fa7f6597e8aa23abaf3e84e26ba Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Sat, 29 Jan 2022 13:55:41 +0100 Subject: [PATCH 4/8] Add better mouse interactions like right mouse button viewport click --- Source/FicsItCam/FICEditorCameraCharacter.cpp | 10 ++++++ Source/FicsItCam/FICEditorCameraCharacter.h | 3 ++ Source/FicsItCam/UI/FICEditor.cpp | 36 ++++++++++++++----- Source/FicsItCam/UI/FICEditor.h | 4 +++ Source/FicsItCam/UI/FICEditorContext.cpp | 5 +++ Source/FicsItCam/UI/FICEditorContext.h | 7 +++- 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/Source/FicsItCam/FICEditorCameraCharacter.cpp b/Source/FicsItCam/FICEditorCameraCharacter.cpp index 393df67..ef7e143 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.cpp +++ b/Source/FicsItCam/FICEditorCameraCharacter.cpp @@ -183,6 +183,8 @@ void AFICEditorCameraCharacter::SetupPlayerInputComponent(UInputComponent* Playe PlayerInputComponent->BindAction(TEXT("FicsItCam.ToggleAutoKeyframe"), EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::ToggleAutoKeyframe); PlayerInputComponent->BindAction(TEXT("FicsItCam.ToggleShowPath"), EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::ToggleShowPath); PlayerInputComponent->BindAction(TEXT("FicsItCam.ToggleLockCamera"), EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::ToggleLockCamera); + + PlayerInputComponent->BindKey(EKeys::RightMouseButton, EInputEvent::IE_Released, this, &AFICEditorCameraCharacter::RightMouseRelease); } void AFICEditorCameraCharacter::PossessedBy(AController* NewController) { @@ -271,6 +273,14 @@ void AFICEditorCameraCharacter::ToggleLockCamera() { EditorContext->bMoveCamera = !EditorContext->bMoveCamera; } +void AFICEditorCameraCharacter::RightMouseRelease() { + if (EditorContext->TempViewportFocus) { + EditorContext->TempViewportFocus = false; + EditorContext->EditorWidget->FocusUI(FSlateApplication::Get().GetUserIndexForKeyboard()); + FSlateApplication::Get().SetCursorPos(EditorContext->TempCursorPos); + } +} + void AFICEditorCameraCharacter::SetEditorContext(UFICEditorContext* InEditorContext) { EditorContext = InEditorContext; if (CameraActor) CameraActor->EditorContext = EditorContext; diff --git a/Source/FicsItCam/FICEditorCameraCharacter.h b/Source/FicsItCam/FICEditorCameraCharacter.h index 4a29c35..ef71a11 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.h +++ b/Source/FicsItCam/FICEditorCameraCharacter.h @@ -102,6 +102,9 @@ class AFICEditorCameraCharacter : public ACharacter { UFUNCTION() void ToggleLockCamera(); + UFUNCTION() + void RightMouseRelease(); + UFUNCTION() void SetEditorContext(UFICEditorContext* InEditorContext); diff --git a/Source/FicsItCam/UI/FICEditor.cpp b/Source/FicsItCam/UI/FICEditor.cpp index 32d29a1..2da5f78 100644 --- a/Source/FicsItCam/UI/FICEditor.cpp +++ b/Source/FicsItCam/UI/FICEditor.cpp @@ -181,6 +181,15 @@ FReply SFICEditor::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent return FReply::Unhandled(); } +FReply SFICEditor::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + if (Context->TempViewportFocus && MouseEvent.GetEffectingButton() == EKeys::RightMouseButton) { + Context->TempViewportFocus = false; + FocusUI(MouseEvent.GetUserIndex()); + return FReply::Handled(); + } + return SCompoundWidget::OnMouseButtonUp(MyGeometry, MouseEvent); +} + bool SFICEditor::SupportsKeyboardFocus() const { return true; } @@ -188,24 +197,35 @@ bool SFICEditor::SupportsKeyboardFocus() const { void SFICEditor::OnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent) { SCompoundWidget::OnFocusChanging(PreviousFocusPath, NewWidgetPath, InFocusEvent); if (!PreviousFocusPath.ContainsWidget(GameWidget.ToSharedRef()) && NewWidgetPath.ContainsWidget(GameWidget.ToSharedRef())) { - APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); - UWidgetBlueprintLibrary::SetInputMode_GameOnly(Controller); + if (FSlateApplication::Get().GetPressedMouseButtons().Contains(EKeys::RightMouseButton)) { + Context->TempViewportFocus = true; + } + FocusViewport(InFocusEvent.GetUser(), false); } } FReply SFICEditor::OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.ToggleCursor")) || IsAction(Context, InKeyEvent, TEXT("PauseGame"))) { if (GameWidget->HasUserFocusedDescendants(InKeyEvent.GetUserIndex())) { - FSlateApplication::Get().SetUserFocus(InKeyEvent.GetUserIndex(), SharedThis(this)); - APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); - UWidgetBlueprintLibrary::SetInputMode_UIOnlyEx(Controller); + FocusUI(InKeyEvent.GetUserIndex()); } else { - FSlateApplication::Get().SetUserFocusToGameViewport(InKeyEvent.GetUserIndex()); - APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); - UWidgetBlueprintLibrary::SetInputMode_GameOnly(Controller); + FocusViewport(InKeyEvent.GetUserIndex()); } return FReply::Handled(); } return SCompoundWidget::OnPreviewKeyDown(MyGeometry, InKeyEvent); } +void SFICEditor::FocusUI(uint32 UserIndex, bool bFocusWidget) { + if (bFocusWidget) FSlateApplication::Get().SetUserFocus(UserIndex, SharedThis(this)); + APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); + UWidgetBlueprintLibrary::SetInputMode_UIOnlyEx(Controller); + FSlateApplication::Get().SetCursorPos(Context->TempCursorPos); +} + +void SFICEditor::FocusViewport(uint32 UserIndex, bool bFocusWidget) { + Context->TempCursorPos = FSlateApplication::Get().GetCursorPos(); + if (bFocusWidget) FSlateApplication::Get().SetUserFocusToGameViewport(UserIndex); + APlayerController* Controller = Context->GetWorld()->GetFirstPlayerController(); + UWidgetBlueprintLibrary::SetInputMode_GameOnly(Controller); +} diff --git a/Source/FicsItCam/UI/FICEditor.h b/Source/FicsItCam/UI/FICEditor.h index 367c27b..db0cb00 100644 --- a/Source/FicsItCam/UI/FICEditor.h +++ b/Source/FicsItCam/UI/FICEditor.h @@ -33,8 +33,12 @@ class SFICEditor : public SCompoundWidget { virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual bool SupportsKeyboardFocus() const override; virtual void OnFocusChanging(const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent) override; virtual FReply OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; // End SWidget + + void FocusUI(uint32 UserIndex, bool bFocusWidget = true); + void FocusViewport(uint32 UserIndex, bool bFocusWidget = true); }; diff --git a/Source/FicsItCam/UI/FICEditorContext.cpp b/Source/FicsItCam/UI/FICEditorContext.cpp index 63e2816..91932f2 100644 --- a/Source/FicsItCam/UI/FICEditorContext.cpp +++ b/Source/FicsItCam/UI/FICEditorContext.cpp @@ -12,6 +12,7 @@ void UFICEditorContext::ShowEditor() { HideEditor(); + IsEditorShowing = true; OriginalCharacter = GetWorld()->GetFirstPlayerController()->GetCharacter(); if (!CameraCharacter) CameraCharacter = GetWorld()->SpawnActor(FVector(PosX.GetValue(), PosY.GetValue(), PosZ.GetValue()), FRotator(RotPitch.GetValue(), RotYaw.GetValue(), RotRoll.GetValue())); CameraCharacter->SetEditorContext(this); @@ -39,9 +40,12 @@ void UFICEditorContext::ShowEditor() { GameOverlay->AddSlot()[ EditorWidget.ToSharedRef() ]; + + IsEditorShown = true; } void UFICEditorContext::HideEditor() { + IsEditorShowing = false; if (CameraCharacter) { GetWorld()->GetFirstPlayerController()->Possess(OriginalCharacter); UFGInputLibrary::UpdateInputMappings(GetWorld()->GetFirstPlayerController()); @@ -61,6 +65,7 @@ void UFICEditorContext::HideEditor() { UWidgetBlueprintLibrary::SetInputMode_GameOnly(Controller); } UGameplayStatics::SetGamePaused(this, false); + IsEditorShown = false; } UFICEditorContext::UFICEditorContext() : diff --git a/Source/FicsItCam/UI/FICEditorContext.h b/Source/FicsItCam/UI/FICEditorContext.h index 314cf29..a5fcfca 100644 --- a/Source/FicsItCam/UI/FICEditorContext.h +++ b/Source/FicsItCam/UI/FICEditorContext.h @@ -33,7 +33,6 @@ class UFICEditorContext : public UObject { UPROPERTY() AFICEditorCameraCharacter* CameraCharacter = nullptr; - TSharedPtr EditorWidget; TSharedPtr GameViewport; TSharedPtr GameViewportContainer; TSharedPtr GameOverlay; @@ -58,6 +57,12 @@ class UFICEditorContext : public UObject { float SensorWidthAdjust = 1.0f; + bool IsEditorShown = false; + bool IsEditorShowing = false; + bool TempViewportFocus = false; + FVector2D TempCursorPos; + TSharedPtr EditorWidget; + UFICEditorContext(); void SetAnimation(AFICAnimation* Anim); From 0addb77acfb29ace51f7db1ede417d7585a63261 Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Mon, 31 Jan 2022 16:04:26 +0100 Subject: [PATCH 5/8] Add In-Editor Player & Timelapse Cam TP Command --- Source/FicsItCam/FICCommand.cpp | 16 +++++ Source/FicsItCam/UI/FICEditorContext.cpp | 28 ++++++++ Source/FicsItCam/UI/FICEditorContext.h | 21 +++++- Source/FicsItCam/UI/FICTimeline.cpp | 86 ++++++++++++++++++++++-- Source/FicsItCam/UI/FICTimeline.h | 3 + 5 files changed, 147 insertions(+), 7 deletions(-) diff --git a/Source/FicsItCam/FICCommand.cpp b/Source/FicsItCam/FICCommand.cpp index 1afab4e..3c5b626 100644 --- a/Source/FicsItCam/FICCommand.cpp +++ b/Source/FicsItCam/FICCommand.cpp @@ -189,6 +189,22 @@ EExecutionStatus AFICCommand::ExecuteCommand_Implementation(UCommandSender* Send Sender->SendChatMessage("Timelapse-Camera '" + CameraName + "' stopped."); return EExecutionStatus::COMPLETED; } + } else if (Arguments[1] == "tp") { + if (Arguments.Num() < 3) { + Sender->SendChatMessage(TEXT("Syntax: /fic timelapse stop "), FColor::Red); + return EExecutionStatus::BAD_ARGUMENTS; + } + FString CameraName = FString::Printf(TEXT("%s-Timelapse"), *Arguments[2]); + if (!SubSys->TimelapseCameras.Contains(CameraName)) { + Sender->SendChatMessage("Timelapse-Camera '" + CameraName + "' doesn't exists."); + return EExecutionStatus::UNCOMPLETED; + } else { + AFICTimelapseCamera* timelapseCamera = SubSys->TimelapseCameras[CameraName]; + Sender->GetPlayer()->GetCharacter()->SetActorLocation(timelapseCamera->CaptureComponent->GetComponentLocation()); + Sender->GetPlayer()->SetControlRotation(timelapseCamera->CaptureComponent->GetComponentRotation()); + Sender->SendChatMessage("Teleported to Timelapse-Camera '" + CameraName + "'."); + return EExecutionStatus::COMPLETED; + } } } Sender->SendChatMessage(TEXT("No valid Sub-Subcommand!"), FColor::Red); diff --git a/Source/FicsItCam/UI/FICEditorContext.cpp b/Source/FicsItCam/UI/FICEditorContext.cpp index 91932f2..d2d5a6f 100644 --- a/Source/FicsItCam/UI/FICEditorContext.cpp +++ b/Source/FicsItCam/UI/FICEditorContext.cpp @@ -68,6 +68,13 @@ void UFICEditorContext::HideEditor() { IsEditorShown = false; } +void UFICEditorContext::SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState) { + if (AnimPlayerState != InAnimPlayerState) { + AnimPlayerState = InAnimPlayerState; + AnimPlayerDelta = 0; + } +} + UFICEditorContext::UFICEditorContext() : PosX(TAttribute::Create([this](){ return Animation ? &Animation->PosX : nullptr; }), FFICAttributeValueChanged::CreateUObject(this, &UFICEditorContext::UpdateCharacterValues), FColor::Red), PosY(TAttribute::Create([this](){ return Animation ? &Animation->PosY : nullptr; }), FFICAttributeValueChanged::CreateUObject(this, &UFICEditorContext::UpdateCharacterValues), FColor::Green), @@ -86,6 +93,27 @@ UFICEditorContext::UFICEditorContext() : PosZ.bShowInGraph = true; } +void UFICEditorContext::Tick(float DeltaTime) { + if (Animation) { + AnimPlayerDelta += DeltaTime; + int32 FrameDelta = FMath::Floor(AnimPlayerDelta * Animation->FPS); + AnimPlayerDelta -= (float)FrameDelta / (float)Animation->FPS; + switch (AnimPlayerState) { + case FIC_PLAY_FORWARDS: + SetCurrentFrame(CurrentFrame + FrameDelta); + break; + case FIC_PLAY_BACKWARDS: + SetCurrentFrame(CurrentFrame - FrameDelta); + break; + default: ; + } + } +} + +bool UFICEditorContext::IsTickable() const { + return !WITH_EDITOR; +} + void UFICEditorContext::SetAnimation(AFICAnimation* Anim) { Animation = Anim; SetCurrentFrame(Animation->AnimationStart); diff --git a/Source/FicsItCam/UI/FICEditorContext.h b/Source/FicsItCam/UI/FICEditorContext.h index a5fcfca..8b1ed34 100644 --- a/Source/FicsItCam/UI/FICEditorContext.h +++ b/Source/FicsItCam/UI/FICEditorContext.h @@ -16,8 +16,15 @@ class ACharacter; +UENUM() +enum EFICAnimPlayerState { + FIC_PLAY_PAUSED, + FIC_PLAY_FORWARDS, + FIC_PLAY_BACKWARDS, +}; + UCLASS() -class UFICEditorContext : public UObject { +class UFICEditorContext : public UObject, public FTickableGameObject { GENERATED_BODY() private: @@ -37,6 +44,9 @@ class UFICEditorContext : public UObject { TSharedPtr GameViewportContainer; TSharedPtr GameOverlay; + EFICAnimPlayerState AnimPlayerState = FIC_PLAY_PAUSED; + float AnimPlayerDelta = 0.0f; + public: TFICEditorAttribute PosX; TFICEditorAttribute PosY; @@ -64,6 +74,12 @@ class UFICEditorContext : public UObject { TSharedPtr EditorWidget; UFICEditorContext(); + + // Begin FTickableGameObject + virtual void Tick(float DeltaTime) override; + virtual bool IsTickable() const override; + virtual TStatId GetStatId() const override { return UObject::GetStatID(); } + // End FTickableGameObject void SetAnimation(AFICAnimation* Anim); AFICAnimation* GetAnimation() const; @@ -79,4 +95,7 @@ class UFICEditorContext : public UObject { void ShowEditor(); void HideEditor(); + + void SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState); + EFICAnimPlayerState GetAnimPlayer() { return AnimPlayerState; } }; diff --git a/Source/FicsItCam/UI/FICTimeline.cpp b/Source/FicsItCam/UI/FICTimeline.cpp index 936ae3e..646681c 100644 --- a/Source/FicsItCam/UI/FICTimeline.cpp +++ b/Source/FicsItCam/UI/FICTimeline.cpp @@ -8,10 +8,22 @@ FSlateColorBrush SFICTimelinePanel::DefaultBackgroundBrush = FSlateColorBrush(FColor::FromHex("030303")); +FCheckBoxStyle SFICTimelinePanel::DefaultToggleButtonStyle = FCoreStyle::Get().GetWidgetStyle("ToggleButtonStyle"); +FSlateBoxBrush SFICTimelinePanel::DefaultToggleButtonChecked = FSlateBoxBrush(((FSlateStyleSet&)FCoreStyle::Get()).RootToContentDir("Common/RoundedSelection_16x", TEXT(".png")), 4.0f/16.0f, FLinearColor(0.25f, 0.25f, 0.25f)); +FSlateBoxBrush SFICTimelinePanel::DefaultToggleButtonUnchecked = FSlateBoxBrush(((FSlateStyleSet&)FCoreStyle::Get()).RootToContentDir("Common/RoundedSelection_16x", TEXT(".png")), 4.0f/16.0f, FLinearColor(0.72f, 0.72f, 0.72f)); + void SFICTimelinePanel::Construct(const FArguments& InArgs) { Context = InArgs._Context; BackgroundBrush = InArgs._Background; + + DefaultToggleButtonStyle.CheckedImage = static_cast(DefaultToggleButtonChecked); + DefaultToggleButtonStyle.CheckedHoveredImage = static_cast(DefaultToggleButtonChecked); + DefaultToggleButtonStyle.CheckedPressedImage = static_cast(DefaultToggleButtonChecked); + DefaultToggleButtonStyle.UncheckedImage = static_cast(DefaultToggleButtonUnchecked); + DefaultToggleButtonStyle.UncheckedHoveredImage = static_cast(DefaultToggleButtonUnchecked); + DefaultToggleButtonStyle.UncheckedPressedImage = static_cast(DefaultToggleButtonUnchecked); + for (TTuple> Attribute : Context->All.GetChildAttributes()) { Attributes.Add(MakeShared(Attribute.Key, Attribute.Value.Get())); } @@ -92,11 +104,42 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { .AllowSpin(true) ] ] - +SGridPanel::Slot(2,0).Padding(5)[ + +SGridPanel::Slot(2, 1) + .Padding(5) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center)[ + SNew(SCheckBox) + .Type(ESlateCheckBoxType::ToggleButton) + .Style(&DefaultToggleButtonStyle) + .Padding(5) + .Content()[ + SNew(STextBlock) + .Text(FText::FromString("<")) + .ColorAndOpacity(FColor::Black) + ] + .ToolTipText_Lambda([this]() { + if (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) { + return FText::FromString(TEXT("Pause Play")); + } else { + return FText::FromString(TEXT("Play Reverse")); + } + }) + .IsChecked_Lambda([this]() { + return (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + }) + .OnCheckStateChanged_Lambda([this](ECheckBoxState State) { + if (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED); + } else { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_BACKWARDS); + } + }) + ] + +SGridPanel::Slot(3,0).Padding(5)[ SNew(STextBlock) .Text(FText::FromString("Current Frame:")) ] - +SGridPanel::Slot(2, 1).Padding(5)[ + +SGridPanel::Slot(3, 1).Padding(5)[ SNew(SBox) .WidthOverride(50) .Content()[ @@ -126,11 +169,42 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { .AllowSpin(true) ] ] - +SGridPanel::Slot(3,0).Padding(5)[ + +SGridPanel::Slot(4, 1) + .Padding(5) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center)[ + SNew(SCheckBox) + .Type(ESlateCheckBoxType::ToggleButton) + .Style(&DefaultToggleButtonStyle) + .Padding(5) + .Content()[ + SNew(STextBlock) + .Text(FText::FromString(">")) + .ColorAndOpacity(FColor::Black) + ] + .ToolTipText_Lambda([this]() { + if (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) { + return FText::FromString(TEXT("Pause Play")); + } else { + return FText::FromString(TEXT("Play")); + } + }) + .IsChecked_Lambda([this]() { + return (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + }) + .OnCheckStateChanged_Lambda([this](ECheckBoxState State) { + if (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED); + } else { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_FORWARDS); + } + }) + ] + +SGridPanel::Slot(5,0).Padding(5)[ SNew(STextBlock) .Text(FText::FromString("View End:")) ] - +SGridPanel::Slot(3, 1).Padding(5)[ + +SGridPanel::Slot(5, 1).Padding(5)[ SNew(SBox) .WidthOverride(50) .Content()[ @@ -160,11 +234,11 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { .AllowSpin(true) ] ] - +SGridPanel::Slot(4,0).Padding(5)[ + +SGridPanel::Slot(6,0).Padding(5)[ SNew(STextBlock) .Text(FText::FromString("Animation End:")) ] - +SGridPanel::Slot(4, 1).Padding(5)[ + +SGridPanel::Slot(6, 1).Padding(5)[ SNew(SBox) .WidthOverride(50) .Content()[ diff --git a/Source/FicsItCam/UI/FICTimeline.h b/Source/FicsItCam/UI/FICTimeline.h index 4e23cde..2ae2e26 100644 --- a/Source/FicsItCam/UI/FICTimeline.h +++ b/Source/FicsItCam/UI/FICTimeline.h @@ -16,6 +16,9 @@ struct FFICEditorAttributeReference { class SFICTimelinePanel : public SCompoundWidget { static FSlateColorBrush DefaultBackgroundBrush; + static FCheckBoxStyle DefaultToggleButtonStyle; + static FSlateBoxBrush DefaultToggleButtonChecked; + static FSlateBoxBrush DefaultToggleButtonUnchecked; SLATE_BEGIN_ARGS(SFICTimelinePanel) : _Background(&DefaultBackgroundBrush) {} From f3ab8e247454975a2c7c60899975d48d9bf99a50 Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Tue, 1 Feb 2022 00:00:53 +0100 Subject: [PATCH 6/8] Undo/Redo & Bug Fixes --- Source/FicsItCam/FICAnimation.cpp | 20 ++++++++--- Source/FicsItCam/FICAnimation.h | 7 ++++ Source/FicsItCam/FICEditorCameraCharacter.cpp | 34 ++++++++++++++++++ Source/FicsItCam/FICEditorCameraCharacter.h | 5 +++ Source/FicsItCam/UI/FICDetails.cpp | 14 ++++---- Source/FicsItCam/UI/FICDragDrop.cpp | 20 ++++++++++- Source/FicsItCam/UI/FICDragDrop.h | 9 ++++- Source/FicsItCam/UI/FICEditor.cpp | 35 +++++++++++++++++-- Source/FicsItCam/UI/FICEditorContext.h | 3 ++ Source/FicsItCam/UI/FICGraphView.cpp | 11 ++++-- Source/FicsItCam/UI/FICGraphView.h | 4 ++- Source/FicsItCam/UI/FICKeyframeControl.cpp | 17 +++++++-- Source/FicsItCam/UI/FICKeyframeControl.h | 5 ++- Source/FicsItCam/UI/FICTimeline.cpp | 2 +- Source/FicsItCam/UI/FICVectorEditor.cpp | 9 ++--- Source/FicsItCam/UI/FICVectorEditor.h | 5 ++- 16 files changed, 171 insertions(+), 29 deletions(-) diff --git a/Source/FicsItCam/FICAnimation.cpp b/Source/FicsItCam/FICAnimation.cpp index 8544812..ee7d5a4 100644 --- a/Source/FicsItCam/FICAnimation.cpp +++ b/Source/FicsItCam/FICAnimation.cpp @@ -197,12 +197,25 @@ void FFICFloatAttribute::RecalculateKeyframe(int64 Time) { } } +void FFICFloatAttribute::Set(TSharedPtr InAttrib) { + FOnUpdate OnUpdateBuf = OnUpdate; + if (InAttrib && InAttrib->GetAttributeType() == GetAttributeType()) { + *this = *StaticCastSharedPtr(InAttrib); + } + OnUpdate = OnUpdateBuf; + OnUpdate.Broadcast(); +} + +TSharedPtr FFICFloatAttribute::Get() { + return MakeShared(*this); +} + FFICFloatKeyframe* FFICFloatAttribute::SetKeyframe(int64 Time, FFICFloatKeyframe Keyframe) { FFICFloatKeyframe* KF = &Keyframes.FindOrAdd(Time); *KF = Keyframe; return KF; } -#pragma optimize("", off) + float FFICFloatAttribute::GetValue(float Time) { float Time1 = TNumericLimits::Lowest(); FFICFloatKeyframe KF1; @@ -244,7 +257,6 @@ float FFICFloatAttribute::GetValue(float Time) { } return Interpolated; } -#pragma optimize("", on) TMap> FFICGroupAttribute::GetKeyframes() { TMap> Keyframes; @@ -317,9 +329,9 @@ void AFICAnimation::RecalculateAllKeyframes() { } float AFICAnimation::GetEndOfAnimation() { - return AnimationEnd / FPS; + return (float)AnimationEnd / (float)FPS; } float AFICAnimation::GetStartOfAnimation() { - return AnimationStart / FPS; + return (float)AnimationStart / (float)FPS; } diff --git a/Source/FicsItCam/FICAnimation.h b/Source/FicsItCam/FICAnimation.h index 92482b1..32321e4 100644 --- a/Source/FicsItCam/FICAnimation.h +++ b/Source/FicsItCam/FICAnimation.h @@ -69,12 +69,15 @@ struct FFICAttribute { FOnUpdate OnUpdate; virtual ~FFICAttribute() = default; + virtual FName GetAttributeType() { return FName(); } virtual TMap> GetKeyframes() { return TMap>(); } virtual EFICKeyframeType GetAllowedKeyframeTypes() const { return FIC_KF_NONE; } virtual TSharedPtr AddKeyframe(int64 Time) { return nullptr; } virtual void RemoveKeyframe(int64 Time) {} virtual void MoveKeyframe(int64 From, int64 To) {} virtual void RecalculateKeyframe(int64 Time) {} + virtual void Set(TSharedPtr InAttrib) {} + virtual TSharedPtr Get() { return nullptr; } void RecalculateAllKeyframes(); bool GetNextKeyframe(int64 Time, int64& outTime, TSharedPtr& outKeyframe); @@ -131,12 +134,15 @@ struct FFICFloatAttribute : public FFICAttribute { float FallBackValue = 0.0f; // Begin FFICAttribute + virtual FName GetAttributeType() { return FName(TEXT("FloatAttribute")); } virtual TMap> GetKeyframes() override; virtual EFICKeyframeType GetAllowedKeyframeTypes() const override; virtual TSharedPtr AddKeyframe(int64 Time) override; virtual void RemoveKeyframe(int64 Time) override; virtual void MoveKeyframe(int64 From, int64 To) override; virtual void RecalculateKeyframe(int64 Time) override; + virtual void Set(TSharedPtr InAttrib) override; + virtual TSharedPtr Get() override; // End FFICAttribute FFICFloatKeyframe* SetKeyframe(int64 Time, FFICFloatKeyframe Keyframe); @@ -152,6 +158,7 @@ struct FFICGroupAttribute : public FFICAttribute { TMap> Children; // Begin FFICAttribute + virtual FName GetAttributeType() { return FName(TEXT("GroupAttribute")); } virtual TMap> GetKeyframes() override; virtual EFICKeyframeType GetAllowedKeyframeTypes() const override; virtual TSharedPtr AddKeyframe(int64 Time) override; diff --git a/Source/FicsItCam/FICEditorCameraCharacter.cpp b/Source/FicsItCam/FICEditorCameraCharacter.cpp index ef7e143..c8fd68a 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.cpp +++ b/Source/FicsItCam/FICEditorCameraCharacter.cpp @@ -185,6 +185,9 @@ void AFICEditorCameraCharacter::SetupPlayerInputComponent(UInputComponent* Playe PlayerInputComponent->BindAction(TEXT("FicsItCam.ToggleLockCamera"), EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::ToggleLockCamera); PlayerInputComponent->BindKey(EKeys::RightMouseButton, EInputEvent::IE_Released, this, &AFICEditorCameraCharacter::RightMouseRelease); + + PlayerInputComponent->BindKey(EKeys::Z, EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::Undo); + PlayerInputComponent->BindKey(EKeys::Y, EInputEvent::IE_Pressed, this, &AFICEditorCameraCharacter::Redo); } void AFICEditorCameraCharacter::PossessedBy(AController* NewController) { @@ -281,12 +284,33 @@ void AFICEditorCameraCharacter::RightMouseRelease() { } } +void AFICEditorCameraCharacter::Undo() { + TSharedPtr Change = EditorContext->ChangeList.PopChange(); + if (Change) Change->UndoChange(); +} + +void AFICEditorCameraCharacter::Redo() { + TSharedPtr Change = EditorContext->ChangeList.PushChange(); + if (Change) Change->RedoChange(); +} + void AFICEditorCameraCharacter::SetEditorContext(UFICEditorContext* InEditorContext) { EditorContext = InEditorContext; if (CameraActor) CameraActor->EditorContext = EditorContext; } void AFICEditorCameraCharacter::ChangedKeyframe() { + auto Change = MakeShared(); + Change->PushChange(MakeShared(EditorContext, TNumericLimits::Min(), EditorContext->GetCurrentFrame())); + BEGIN_ATTRIB_CHANGE(EditorContext->PosX.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->PosY.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->PosZ.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->RotPitch.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->RotYaw.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->RotRoll.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->FOV.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->FocusDistance.GetAttribute()) + BEGIN_ATTRIB_CHANGE(EditorContext->Aperture.GetAttribute()) int64 Time = EditorContext->GetCurrentFrame(); if (EditorContext->PosX.GetKeyframe(Time) && EditorContext->PosY.GetKeyframe(Time) && EditorContext->PosZ.GetKeyframe(Time) && EditorContext->RotPitch.GetKeyframe(Time) && EditorContext->RotYaw.GetKeyframe(Time) && EditorContext->RotRoll.GetKeyframe(Time) && EditorContext->FOV.GetKeyframe(Time) && !EditorContext->PosX.HasChanged(Time) && !EditorContext->PosY.HasChanged(Time) && !EditorContext->PosZ.HasChanged(Time) && !EditorContext->RotPitch.HasChanged(Time) && !EditorContext->RotYaw.HasChanged(Time) && !EditorContext->RotRoll.HasChanged(Time) && !EditorContext->FOV.HasChanged(Time)) { @@ -294,6 +318,16 @@ void AFICEditorCameraCharacter::ChangedKeyframe() { } else { EditorContext->All.SetKeyframe(Time); } + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + EditorContext->ChangeList.PushChange(Change); } void AFICEditorCameraCharacter::Zoom(float Value) { diff --git a/Source/FicsItCam/FICEditorCameraCharacter.h b/Source/FicsItCam/FICEditorCameraCharacter.h index ef71a11..a12ad3c 100644 --- a/Source/FicsItCam/FICEditorCameraCharacter.h +++ b/Source/FicsItCam/FICEditorCameraCharacter.h @@ -105,6 +105,11 @@ class AFICEditorCameraCharacter : public ACharacter { UFUNCTION() void RightMouseRelease(); + UFUNCTION() + void Undo(); + UFUNCTION() + void Redo(); + UFUNCTION() void SetEditorContext(UFICEditorContext* InEditorContext); diff --git a/Source/FicsItCam/UI/FICDetails.cpp b/Source/FicsItCam/UI/FICDetails.cpp index ef71697..9c63e75 100644 --- a/Source/FicsItCam/UI/FICDetails.cpp +++ b/Source/FicsItCam/UI/FICDetails.cpp @@ -40,7 +40,7 @@ TSharedRef ScalarAttribute(UFICEditorContext* Context, TFICEditorAttrib .TypeInterface(MakeShared>()) ] +SHorizontalBox::Slot().Padding(5).AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute_Lambda([&Attr]() -> FFICEditorAttributeBase* { return &Attr; }) @@ -74,7 +74,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .ToolTipText(FText::FromString(FString::Printf(TEXT("Allows you to manipulate the keyframes of all attributes at the current frame.\n\nToggle All Keyframes: %s\nGo-To Next Keyframe: %s\nGo-To Previous Keyframe: %s"), *UFICUtils::KeymappingToString("FicsItCam.ToggleAllKeyframes"), *UFICUtils::KeymappingToString("FicsItCam.NextKeyframe"), *UFICUtils::KeymappingToString("FicsItCam.PrevKeyframe")))) ] +SHorizontalBox::Slot().Padding(5).AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute_Lambda([this]() -> FFICEditorAttributeBase* { return &Context->All; }) @@ -90,7 +90,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .Text(FText::FromString("Position")) ] +SHorizontalBox::Slot().Padding(5).AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute_Lambda([this]() -> FFICEditorAttributeBase* { return &Context->Pos; }) @@ -103,7 +103,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .Text(FText::FromString(":")) ] +SHorizontalBox::Slot().Padding(5).FillWidth(1)[ - SNew(SFICVectorEditor) + SNew(SFICVectorEditor, Context) .ShowKeyframeControls(true) .XAttr_Lambda([this]() { return &Context->PosX; @@ -129,7 +129,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .Text(FText::FromString("Rotation")) ] +SHorizontalBox::Slot().Padding(5).AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute_Lambda([this]() -> FFICEditorAttributeBase* { return &Context->Rot; }) @@ -142,7 +142,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .Text(FText::FromString(":")) ] +SHorizontalBox::Slot().Padding(5).FillWidth(1)[ - SNew(SFICVectorEditor) + SNew(SFICVectorEditor, Context) .ShowKeyframeControls(true) .XAttr_Lambda([this]() { return &Context->RotPitch; @@ -188,7 +188,7 @@ void SFICDetails::Construct(const FArguments& InArgs) { .TypeInterface(MakeShared>()) ] +SHorizontalBox::Slot().Padding(5).AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute_Lambda([this]() { return &Context->FOV; }) diff --git a/Source/FicsItCam/UI/FICDragDrop.cpp b/Source/FicsItCam/UI/FICDragDrop.cpp index bb02215..5549dea 100644 --- a/Source/FicsItCam/UI/FICDragDrop.cpp +++ b/Source/FicsItCam/UI/FICDragDrop.cpp @@ -81,7 +81,17 @@ FVector2D FFICGraphKeyframeDragDrop::GetDecoratorPosition() const { return FFICGraphDragDrop::GetDecoratorPosition(); } -FFICGraphKeyframeHandleDragDrop::FFICGraphKeyframeHandleDragDrop(TSharedPtr KeyframeHandle, FPointerEvent InitEvent) : FFICGraphDragDrop(SharedThis(KeyframeHandle->KeyframeControl->GraphView), InitEvent), KeyframeHandle(KeyframeHandle) {} +void FFICGraphKeyframeDragDrop::OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) { + FFICGraphDragDrop::OnDrop(bDropWasHandled, MouseEvent); + auto Change = MakeShared(); + Change->PushChange(MakeShared(KeyframeControl->Context, TimelineStart, KeyframeControl->GetFrame())); + Change->PushChange(MakeShared(KeyframeControl->GetAttribute()->GetAttribute(), AttribBegin)); + KeyframeControl->Context->ChangeList.PushChange(Change); +} + +FFICGraphKeyframeHandleDragDrop::FFICGraphKeyframeHandleDragDrop(TSharedPtr KeyframeHandle, FPointerEvent InitEvent) : FFICGraphDragDrop(SharedThis(KeyframeHandle->KeyframeControl->GraphView), InitEvent), KeyframeHandle(KeyframeHandle) { + AttribBegin = KeyframeHandle->KeyframeControl->GetAttribute()->GetAttribute()->Get(); +} void FFICGraphKeyframeHandleDragDrop::OnDragged(const FDragDropEvent& DragDropEvent) { FFICGraphDragDrop::OnDragged(DragDropEvent); @@ -156,3 +166,11 @@ void FFICGraphKeyframeHandleDragDrop::OnDragged(const FDragDropEvent& DragDropEv } } } + +void FFICGraphKeyframeHandleDragDrop::OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) { + FFICGraphDragDrop::OnDrop(bDropWasHandled, MouseEvent); + auto Change = MakeShared(); + Change->PushChange(MakeShared(KeyframeHandle->KeyframeControl->Context, TimelineStart, KeyframeHandle->KeyframeControl->GetFrame())); + Change->PushChange(MakeShared(KeyframeHandle->KeyframeControl->GetAttribute()->GetAttribute(), AttribBegin)); + KeyframeHandle->KeyframeControl->Context->ChangeList.PushChange(Change); +} diff --git a/Source/FicsItCam/UI/FICDragDrop.h b/Source/FicsItCam/UI/FICDragDrop.h index 4b4370b..ceac111 100644 --- a/Source/FicsItCam/UI/FICDragDrop.h +++ b/Source/FicsItCam/UI/FICDragDrop.h @@ -15,6 +15,7 @@ class FFICGraphDragDrop : public FDragDropOperation { int64 TimelineDiff; float ValueDiff; + FFICGraphDragDrop(TSharedRef GraphView, FPointerEvent InitEvent); // Begin FDragDropOperation @@ -40,13 +41,17 @@ class FFICGraphKeyframeDragDrop : public FFICGraphDragDrop { DRAG_DROP_OPERATOR_TYPE(FFICGraphKeyframeDragDrop, FFICGraphDragDrop) TSharedPtr KeyframeControl; + TSharedPtr AttribBegin; - FFICGraphKeyframeDragDrop(TSharedRef GraphView, TSharedRef KeyframeControl, FPointerEvent InitEvent) : FFICGraphDragDrop(GraphView, InitEvent), KeyframeControl(KeyframeControl) {} + FFICGraphKeyframeDragDrop(TSharedRef GraphView, TSharedRef KeyframeControl, FPointerEvent InitEvent) : FFICGraphDragDrop(GraphView, InitEvent), KeyframeControl(KeyframeControl) { + AttribBegin = KeyframeControl->GetAttribute()->GetAttribute()->Get(); + } // Begin FDragDropOperation virtual void OnDragged( const FDragDropEvent& DragDropEvent ) override; virtual TSharedPtr GetDefaultDecorator() const override; virtual FVector2D GetDecoratorPosition() const override; + virtual void OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) override; // End FDragDropOperation }; @@ -55,10 +60,12 @@ class FFICGraphKeyframeHandleDragDrop : public FFICGraphDragDrop { DRAG_DROP_OPERATOR_TYPE(FFICGraphKeyframeHandleDragDrop, FFICGraphDragDrop) TSharedPtr KeyframeHandle; + TSharedPtr AttribBegin; FFICGraphKeyframeHandleDragDrop(TSharedPtr KeyframeHandle, FPointerEvent InitEvent); // Begin FDragDropOperation virtual void OnDragged( const FDragDropEvent& DragDropEvent ) override; + virtual void OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) override; // End FDragDropOperation }; \ No newline at end of file diff --git a/Source/FicsItCam/UI/FICEditor.cpp b/Source/FicsItCam/UI/FICEditor.cpp index 2da5f78..ca5082f 100644 --- a/Source/FicsItCam/UI/FICEditor.cpp +++ b/Source/FicsItCam/UI/FICEditor.cpp @@ -106,13 +106,34 @@ FReply SFICEditor::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKey return FReply::Handled(); } else /*if (!GameWidget->HasAnyUserFocusOrFocusedDescendants())*/ { if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.ToggleAllKeyframes"))) { + auto Change = MakeShared(); + Change->PushChange(MakeShared(Context, TNumericLimits::Min(), Context->GetCurrentFrame())); + BEGIN_ATTRIB_CHANGE(Context->PosX.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->PosY.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->PosZ.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->RotPitch.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->RotYaw.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->RotRoll.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->FOV.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->Aperture.GetAttribute()) + BEGIN_ATTRIB_CHANGE(Context->FocusDistance.GetAttribute()) int64 Time = Context->GetCurrentFrame(); if (Context->PosX.GetKeyframe(Time) && Context->PosY.GetKeyframe(Time) && Context->PosZ.GetKeyframe(Time) && Context->RotPitch.GetKeyframe(Time) && Context->RotYaw.GetKeyframe(Time) && Context->RotRoll.GetKeyframe(Time) && Context->FOV.GetKeyframe(Time) && !Context->PosX.HasChanged(Time) && !Context->PosY.HasChanged(Time) && !Context->PosZ.HasChanged(Time) && !Context->RotPitch.HasChanged(Time) && !Context->RotYaw.HasChanged(Time) && !Context->RotRoll.HasChanged(Time) && !Context->FOV.HasChanged(Time)) { Context->All.RemoveKeyframe(Time); - } else { - Context->All.SetKeyframe(Time); - } + } else { + Context->All.SetKeyframe(Time); + } + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + END_ATTRIB_CHANGE(Change) + Context->ChangeList.PushChange(Change); return FReply::Handled(); } else if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.PrevFrame"))) { int64 Rate = 1; @@ -147,6 +168,14 @@ FReply SFICEditor::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKey } else if (IsAction(Context, InKeyEvent, TEXT("FicsItCam.ToggleLockCamera"))) { Context->bMoveCamera = !Context->bMoveCamera; return FReply::Handled(); + } else if (InKeyEvent.GetKey() == EKeys::Z && InKeyEvent.IsControlDown()) { + TSharedPtr Change = Context->ChangeList.PopChange(); + if (Change) Change->UndoChange(); + return FReply::Handled(); + } else if (InKeyEvent.GetKey() == EKeys::Y && InKeyEvent.IsControlDown()) { + TSharedPtr Change = Context->ChangeList.PushChange(); + if (Change) Change->RedoChange(); + return FReply::Handled(); } } return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent); diff --git a/Source/FicsItCam/UI/FICEditorContext.h b/Source/FicsItCam/UI/FICEditorContext.h index 8b1ed34..733c6c3 100644 --- a/Source/FicsItCam/UI/FICEditorContext.h +++ b/Source/FicsItCam/UI/FICEditorContext.h @@ -1,5 +1,6 @@ #pragma once +#include "FICChangeList.h" #include "FicsItCam/FICAnimation.h" #include "FicsItCam/FICEditorCameraCharacter.h" #include "FICEditor.h" @@ -73,6 +74,8 @@ class UFICEditorContext : public UObject, public FTickableGameObject { FVector2D TempCursorPos; TSharedPtr EditorWidget; + FFICChangeList ChangeList; + UFICEditorContext(); // Begin FTickableGameObject diff --git a/Source/FicsItCam/UI/FICGraphView.cpp b/Source/FicsItCam/UI/FICGraphView.cpp index 38085b7..4c87e2f 100644 --- a/Source/FicsItCam/UI/FICGraphView.cpp +++ b/Source/FicsItCam/UI/FICGraphView.cpp @@ -4,7 +4,7 @@ FSlateColorBrush SFICGraphView::DefaultAnimationBrush = FSlateColorBrush(FColor::FromHex("050505")); -void SFICGraphView::Construct(const FArguments& InArgs) { +void SFICGraphView::Construct(const FArguments& InArgs, UFICEditorContext* InContext) { AnimationBrush = InArgs._AnimationBrush; ActiveFrame = InArgs._Frame; TimelineRangeBegin = InArgs._TimelineRangeBegin; @@ -15,6 +15,7 @@ void SFICGraphView::Construct(const FArguments& InArgs) { AnimationEnd = InArgs._AnimationEnd; OnTimelineRangeChanged = InArgs._OnTimelineRangedChanged; OnValueRangeChanged = InArgs._OnValueRangeChanged; + Context = InContext; if (!OnTimelineRangeChanged.IsBound()) OnTimelineRangeChanged.BindLambda([this](int64 Start, int64 End) { if (!TimelineRangeBegin.IsBound()) TimelineRangeBegin.Set(Start); @@ -126,8 +127,10 @@ FReply SFICGraphView::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointe FVector2D PlotPoint = FrameAttributeToLocal(Attribute, Frame); FVector2D Difference = PlotPoint - LocalMousePos; if (Difference.Size() < 5) { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute->GetAttribute(), TNumericLimits::Min(), Frame) Attribute->SetKeyframeFromFloat(Frame, LocalToValue(LocalMousePos.Y)); Attribute->GetAttribute()->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) return FReply::Handled(); } } @@ -237,7 +240,7 @@ void SFICGraphView::Update() { for (FFICEditorAttributeBase* Attribute : Attributes) { for (const TPair>& Keyframe : Attribute->GetAttribute()->GetKeyframes()) { TSharedRef Child = - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute(Attribute) .Frame(Keyframe.Key) .GraphView(this) @@ -247,11 +250,12 @@ void SFICGraphView::Update() { } } +#pragma optimize("", off) void SFICGraphView::FitAll() { int64 FrameMin = AnimationStart.Get(); int64 FrameMax = AnimationEnd.Get(); float ValueMin = TNumericLimits::Max(); - float ValueMax = TNumericLimits::Min(); + float ValueMax = TNumericLimits::Lowest(); int MaxKeyframeCountOfAnyAttribute = 0; for (FFICEditorAttributeBase* Attribute : Attributes) { TMap> Keyframes = Attribute->GetAttribute()->GetKeyframes(); @@ -287,6 +291,7 @@ void SFICGraphView::FitAll() { } OnValueRangeChanged.Execute(ValueMin, ValueMax); } +#pragma optimize("", on) int64 SFICGraphView::LocalToFrame(float Local) const { return (int64) FMath::Lerp( diff --git a/Source/FicsItCam/UI/FICGraphView.h b/Source/FicsItCam/UI/FICGraphView.h index 0c5e07f..667a703 100644 --- a/Source/FicsItCam/UI/FICGraphView.h +++ b/Source/FicsItCam/UI/FICGraphView.h @@ -27,7 +27,7 @@ class SFICGraphView : public SPanel { SLATE_END_ARGS() public: - void Construct(const FArguments& InArgs); + void Construct(const FArguments& InArgs, UFICEditorContext* Context); private: static FSlateColorBrush DefaultAnimationBrush; @@ -49,6 +49,8 @@ class SFICGraphView : public SPanel { TMap DelegateHandles; public: + UFICEditorContext* Context = nullptr; + SFICGraphView(); virtual ~SFICGraphView() override; diff --git a/Source/FicsItCam/UI/FICKeyframeControl.cpp b/Source/FicsItCam/UI/FICKeyframeControl.cpp index fe4d4b2..466b4a7 100644 --- a/Source/FicsItCam/UI/FICKeyframeControl.cpp +++ b/Source/FicsItCam/UI/FICKeyframeControl.cpp @@ -103,11 +103,12 @@ FFICKeyframeControlStyle* SFICKeyframeControl::DefaultStyle() { SFICKeyframeControl::SFICKeyframeControl() : Children(this) {} -void SFICKeyframeControl::Construct(FArguments InArgs) { +void SFICKeyframeControl::Construct(FArguments InArgs, UFICEditorContext* InContext) { Attribute = InArgs._Attribute; Style = InArgs._Style; Frame = InArgs._Frame; GraphView = InArgs._GraphView; + Context = InContext; Children.Add( SAssignNew(MainHandle, SBox) @@ -265,9 +266,11 @@ FReply SFICKeyframeControl::OnMouseButtonDown(const FGeometry& MyGeometry, const FReply SFICKeyframeControl::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& Event) { if (Event.GetEffectingButton() == EKeys::LeftMouseButton && !bWasDoubleClick) { FFICAttribute* Attrib = Attribute.Get()->GetAttribute(); + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attrib, GetFrame(), GetFrame()) if (Attribute.Get()->GetKeyframe(GetFrame()) && (ToHandle || FromHandle || !Attribute.Get()->HasChanged(GetFrame()))) Attribute.Get()->RemoveKeyframe(GetFrame()); else Attribute.Get()->SetKeyframe(GetFrame()); - Attrib->OnUpdate.Broadcast(); + Attrib->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) return FReply::Handled(); } else if (Event.GetEffectingButton() == EKeys::RightMouseButton) { TSharedPtr KF = Attribute.Get()->GetKeyframe(GetFrame()); @@ -279,32 +282,40 @@ FReply SFICKeyframeControl::OnMouseButtonUp(const FGeometry& MyGeometry, const F FText(), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([KF, this]() { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute.Get()->GetAttribute(), GetFrame(), GetFrame()) Attribute.Get()->SetKeyframeFromFloat(GetFrame(), Attribute.Get()->GetKeyframe(GetFrame())->Get()->GetValueAsFloat(), FIC_KF_EASE, false); Attribute.Get()->GetAttribute()->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) }), FCanExecuteAction::CreateRaw(&FSlateApplication::Get(), &FSlateApplication::IsNormalExecution))); MenuBuilder.AddMenuEntry( FText::FromString("Ease-In/Out"), FText(), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([KF, this]() { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute.Get()->GetAttribute(), GetFrame(), GetFrame()) Attribute.Get()->SetKeyframeFromFloat(GetFrame(), Attribute.Get()->GetKeyframe(GetFrame())->Get()->GetValueAsFloat(), FIC_KF_EASEINOUT, false); Attribute.Get()->GetAttribute()->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) }), FCanExecuteAction::CreateRaw(&FSlateApplication::Get(), &FSlateApplication::IsNormalExecution))); MenuBuilder.AddMenuEntry( FText::FromString("Linear"), FText(), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([KF, this]() { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute.Get()->GetAttribute(), GetFrame(), GetFrame()) Attribute.Get()->SetKeyframeFromFloat(GetFrame(), Attribute.Get()->GetKeyframe(GetFrame())->Get()->GetValueAsFloat(), FIC_KF_LINEAR, false); Attribute.Get()->GetAttribute()->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) }), FCanExecuteAction::CreateRaw(&FSlateApplication::Get(), &FSlateApplication::IsNormalExecution))); MenuBuilder.AddMenuEntry( FText::FromString("Step"), FText(), FSlateIcon(), FUIAction(FExecuteAction::CreateLambda([KF, this]() { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute.Get()->GetAttribute(), GetFrame(), GetFrame()) Attribute.Get()->SetKeyframeFromFloat(GetFrame(), Attribute.Get()->GetKeyframe(GetFrame())->Get()->GetValueAsFloat(), FIC_KF_STEP, false); Attribute.Get()->GetAttribute()->RecalculateAllKeyframes(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) }), FCanExecuteAction::CreateRaw(&FSlateApplication::Get(), &FSlateApplication::IsNormalExecution))); FSlateApplication::Get().PushMenu(SharedThis(this), *Event.GetEventPath(), MenuBuilder.MakeWidget(), Event.GetScreenSpacePosition(), FPopupTransitionEffect::ContextMenu); @@ -321,8 +332,10 @@ FReply SFICKeyframeControl::OnMouseButtonDoubleClick(const FGeometry& MyGeometry FFICEditorAttributeBase* Attr = Attribute.Get(); TMap> Keyframes = Attr->GetAttribute()->GetKeyframes(); if (Keyframes.Num() > 0) { + BEGIN_QUICK_ATTRIB_CHANGE(Context, Attr->GetAttribute(), GetFrame(), GetFrame()) for (const TPair>& KF : Keyframes) Attr->RemoveKeyframe(KF.Key); Attr->GetAttribute()->OnUpdate.Broadcast(); + END_QUICK_ATTRIB_CHANGE(Context->ChangeList) return FReply::Handled(); } } diff --git a/Source/FicsItCam/UI/FICKeyframeControl.h b/Source/FicsItCam/UI/FICKeyframeControl.h index 4d10b76..1a9f5d7 100644 --- a/Source/FicsItCam/UI/FICKeyframeControl.h +++ b/Source/FicsItCam/UI/FICKeyframeControl.h @@ -1,5 +1,6 @@ #pragma once +#include "FICEditorContext.h" #include "FicsItCam/FICEditorAttributeBase.h" class SFICGraphView; @@ -61,7 +62,7 @@ class SFICKeyframeControl : public SPanel { public: SFICKeyframeControl(); - void Construct(FArguments InArgs); + void Construct(FArguments InArgs, UFICEditorContext* Context); private: TAttribute> Frame; @@ -75,8 +76,10 @@ class SFICKeyframeControl : public SPanel { bool bWasDoubleClick = false; + public: SFICGraphView* GraphView = nullptr; + UFICEditorContext* Context = nullptr; // Begin SWidget virtual int OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; diff --git a/Source/FicsItCam/UI/FICTimeline.cpp b/Source/FicsItCam/UI/FICTimeline.cpp index 646681c..cb7a775 100644 --- a/Source/FicsItCam/UI/FICTimeline.cpp +++ b/Source/FicsItCam/UI/FICTimeline.cpp @@ -364,7 +364,7 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { +SGridPanel::Slot(1, 2) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill)[ - SAssignNew(Graph, SFICGraphView) + SAssignNew(Graph, SFICGraphView, Context) .Attributes({&Context->PosX, &Context->PosY, &Context->PosZ}) .AutoFit(true) .Clipping(EWidgetClipping::ClipToBoundsAlways) diff --git a/Source/FicsItCam/UI/FICVectorEditor.cpp b/Source/FicsItCam/UI/FICVectorEditor.cpp index 621cb2b..f4dd329 100644 --- a/Source/FicsItCam/UI/FICVectorEditor.cpp +++ b/Source/FicsItCam/UI/FICVectorEditor.cpp @@ -10,7 +10,7 @@ SFICVectorEditor::SFICVectorEditor() { SpinBoxStyle.InactiveFillBrush.DrawAs = ESlateBrushDrawType::NoDrawType; } -void SFICVectorEditor::Construct(FArguments InArgs) { +void SFICVectorEditor::Construct(FArguments InArgs, UFICEditorContext* InContext) { X = InArgs._X; Y = InArgs._Y; Z = InArgs._Z; @@ -25,6 +25,7 @@ void SFICVectorEditor::Construct(FArguments InArgs) { OnYChanged = InArgs._OnYChanged; OnZChanged = InArgs._OnZChanged; AutoKeyframe = InArgs._AutoKeyframe; + Context = InContext; if (!X.IsSet() && !X.IsBound()) X = TAttribute>::Create([this]() { if (XAttr.Get()) return TOptional(static_cast*>(XAttr.Get())->GetValue()); @@ -110,7 +111,7 @@ void SFICVectorEditor::Construct(FArguments InArgs) { ]; if (InArgs._ShowKeyframeControls) { Holder->AddSlot().AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute(XAttr) .Frame(Frame) ]; @@ -142,7 +143,7 @@ void SFICVectorEditor::Construct(FArguments InArgs) { ]; if (InArgs._ShowKeyframeControls) { Holder->AddSlot().AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute(YAttr) .Frame(Frame) ]; @@ -174,7 +175,7 @@ void SFICVectorEditor::Construct(FArguments InArgs) { ]; if (InArgs._ShowKeyframeControls) { Holder->AddSlot().AutoWidth()[ - SNew(SFICKeyframeControl) + SNew(SFICKeyframeControl, Context) .Attribute(ZAttr) .Frame(Frame) ]; diff --git a/Source/FicsItCam/UI/FICVectorEditor.h b/Source/FicsItCam/UI/FICVectorEditor.h index 60a6bf3..3ee17fd 100644 --- a/Source/FicsItCam/UI/FICVectorEditor.h +++ b/Source/FicsItCam/UI/FICVectorEditor.h @@ -1,5 +1,6 @@ #pragma once +#include "FICEditorContext.h" #include "SlateBasics.h" class FFICEditorAttributeBase; @@ -29,9 +30,11 @@ class SFICVectorEditor : public SCompoundWidget { public: SFICVectorEditor(); - void Construct(FArguments InArgs); + void Construct(FArguments InArgs, UFICEditorContext* Context); private: + UFICEditorContext* Context = nullptr; + TAttribute> X; TAttribute> Y; TAttribute> Z; From 7912f0ed9e31f19e7a990812ff4b81cd670a5fb9 Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Tue, 1 Feb 2022 02:38:51 +0100 Subject: [PATCH 7/8] Version Bump & Fixes & In-Editor Player Speed --- FicsItCam.uplugin | 2 +- Source/FicsItCam/UI/FICDragDrop.cpp | 9 +-- Source/FicsItCam/UI/FICDragDrop.h | 3 +- Source/FicsItCam/UI/FICEditorContext.cpp | 5 +- Source/FicsItCam/UI/FICEditorContext.h | 4 +- Source/FicsItCam/UI/FICGraphView.cpp | 4 ++ Source/FicsItCam/UI/FICTimeline.cpp | 66 +++++++++++++++++---- Source/FicsItCam/UI/FICTimelineScrubber.cpp | 3 +- 8 files changed, 73 insertions(+), 23 deletions(-) diff --git a/FicsItCam.uplugin b/FicsItCam.uplugin index d70827b..5d60e77 100644 --- a/FicsItCam.uplugin +++ b/FicsItCam.uplugin @@ -2,7 +2,7 @@ "FileVersion": 3, "Version": 0, "VersionName": "0.2", - "SemVersion": "0.2.1", + "SemVersion": "0.2.2", "FriendlyName": "FicsIt-Cam", "Description": "This mod allows you to create beautiful camera animations in-game.", "Category": "Modding", diff --git a/Source/FicsItCam/UI/FICDragDrop.cpp b/Source/FicsItCam/UI/FICDragDrop.cpp index 5549dea..a8612e1 100644 --- a/Source/FicsItCam/UI/FICDragDrop.cpp +++ b/Source/FicsItCam/UI/FICDragDrop.cpp @@ -6,11 +6,6 @@ FFICGraphDragDrop::FFICGraphDragDrop(TSharedRef GraphView, FPoint ValueStart = GraphView->LocalToValue(LocalPos.Y); } -void FFICGraphDragDrop::Construct() { - bCreateNewWindow = true; - FDragDropOperation::Construct(); -} - void FFICGraphDragDrop::OnDragged(const FDragDropEvent& DragDropEvent) { FDragDropOperation::OnDragged(DragDropEvent); @@ -72,13 +67,13 @@ TSharedPtr FFICGraphKeyframeDragDrop::GetDefaultDecorator() const { .ColorAndOpacity( FLinearColor::Black ) .WrapTextAt_Static( &SToolTip::GetToolTipWrapWidth ) .Text_Lambda([this]() { - return FText::FromString(FString::Printf(TEXT("Frame: %lld\nValue: %f\n\nShift: Fixed Frame\nCTRL: Fixed Value"), KeyframeControl->GetFrame(), KeyframeControl->GetAttribute()->GetValueAsFloat(KeyframeControl->GetFrame()))); + return FText::FromString(FString::Printf(TEXT("Frame: %lld\nValue: %i\n\nShift: Fixed Frame\nCTRL: Fixed Value"), KeyframeControl->GetFrame(), static_cast(KeyframeControl->GetAttribute()->GetValueAsFloat(KeyframeControl->GetFrame())))); }) ]; } FVector2D FFICGraphKeyframeDragDrop::GetDecoratorPosition() const { - return FFICGraphDragDrop::GetDecoratorPosition(); + return FSlateApplication::Get().GetCursorPos() + FVector2D(20, 20); } void FFICGraphKeyframeDragDrop::OnDrop(bool bDropWasHandled, const FPointerEvent& MouseEvent) { diff --git a/Source/FicsItCam/UI/FICDragDrop.h b/Source/FicsItCam/UI/FICDragDrop.h index ceac111..29553ad 100644 --- a/Source/FicsItCam/UI/FICDragDrop.h +++ b/Source/FicsItCam/UI/FICDragDrop.h @@ -19,7 +19,6 @@ class FFICGraphDragDrop : public FDragDropOperation { FFICGraphDragDrop(TSharedRef GraphView, FPointerEvent InitEvent); // Begin FDragDropOperation - virtual void Construct() override; virtual void OnDragged( const FDragDropEvent& DragDropEvent ) override; // End FDragDropOperation }; @@ -45,6 +44,8 @@ class FFICGraphKeyframeDragDrop : public FFICGraphDragDrop { FFICGraphKeyframeDragDrop(TSharedRef GraphView, TSharedRef KeyframeControl, FPointerEvent InitEvent) : FFICGraphDragDrop(GraphView, InitEvent), KeyframeControl(KeyframeControl) { AttribBegin = KeyframeControl->GetAttribute()->GetAttribute()->Get(); + FDragDropOperation::SetDecoratorVisibility(true); + bCreateNewWindow = false; } // Begin FDragDropOperation diff --git a/Source/FicsItCam/UI/FICEditorContext.cpp b/Source/FicsItCam/UI/FICEditorContext.cpp index d2d5a6f..43aaf13 100644 --- a/Source/FicsItCam/UI/FICEditorContext.cpp +++ b/Source/FicsItCam/UI/FICEditorContext.cpp @@ -68,7 +68,8 @@ void UFICEditorContext::HideEditor() { IsEditorShown = false; } -void UFICEditorContext::SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState) { +void UFICEditorContext::SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState, float InAnimPlayerFactor) { + AnimPlayerFactor = InAnimPlayerFactor; if (AnimPlayerState != InAnimPlayerState) { AnimPlayerState = InAnimPlayerState; AnimPlayerDelta = 0; @@ -95,7 +96,7 @@ UFICEditorContext::UFICEditorContext() : void UFICEditorContext::Tick(float DeltaTime) { if (Animation) { - AnimPlayerDelta += DeltaTime; + AnimPlayerDelta += DeltaTime * AnimPlayerFactor; int32 FrameDelta = FMath::Floor(AnimPlayerDelta * Animation->FPS); AnimPlayerDelta -= (float)FrameDelta / (float)Animation->FPS; switch (AnimPlayerState) { diff --git a/Source/FicsItCam/UI/FICEditorContext.h b/Source/FicsItCam/UI/FICEditorContext.h index 733c6c3..c140bb1 100644 --- a/Source/FicsItCam/UI/FICEditorContext.h +++ b/Source/FicsItCam/UI/FICEditorContext.h @@ -47,6 +47,7 @@ class UFICEditorContext : public UObject, public FTickableGameObject { EFICAnimPlayerState AnimPlayerState = FIC_PLAY_PAUSED; float AnimPlayerDelta = 0.0f; + float AnimPlayerFactor = 1.0f; public: TFICEditorAttribute PosX; @@ -99,6 +100,7 @@ class UFICEditorContext : public UObject, public FTickableGameObject { void ShowEditor(); void HideEditor(); - void SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState); + void SetAnimPlayer(EFICAnimPlayerState InAnimPlayerState, float InAnimPlayerFactor); EFICAnimPlayerState GetAnimPlayer() { return AnimPlayerState; } + float GetAnimPlayerFactor() { return AnimPlayerFactor; } }; diff --git a/Source/FicsItCam/UI/FICGraphView.cpp b/Source/FicsItCam/UI/FICGraphView.cpp index 4c87e2f..f8bbc77 100644 --- a/Source/FicsItCam/UI/FICGraphView.cpp +++ b/Source/FicsItCam/UI/FICGraphView.cpp @@ -70,11 +70,15 @@ int32 SFICGraphView::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe int64 FrameStart = TimelineRangeBegin.Get(); int64 FrameEnd = TimelineRangeEnd.Get(); int64 Steps = 1; + int64 SafetyCounter = 0; while (FMath::Abs(FrameEnd - FrameStart) / Steps > 30) Steps *= 10; for (float x = FrameStart - FrameStart % Steps; x <= FrameEnd; x += Steps) { + if (SafetyCounter++ > 1000) break; FSlateDrawElement::MakeLines(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), {FVector2D(FrameToLocal(x), 0), FVector2D(FrameToLocal(x), AllottedGeometry.GetLocalSize().Y)}, ESlateDrawEffect::None, GridColor, true, 1); } + SafetyCounter = 0; for (float y = 0; y <= AllottedGeometry.GetLocalSize().Y; y += Distance.Y) { + if (SafetyCounter++ > 1000) break; FSlateDrawElement::MakeLines(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), {FVector2D(0, y + RenderOffset.Y), FVector2D(AllottedGeometry.GetLocalSize().X, y + RenderOffset.Y)}, ESlateDrawEffect::None, GridColor, true, 1); } FLinearColor FrameColor = FLinearColor(FColor::FromHex("666600")); diff --git a/Source/FicsItCam/UI/FICTimeline.cpp b/Source/FicsItCam/UI/FICTimeline.cpp index cb7a775..9287f9f 100644 --- a/Source/FicsItCam/UI/FICTimeline.cpp +++ b/Source/FicsItCam/UI/FICTimeline.cpp @@ -118,20 +118,43 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { .ColorAndOpacity(FColor::Black) ] .ToolTipText_Lambda([this]() { + bool Ctrl = FSlateApplication::Get().GetModifierKeys().IsControlDown() && Context->GetAnimPlayer() != FIC_PLAY_PAUSED; + float Factor = Context->GetAnimPlayerFactor(); if (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) { - return FText::FromString(TEXT("Pause Play")); + return FText::FromString(Ctrl ? FString::Printf(TEXT("Increate to %ix Reverse Play"), (int)Factor+1) : TEXT("Pause Play")); } else { - return FText::FromString(TEXT("Play Reverse")); + return FText::FromString(Ctrl ? FString::Printf(TEXT("Decrease to %ix Play"), (int)Factor-1) : TEXT("Play Reverse")); } }) .IsChecked_Lambda([this]() { return (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged_Lambda([this](ECheckBoxState State) { - if (Context->GetAnimPlayer() == FIC_PLAY_BACKWARDS) { - Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED); + bool Ctrl = FSlateApplication::Get().GetModifierKeys().IsControlDown(); + float Factor = Context->GetAnimPlayerFactor(); + switch (Context->GetAnimPlayer()) { + case FIC_PLAY_PAUSED: + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_BACKWARDS, Factor = 1); + break; + case FIC_PLAY_FORWARDS: + if (Ctrl) { + Factor -= 1; + } else { + Factor = 0; + } + break; + case FIC_PLAY_BACKWARDS: + if (Ctrl) { + Factor += 1; + } else { + Factor = 0; + } + break; + } + if (FMath::Abs(Factor) < 0.1) { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED, 0); } else { - Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_BACKWARDS); + Context->SetAnimPlayer(Context->GetAnimPlayer(), Factor); } }) ] @@ -183,20 +206,43 @@ void SFICTimelinePanel::Construct(const FArguments& InArgs) { .ColorAndOpacity(FColor::Black) ] .ToolTipText_Lambda([this]() { + bool Ctrl = FSlateApplication::Get().GetModifierKeys().IsControlDown() && Context->GetAnimPlayer() != FIC_PLAY_PAUSED; + float Factor = Context->GetAnimPlayerFactor(); if (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) { - return FText::FromString(TEXT("Pause Play")); + return FText::FromString(Ctrl ? FString::Printf(TEXT("Increate to %ix Play"), (int)Factor+1) : TEXT("Pause Play")); } else { - return FText::FromString(TEXT("Play")); + return FText::FromString(Ctrl ? FString::Printf(TEXT("Decrease to %ix Reverse Play"), (int)Factor+1) : TEXT("Play")); } }) .IsChecked_Lambda([this]() { return (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged_Lambda([this](ECheckBoxState State) { - if (Context->GetAnimPlayer() == FIC_PLAY_FORWARDS) { - Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED); + bool Ctrl = FSlateApplication::Get().GetModifierKeys().IsControlDown(); + float Factor = Context->GetAnimPlayerFactor(); + switch (Context->GetAnimPlayer()) { + case FIC_PLAY_PAUSED: + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_FORWARDS, Factor = 1); + break; + case FIC_PLAY_BACKWARDS: + if (Ctrl) { + Factor -= 1; + } else { + Factor = 0; + } + break; + case FIC_PLAY_FORWARDS: + if (Ctrl) { + Factor += 1; + } else { + Factor = 0; + } + break; + } + if (FMath::Abs(Factor) < 0.1) { + Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_PAUSED, 0); } else { - Context->SetAnimPlayer(EFICAnimPlayerState::FIC_PLAY_FORWARDS); + Context->SetAnimPlayer(Context->GetAnimPlayer(), Factor); } }) ] diff --git a/Source/FicsItCam/UI/FICTimelineScrubber.cpp b/Source/FicsItCam/UI/FICTimelineScrubber.cpp index 4c472cc..47d275b 100644 --- a/Source/FicsItCam/UI/FICTimelineScrubber.cpp +++ b/Source/FicsItCam/UI/FICTimelineScrubber.cpp @@ -36,7 +36,8 @@ int32 SFICTimelineScrubber::OnPaint(const FPaintArgs& Args, const FGeometry& All int64 End = RangeEnd.Get(); int64 Increment = 10; while ((End - Start) / Increment > 30) Increment *= 10; - Start += Increment - (Start % Increment); + Start += Increment - Start % Increment; + if (Start <= 0) Start -= Increment; FLinearColor IncColor = IncrementColor.Get(); for (int64 i = Start; i <= End; i += Increment) { float IX = RangePosToLocalPos(AllottedGeometry, i); From 6a208e8c26462c062b3f578bc2b8efc799072ed2 Mon Sep 17 00:00:00 2001 From: Panakotta00 Date: Tue, 1 Feb 2022 02:39:55 +0100 Subject: [PATCH 8/8] Undo/Redo Part 2 (idk why i forgot to add those) --- Source/FicsItCam/UI/FICChangeList.cpp | 64 ++++++++++++++++ Source/FicsItCam/UI/FICChangeList.h | 102 ++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 Source/FicsItCam/UI/FICChangeList.cpp create mode 100644 Source/FicsItCam/UI/FICChangeList.h diff --git a/Source/FicsItCam/UI/FICChangeList.cpp b/Source/FicsItCam/UI/FICChangeList.cpp new file mode 100644 index 0000000..c915abc --- /dev/null +++ b/Source/FicsItCam/UI/FICChangeList.cpp @@ -0,0 +1,64 @@ +#include "FICChangeList.h" + +#include "FICEditorContext.h" + +TArray FFICChange::ChangeStack = TArray(); + +FFICChange_ActiveFrame::FFICChange_ActiveFrame(UFICEditorContext* InEditorContext, int64 InFromFrame, int64 InToFrame) : EditorContext(InEditorContext), FromFrame(InFromFrame), ToFrame(InToFrame) { + if (ToFrame == TNumericLimits::Min()) { + ToFrame = EditorContext->GetCurrentFrame(); + } + if (FromFrame == TNumericLimits::Min()) { + TFunction)> DoChange; + DoChange = [this, &DoChange](TSharedPtr InChange) { + if (InChange) { + if (InChange->ChangeType() == "Group") { + for (TSharedPtr Change : StaticCastSharedPtr(InChange)->Changes) { + if (DoChange(Change)) return true; + } + } else if (InChange->ChangeType() == "ActiveFrame") { + FromFrame = StaticCastSharedPtr(InChange)->ToFrame; + return true; + } + } + return false; + }; + if (!DoChange(EditorContext->ChangeList.PeakChange())) { + FromFrame = EditorContext->GetAnimation()->AnimationStart; + } + } +} + +void FFICChange_ActiveFrame::RedoChange() { + EditorContext->SetCurrentFrame(ToFrame); +} + +void FFICChange_ActiveFrame::UndoChange() { + EditorContext->SetCurrentFrame(FromFrame); +} + +void FFICChangeList::PushChange(TSharedPtr InChange) { + if (Changes.Num() > ChangeIndex+1) Changes.RemoveAt(ChangeIndex+1, Changes.Num() - ChangeIndex - 1); + Changes.Push(InChange); + + if (Changes.Num() > MaxChanges) { + Changes.RemoveAt(0, Changes.Num() - MaxChanges); + } + + ChangeIndex = Changes.Num() - 1; +} + +TSharedPtr FFICChangeList::PushChange() { + if (ChangeIndex >= Changes.Num()-1) return nullptr; + return Changes[++ChangeIndex]; +} + +TSharedPtr FFICChangeList::PopChange() { + if (ChangeIndex < 0) return nullptr; + return Changes[ChangeIndex--]; +} + +TSharedPtr FFICChangeList::PeakChange() { + if (ChangeIndex < 0) return nullptr; + return Changes[ChangeIndex]; +} diff --git a/Source/FicsItCam/UI/FICChangeList.h b/Source/FicsItCam/UI/FICChangeList.h new file mode 100644 index 0000000..89155f6 --- /dev/null +++ b/Source/FicsItCam/UI/FICChangeList.h @@ -0,0 +1,102 @@ +#pragma once + +#include "FICEditorContext.h" +#include "../FICAnimation.h" +#include "../FICEditorAttributeBase.h" + +class FFICChangeList; + +typedef TPair> FChangeStackEntry; + +#define BEGIN_ATTRIB_CHANGE(Attribute) \ + FFICChange::ChangeStack.Push(FChangeStackEntry(Attribute, Attribute->Get())); + +#define END_ATTRIB_CHANGE(ChangeList) { \ + FChangeStackEntry StackEntry = FFICChange::ChangeStack.Pop(); \ + ChangeList->PushChange(MakeShared(StackEntry.Key, StackEntry.Value)); \ + } + +#define BEGIN_QUICK_ATTRIB_CHANGE(Context, Attribute, FromFrame, ToFrame) \ + auto _Change = MakeShared(); \ + _Change->PushChange(MakeShared(Context, FromFrame, ToFrame)); \ + BEGIN_ATTRIB_CHANGE(Attribute) + +#define END_QUICK_ATTRIB_CHANGE(ChangeList) \ + END_ATTRIB_CHANGE(_Change) \ + ChangeList.PushChange(_Change); + +struct FFICChange { + static TArray ChangeStack; + + virtual ~FFICChange() = default; + + virtual void RedoChange() = 0; + virtual void UndoChange() = 0; + virtual FName ChangeType() = 0; +}; + +struct FFICChange_ActiveFrame : public FFICChange { + class UFICEditorContext* EditorContext; + int64 FromFrame; + int64 ToFrame; + + FFICChange_ActiveFrame(class UFICEditorContext* InEditorContext, int64 FromFrame = TNumericLimits::Min(), int64 ToFrame = TNumericLimits::Min()); + + virtual void RedoChange() override; + virtual void UndoChange() override; + virtual FName ChangeType() override { return FName(TEXT("ActiveFrame")); } +}; + +struct FFICChange_Attribute : public FFICChange { + FFICAttribute* Attribute; + TSharedPtr FromAttribute; + TSharedPtr ToAttribute; + + FFICChange_Attribute(FFICAttribute* InAttribute, TSharedPtr InFromAttribute) : Attribute(InAttribute), FromAttribute(InFromAttribute), ToAttribute(Attribute->Get()) {} + + virtual void RedoChange() override { + Attribute->Set(ToAttribute); + } + + virtual void UndoChange() override { + Attribute->Set(FromAttribute); + } + + virtual FName ChangeType() override { + return FName(TEXT("Attribute")); + } +}; + +struct FFICChange_Group : public FFICChange { + TSet> Changes; + + virtual void RedoChange() override { + for (TSharedPtr Change : Changes) Change->RedoChange(); + } + + virtual void UndoChange() override { + for (TSharedPtr Change : Changes) Change->UndoChange(); + } + + virtual FName ChangeType() override { + return FName(TEXT("Group")); + } + + void PushChange(TSharedPtr InChange) { + Changes.Add(InChange); + } +}; + +class FFICChangeList { +private: + TArray> Changes; + int ChangeIndex = -1; + + int MaxChanges = 50; + +public: + void PushChange(TSharedPtr InChange); + TSharedPtr PushChange(); + TSharedPtr PopChange(); + TSharedPtr PeakChange(); +};