Files
UnrealEngine/Engine/Plugins/Animation/GameplayInsights/Source/RewindDebugger/Private/RewindDebuggerCamera.cpp
2025-05-18 13:04:45 +08:00

198 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RewindDebuggerCamera.h"
#include "Camera/CameraActor.h"
#include "Camera/CameraComponent.h"
#include "Editor.h"
#include "IGameplayProvider.h"
#include "Insights/IUnrealInsightsModule.h"
#include "IRewindDebugger.h"
#include "LevelEditor.h"
#include "RewindDebuggerModule.h"
#include "Modules/ModuleManager.h"
#include "SLevelViewport.h"
#include "ToolMenu.h"
#include "ToolMenus.h"
#include "TraceServices/Model/Frames.h"
#define LOCTEXT_NAMESPACE "RewindDebuggerCamera"
FRewindDebuggerCamera::FRewindDebuggerCamera()
: bLastPositionValid(false)
, LastPosition()
{
}
void FRewindDebuggerCamera::Initialize()
{
UToolMenu* Menu = UToolMenus::Get()->FindMenu(FRewindDebuggerModule::MainMenuName);
Menu->AddSection("Camera Mode", LOCTEXT("Camera Mode","Camera Mode"));
Menu->AddMenuEntry("Camera Mode",
FToolMenuEntry::InitMenuEntry("CameraModeDisabled",
LOCTEXT("Camera Mode Disabled", "Disabled"),
FText(),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw(this, &FRewindDebuggerCamera::SetCameraMode, ERewindDebuggerCameraMode::Disabled),
FCanExecuteAction(),
FGetActionCheckState::CreateLambda([this] { return CameraMode() == ERewindDebuggerCameraMode::Disabled ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } )),
EUserInterfaceActionType::Check
)
);
Menu->AddMenuEntry("Camera Mode",
FToolMenuEntry::InitMenuEntry("CameraModeFollow",
LOCTEXT("Camera Mode Follow", "Follow Target Actor"),
FText(),
FSlateIcon(),
FUIAction( FExecuteAction::CreateRaw(this, &FRewindDebuggerCamera::SetCameraMode, ERewindDebuggerCameraMode::FollowTargetActor),
FCanExecuteAction(),
FGetActionCheckState::CreateLambda([this] { return CameraMode() == ERewindDebuggerCameraMode::FollowTargetActor ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } )),
EUserInterfaceActionType::Check
)
);
Menu->AddMenuEntry("Camera Mode",
FToolMenuEntry::InitMenuEntry("CameraModeReplay",
LOCTEXT("Camera Mode Recorded", "Replay Recorded Camera"),
FText(),
FSlateIcon(),
FUIAction( FExecuteAction::CreateRaw(this, &FRewindDebuggerCamera::SetCameraMode, ERewindDebuggerCameraMode::Replay),
FCanExecuteAction(),
FGetActionCheckState::CreateLambda([this] { return CameraMode() == ERewindDebuggerCameraMode::Replay ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } )),
EUserInterfaceActionType::Check
)
);
}
ERewindDebuggerCameraMode FRewindDebuggerCamera::CameraMode() const
{
return URewindDebuggerSettings::Get().CameraMode;
}
void FRewindDebuggerCamera::SetCameraMode(ERewindDebuggerCameraMode InMode)
{
FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
SLevelViewport* LevelViewport = LevelEditor.GetFirstActiveLevelViewport().Get();
FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient();
URewindDebuggerSettings& RewindDebuggerSettings = URewindDebuggerSettings::Get();
if (RewindDebuggerSettings.CameraMode == ERewindDebuggerCameraMode::Replay && InMode != ERewindDebuggerCameraMode::Replay)
{
LevelViewportClient.SetActorLock(nullptr);
}
else if (RewindDebuggerSettings.CameraMode == ERewindDebuggerCameraMode::Replay)
{
if (CameraActor.IsValid())
{
LevelViewportClient.SetActorLock(CameraActor.Get());
}
}
RewindDebuggerSettings.CameraMode = InMode;
RewindDebuggerSettings.Modify();
RewindDebuggerSettings.SaveConfig();
}
void FRewindDebuggerCamera::Update(float DeltaTime, IRewindDebugger* RewindDebugger)
{
if (RewindDebugger->IsPIESimulating() || RewindDebugger->GetRecordingDuration() == 0.0)
{
bLastPositionValid = false;
return;
}
if (const TraceServices::IAnalysisSession* Session = RewindDebugger->GetAnalysisSession())
{
TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session);
double CurrentTraceTime = RewindDebugger->CurrentTraceTime();
static double LastCameraScrubTime = 0.0f;
if (CurrentTraceTime != LastCameraScrubTime)
{
bool bCameraTraceDataFound = false;
FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
if (SLevelViewport* LevelViewport = LevelEditor.GetFirstActiveLevelViewport().Get())
{
FLevelEditorViewportClient& LevelViewportClient = LevelViewport->GetLevelViewportClient();
FVector TargetActorPosition;
bool bTargetActorPositionValid = RewindDebugger->GetTargetActorPosition(TargetActorPosition);
if (CameraMode() == ERewindDebuggerCameraMode::FollowTargetActor)
{
// Follow Actor mode: apply position changes from the target actor to the camera
if (bTargetActorPositionValid)
{
if(bLastPositionValid)
{
LevelViewportClient.SetViewLocation(LevelViewportClient.GetViewLocation() + TargetActorPosition - LastPosition);
}
}
}
// always update the camera actor to the replay values even if it isn't locked
if (const IGameplayProvider* GameplayProvider = Session->ReadProvider<IGameplayProvider>("GameplayProvider"))
{
GameplayProvider->ReadViewTimeline([&bCameraTraceDataFound, this, RewindDebugger, CurrentTraceTime, Session](const IGameplayProvider::ViewTimeline& TimelineData)
{
const TraceServices::IFrameProvider& FrameProvider = TraceServices::ReadFrameProvider(*Session);
TraceServices::FFrame Frame;
if(FrameProvider.GetFrameFromTime(ETraceFrameType::TraceFrameType_Game, CurrentTraceTime, Frame))
{
TimelineData.EnumerateEvents(Frame.StartTime, Frame.EndTime,
[&bCameraTraceDataFound, this, RewindDebugger](double InStartTime, double InEndTime, uint32 InDepth, const FViewMessage& ViewMessage)
{
if (!CameraActor.IsValid())
{
FActorSpawnParameters SpawnParameters;
SpawnParameters.ObjectFlags |= RF_Transient;
if (UWorld* World = RewindDebugger->GetWorldToVisualize())
{
CameraActor = World->SpawnActor<ACameraActor>(ViewMessage.Position, ViewMessage.Rotation, SpawnParameters);
CameraActor->SetActorLabel("RewindDebuggerCamera");
}
}
if (CameraActor.IsValid())
{
UCameraComponent* Camera = CameraActor->GetCameraComponent();
Camera->SetWorldLocationAndRotation(ViewMessage.Position, ViewMessage.Rotation);
Camera->SetFieldOfView(ViewMessage.Fov);
Camera->SetAspectRatio(ViewMessage.AspectRatio);
}
bCameraTraceDataFound = true;
return TraceServices::EEventEnumerate::Stop;
});
}
});
}
if (CameraMode() == ERewindDebuggerCameraMode::Replay)
{
if (CameraActor.IsValid())
{
LevelViewportClient.SetActorLock(CameraActor.Get());
}
}
LastPosition = TargetActorPosition;
bLastPositionValid = bTargetActorPositionValid;
if (bCameraTraceDataFound) // don't update this if there was no trace data found, because when first pausing, it can take a few frames for latest data to get processed
{
// only update camera in playback or scrubbing when the time has changed (allow free movement when paused)
LastCameraScrubTime = CurrentTraceTime;
}
}
}
}
}
#undef LOCTEXT_NAMESPACE