fixed build issues, added binaries, and updated the AsyncLoadingScreen plugin directory

This commit is contained in:
Devrim Yasar
2025-04-14 13:03:05 -05:00
parent b91d8c9412
commit cc50ba0c39
2200 changed files with 910483 additions and 318 deletions

View File

@ -1,25 +1,33 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#include "FileHelperScreenshotAction.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "ImageUtils.h"
#include "Kismet/GameplayStatics.h"
#include "TextureResource.h"
#include "UnrealClient.h"
#include "Async/Async.h"
#include "Camera/CameraActor.h"
#include "Camera/CameraComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/Engine.h"
#include "Framework/Application/SlateApplication.h"
#include "HAL/PlatformFileManager.h"
#include "IImageWrapper.h"
#include "ImageUtils.h"
#include "ImageWriteQueue.h"
#include "ImageWriteTask.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/Paths.h"
#include "Misc/FileHelper.h"
#include "Modules/ModuleManager.h"
#include "TextureResource.h"
#include "UnrealClient.h"
#include "Widgets/SViewport.h"
UFileHelperScreenshotAction* UFileHelperScreenshotAction::TakeScreenshot(UObject* InWorldContextObject, const FFileHelperScreenshotActionOptions& InOptions)
UFileHelperScreenshotAction* UFileHelperScreenshotAction::TakeScreenshot(const FFileHelperScreenshotActionOptions& InOptions)
{
UFileHelperScreenshotAction* Node = NewObject<UFileHelperScreenshotAction>();
Node->Options = InOptions;
Node->Options.Filename = FPaths::GetBaseFilename(Node->Options.Filename);
Node->Options.Filename = FPaths::GetBaseFilename(Node->Options.Filename, /** Remove path */true);
Node->bActive = false;
Node->WorldContextObject = InWorldContextObject;
// not garbage collected
Node->AddToRoot();
return Node;
}
@ -37,13 +45,8 @@ void UFileHelperScreenshotAction::Activate()
return;
}
if (!WorldContextObject || !WorldContextObject->GetWorld() || !WorldContextObject->GetWorld()->GetGameViewport())
{
FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject. Cannot execute ScreenshotUtility"), ELogVerbosity::Error);
OnTaskFailed();
return;
}
Reset();
FText ErrorFilename;
if (!FFileHelper::IsFilenameValidForSaving(Options.Filename, ErrorFilename))
{
@ -51,44 +54,38 @@ void UFileHelperScreenshotAction::Activate()
OnTaskFailed();
return;
}
const FString FinalFilename = (Options.bPrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + Options.Filename;
if (!FScreenshotRequest::IsScreenshotRequested())
{
bActive = true;
ScreenshotTexture = nullptr;
if (!Options.CustomCameraActor)
{
constexpr bool bAddFilenameSuffix = false;
const FViewport* Viewport = WorldContextObject->GetWorld()->GetGameViewport()->Viewport;
const bool bHDREnabled = Viewport->GetSceneHDREnabled();
FScreenshotRequest::Reset();
FScreenshotRequest::RequestScreenshot(FinalFilename, Options.bShowUI, bAddFilenameSuffix, bHDREnabled && Options.bWithHDR);
FilePath = FScreenshotRequest::GetFilename();
FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UFileHelperScreenshotAction::OnTaskCompleted);
bActive = true;
ScreenshotTexture = nullptr;
const FString FinalFilename = (Options.bPrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + Options.Filename;
FilePath = FPaths::Combine(Options.DirectoryPath, FinalFilename);
if (!Options.CustomCameraActor)
{
if (Options.bShowUI)
{
CreateViewportScreenshot();
}
else
{
FilePath = FPaths::ScreenShotDir() + FinalFilename + TEXT(".png");
CreateCustomCameraScreenshot();
CreatePlayerPOVScreenshot();
}
}
else
{
CreateCustomCameraScreenshot();
}
}
void UFileHelperScreenshotAction::OnTaskCompleted()
{
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
if (!ScreenshotTexture && FileManager.FileExists(*FilePath))
if (!ScreenshotTexture && FileManager.FileExists(*FilePath))
{
ScreenshotTexture = FImageUtils::ImportFileAsTexture2D(FilePath);
}
if (ScreenshotTexture)
{
Completed.Broadcast(ScreenshotTexture, FilePath);
@ -97,7 +94,7 @@ void UFileHelperScreenshotAction::OnTaskCompleted()
{
OnTaskFailed();
}
Reset();
}
@ -109,15 +106,15 @@ void UFileHelperScreenshotAction::OnTaskFailed()
void UFileHelperScreenshotAction::CreateCustomCameraScreenshot()
{
UWorld* World = WorldContextObject->GetWorld();
if (!World || !World->GetGameViewport() || !World->GetGameViewport()->Viewport)
const ACameraActor* Camera = Options.CustomCameraActor;
if (!Camera)
{
OnTaskFailed();
return;
}
const ACameraActor* Camera = Options.CustomCameraActor;
if (!Camera)
UWorld* World = Camera->GetWorld();
if (!World)
{
OnTaskFailed();
return;
@ -129,74 +126,316 @@ void UFileHelperScreenshotAction::CreateCustomCameraScreenshot()
OnTaskFailed();
return;
}
/*const APlayerCameraManager* PlayerCamera = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0);
if (!PlayerCamera)
{
OnTaskFailed();
return;
}*/
const FViewport* GameViewport = World->GetGameViewport()->Viewport;
const FIntRect ViewRect(0, 0, GameViewport->GetSizeXY().X, GameViewport->GetSizeXY().Y);
const FVector CameraLocation = Camera->GetActorLocation();
const FRotator CameraRotation = Camera->GetActorRotation();
const float CameraFOV = CameraComponent->FieldOfView;
const ECameraProjectionMode::Type CameraProjectionMode = CameraComponent->ProjectionMode;
const float CameraOrthoWidth = CameraComponent->OrthoWidth;
CreateRenderTargetScreenshot(GWorld, CameraFOV, CameraProjectionMode, CameraOrthoWidth, CameraLocation, CameraRotation, ViewRect.Width(), ViewRect.Height());
}
void UFileHelperScreenshotAction::CreateViewportScreenshot()
{
if (!GEngine || !GEngine->GameViewport || !GEngine->GameViewport->Viewport)
{
OnTaskFailed();
return;
}
FViewport* Viewport = GEngine->GameViewport->Viewport;
// Get viewport size
const FIntPoint ViewportSize = Viewport->GetSizeXY();
const int32 Width = ViewportSize.X;
const int32 Height = ViewportSize.Y;
TWeakObjectPtr<UFileHelperScreenshotAction> ThisWeak(this);
auto ThenTask = [ThisWeak](const TFuture<bool>& InFuture)
{
bool bResult = InFuture.Get();
AsyncTask(ENamedThreads::Type::GameThread, [ThisWeak, bResult]()
{
UFileHelperScreenshotAction* This = ThisWeak.Get();
if (!This)
{
return;
}
if (!bResult)
{
This->OnTaskFailed();
return;
}
UTexture2D* Texture = FImageUtils::ImportFileAsTexture2D(This->FilePath);
if (!Texture)
{
This->OnTaskFailed();
return;
}
This->ScreenshotTexture = Texture;
This->OnTaskCompleted();
});
};
if (Options.bWithHDR && Viewport->IsHDRViewport())
{
TArray<FLinearColor> OutPixels;
OutPixels.Reserve(Width * Height);
if (!Viewport->ReadLinearColorPixels(OutPixels, FReadSurfaceDataFlags(), FIntRect(0, 0, Width, Height)) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
// If UI is enabled, capture it separately and blend it
const TSharedPtr<SViewport> ViewportWidget = GEngine->GameViewport->GetGameViewportWidget();
if (Options.bShowUI && ViewportWidget.IsValid())
{
FIntVector OutSize;
if (!FSlateApplication::Get().TakeHDRScreenshot(ViewportWidget.ToSharedRef(), OutPixels, OutSize))
{
OnTaskFailed();
return;
}
}
FilePath += TEXT(".exr");
WriteLinearColorBufferToDiskAsync(MoveTemp(OutPixels), Width, Height)
.Then(ThenTask);
}
else
{
TArray<FColor> OutPixels;
OutPixels.Reserve(Width * Height);
if (!Viewport->ReadPixels(OutPixels, FReadSurfaceDataFlags(), FIntRect(0, 0, Width, Height)) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
// If UI is enabled, capture it separately and blend it
const TSharedPtr<SViewport> ViewportWidget = GEngine->GameViewport->GetGameViewportWidget();
if (Options.bShowUI && ViewportWidget.IsValid())
{
FIntVector OutSize;
if (!FSlateApplication::Get().TakeScreenshot(ViewportWidget.ToSharedRef(), OutPixels, OutSize))
{
OnTaskFailed();
return;
}
}
for (FColor& Color : OutPixels)
{
Color.A = 255;
}
FilePath += TEXT(".png");
WriteColorBufferToDiskAsync(MoveTemp(OutPixels), Width, Height)
.Then(ThenTask);
}
}
void UFileHelperScreenshotAction::CreatePlayerPOVScreenshot()
{
if (!GEngine || !GEngine->GameViewport || !GEngine->GameViewport->Viewport)
{
OnTaskFailed();
return;
}
const APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GWorld, 0);
if (!PlayerController || !PlayerController->PlayerCameraManager)
{
OnTaskFailed();
return;
}
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);
FVector CameraLocation;
FRotator CameraRotation;
PlayerController->GetPlayerViewPoint(CameraLocation, CameraRotation);
const float FOV = PlayerController->PlayerCameraManager->GetFOVAngle();
constexpr ECameraProjectionMode::Type ProjectionMode = ECameraProjectionMode::Perspective;
CreateRenderTargetScreenshot(GWorld, FOV, ProjectionMode, 0.f, CameraLocation, CameraRotation, ViewportSize.X, ViewportSize.Y);
}
void UFileHelperScreenshotAction::CreateRenderTargetScreenshot(UWorld* InWorld, float InFOV, ECameraProjectionMode::Type InProjectionMode, float InOrthoWidth, const FVector& InLocation, const FRotator& InRotation, int32 InWidth, int32 InHeight)
{
USceneCaptureComponent2D* SceneComponent = NewObject<USceneCaptureComponent2D>(this, TEXT("SceneComponent"));
SceneComponent->RegisterComponentWithWorld(World);
SceneComponent->RegisterComponentWithWorld(InWorld);
SceneComponent->bCaptureEveryFrame = false;
SceneComponent->bCaptureOnMovement = false;
SceneComponent->bAlwaysPersistRenderingState = true;
SceneComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
SceneComponent->FOVAngle = CameraComponent->FieldOfView;
SceneComponent->ProjectionType = CameraComponent->ProjectionMode;
SceneComponent->OrthoWidth = CameraComponent->OrthoWidth;
SceneComponent->SetWorldLocationAndRotation(CameraLocation, CameraRotation);
SceneComponent->FOVAngle = InFOV;
SceneComponent->ProjectionType = InProjectionMode;
SceneComponent->OrthoWidth = InOrthoWidth;
SceneComponent->SetWorldLocationAndRotation(InLocation, InRotation);
UTextureRenderTarget2D* TextureRenderTarget = NewObject<UTextureRenderTarget2D>();
TextureRenderTarget->InitCustomFormat(ViewRect.Width(),ViewRect.Height(),PF_B8G8R8A8,false);
TextureRenderTarget->InitCustomFormat(InWidth,InHeight,PF_B8G8R8A8,false);
TextureRenderTarget->UpdateResourceImmediate();
SceneComponent->TextureTarget = TextureRenderTarget;
SceneComponent->CaptureScene();
TArray<FColor> OutColors;
OutColors.Reserve(ViewRect.Width() * ViewRect.Height());
TextureRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels(OutColors);
OutColors.Shrink();
SceneComponent->UnregisterComponent();
if (OutColors.Num() == 0)
SceneComponent->MarkAsGarbage();
FTextureRenderTargetResource* RenderTargetResource = TextureRenderTarget->GameThread_GetRenderTargetResource();
if (!RenderTargetResource)
{
OnTaskFailed();
return;
}
TWeakObjectPtr<UFileHelperScreenshotAction> ThisWeak(this);
auto ThenTask = [ThisWeak](const TFuture<bool>& InFuture)
{
bool bResult = InFuture.Get();
AsyncTask(ENamedThreads::Type::GameThread, [ThisWeak, bResult]()
{
UFileHelperScreenshotAction* This = ThisWeak.Get();
if (!This)
{
return;
}
if (!bResult)
{
This->OnTaskFailed();
return;
}
UTexture2D* Texture = FImageUtils::ImportFileAsTexture2D(This->FilePath);
if (!Texture)
{
This->OnTaskFailed();
return;
}
This->ScreenshotTexture = Texture;
This->OnTaskCompleted();
});
};
if (Options.bWithHDR && RenderTargetResource->GetSceneHDREnabled())
{
TArray<FLinearColor> OutPixels;
OutPixels.Reserve(InWidth * InHeight);
if (!RenderTargetResource->ReadLinearColorPixels(OutPixels) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
FilePath += TEXT(".exr");
WriteLinearColorBufferToDiskAsync(MoveTemp(OutPixels), InWidth, InHeight)
.Then(ThenTask);
}
else
{
TArray<FColor> OutPixels;
OutPixels.Reserve(InWidth * InHeight);
if (!RenderTargetResource->ReadPixels(OutPixels) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
for (FColor& Color : OutPixels)
{
Color.A = 255;
}
FilePath += TEXT(".png");
WriteColorBufferToDiskAsync(MoveTemp(OutPixels), InWidth, InHeight)
.Then(ThenTask);
}
TextureRenderTarget->MarkAsGarbage();
}
bool UFileHelperScreenshotAction::WriteColorBufferToDisk(const TArray<FColor>& InBuffer, int32 InWidth, int32 InHeight, UTexture2D*& OutTexture) const
{
if (InBuffer.IsEmpty())
{
return false;
}
TArray<uint8> OutImage;
FImageUtils::ThumbnailCompressImageArray(ViewRect.Width(), ViewRect.Height(), OutColors, OutImage);
if (OutImage.Num() == 0)
FImageUtils::ThumbnailCompressImageArray(InWidth, InHeight, InBuffer, OutImage);
if (OutImage.IsEmpty())
{
OnTaskFailed();
return;
return false;
}
if (!FFileHelper::SaveArrayToFile(OutImage, *FilePath))
{
OnTaskFailed();
return;
return false;
}
OutTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
return IsValid(OutTexture);
}
TFuture<bool> UFileHelperScreenshotAction::WriteLinearColorBufferToDiskAsync(TArray<FLinearColor>&& InBuffer, int32 InWidth, int32 InHeight)
{
IImageWriteQueueModule& ImageWriteQueueModule = FModuleManager::LoadModuleChecked<IImageWriteQueueModule>("ImageWriteQueue");
TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
ImageTask->Format = EImageFormat::EXR;
ImageTask->PixelData = MakeUnique<TImagePixelData<FLinearColor>>(FIntPoint(InWidth, InHeight), TArray64<FLinearColor>(InBuffer));
ImageTask->Filename = FilePath;
ImageTask->bOverwriteFile = true;
ImageTask->CompressionQuality = 100;
return ImageWriteQueueModule.GetWriteQueue().Enqueue(MoveTemp(ImageTask));
}
TFuture<bool> UFileHelperScreenshotAction::WriteColorBufferToDiskAsync(TArray<FColor>&& InBuffer, int32 InWidth, int32 InHeight)
{
IImageWriteQueueModule& ImageWriteQueueModule = FModuleManager::LoadModuleChecked<IImageWriteQueueModule>("ImageWriteQueue");
TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
ImageTask->Format = EImageFormat::PNG;
ImageTask->PixelData = MakeUnique<TImagePixelData<FColor>>(FIntPoint(InWidth, InHeight), TArray64<FColor>(InBuffer));
ImageTask->Filename = FilePath;
ImageTask->bOverwriteFile = true;
ImageTask->CompressionQuality = 100;
return ImageWriteQueueModule.GetWriteQueue().Enqueue(MoveTemp(ImageTask));
}
void UFileHelperScreenshotAction::ConvertLinearColorToColorBuffer(const TArray<FLinearColor>& InSourceBuffer, TArray<FColor>& OutDestBuffer)
{
OutDestBuffer.Empty(InSourceBuffer.Num());
for (const FLinearColor& LinearColor : InSourceBuffer)
{
OutDestBuffer.Emplace(LinearColor.ToFColor(false)); // Apply gamma correction
}
ScreenshotTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
OnTaskCompleted();
}
void UFileHelperScreenshotAction::Reset()
{
WorldContextObject = nullptr;
ScreenshotTexture = nullptr;
bActive = false;
FilePath.Empty();
FScreenshotRequest::Reset();
FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
RemoveFromRoot();
}