You've already forked LuckyWorld
fixed build issues, added binaries, and updated the AsyncLoadingScreen plugin directory
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
// Copyright 2023 RLoris
|
||||
// Copyright 2025 RLoris
|
||||
|
||||
#include "FileHelperBPLibrary.h"
|
||||
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
@ -9,22 +10,26 @@
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "Engine/DataTable.h"
|
||||
#include "Internationalization/Regex.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Serialization/Csv/CsvParser.h"
|
||||
#include "UObject/TextProperty.h"
|
||||
|
||||
class FCustomFileVisitor : public IPlatformFile::FDirectoryVisitor
|
||||
{
|
||||
public:
|
||||
FCustomFileVisitor(const FString& Path, TArray<FString>& Paths, const FString& Pattern, bool File, bool Directory) : BasePath(Path), Nodes(Paths), Filter(Pattern), CustomPattern(Pattern), bFile(File), bDirectory(Directory) {};
|
||||
|
||||
//~ Begin FDirectoryVisitor
|
||||
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override;
|
||||
//~ End FDirectoryVisitor
|
||||
|
||||
private:
|
||||
FString BasePath;
|
||||
TArray<FString>& Nodes;
|
||||
FString Filter;
|
||||
FRegexPattern CustomPattern;
|
||||
bool bFile = true;
|
||||
bool bDirectory = true;
|
||||
|
||||
FCustomFileVisitor(FString& Path, TArray<FString>& Paths, const FString& Pattern, bool File, bool Directory) : BasePath(Path), Nodes(Paths), Filter(Pattern), CustomPattern(Pattern), bFile(File), bDirectory(Directory) {};
|
||||
|
||||
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory);
|
||||
};
|
||||
|
||||
FEnginePath UFileHelperBPLibrary::GetEngineDirectories()
|
||||
@ -38,7 +43,11 @@ FEnginePath UFileHelperBPLibrary::GetEngineDirectories()
|
||||
P.Saved =FPaths::ConvertRelativePathToFull( FPaths::EngineSavedDir());
|
||||
P.User = FPaths::ConvertRelativePathToFull(FPaths::EngineUserDir());
|
||||
P.DefaultLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineDefaultLayoutDir());
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
|
||||
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::EnginePlatformExtensionDir(TEXT("")).TrimChar('/'));
|
||||
#else
|
||||
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::EnginePlatformExtensionsDir());
|
||||
#endif
|
||||
P.UserLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineUserLayoutDir());
|
||||
return P;
|
||||
}
|
||||
@ -56,14 +65,18 @@ FProjectPath UFileHelperBPLibrary::GetProjectDirectories()
|
||||
P.Saved = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
|
||||
P.User = FPaths::ConvertRelativePathToFull(FPaths::ProjectUserDir());
|
||||
P.PersistentDownload = FPaths::ConvertRelativePathToFull(FPaths::ProjectPersistentDownloadDir());
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
|
||||
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::ProjectPlatformExtensionDir(TEXT("")).TrimChar('/'));
|
||||
#else
|
||||
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::ProjectPlatformExtensionsDir());
|
||||
#endif
|
||||
return P;
|
||||
}
|
||||
|
||||
bool UFileHelperBPLibrary::ReadText(FString Path, FString& Output)
|
||||
{
|
||||
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (FileManager.FileExists(*Path))
|
||||
if (FileManager.FileExists(*Path))
|
||||
{
|
||||
return FFileHelper::LoadFileToString(Output, *Path);
|
||||
}
|
||||
@ -369,10 +382,10 @@ bool FCustomFileVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirecto
|
||||
{
|
||||
FString RelativePath = FString(FilenameOrDirectory);
|
||||
FPaths::MakePathRelativeTo(RelativePath, *BasePath);
|
||||
if (!Filter.IsEmpty())
|
||||
if (!Filter.IsEmpty())
|
||||
{
|
||||
FRegexMatcher CustomMatcher(CustomPattern, RelativePath);
|
||||
if (CustomMatcher.FindNext())
|
||||
if (CustomMatcher.FindNext())
|
||||
{
|
||||
Nodes.Add(RelativePath);
|
||||
}
|
||||
@ -388,21 +401,21 @@ bool FCustomFileVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirecto
|
||||
bool UFileHelperBPLibrary::ListDirectory(FString Path, FString Pattern, TArray<FString>& Nodes, bool ShowFile, bool ShowDirectory, bool Recursive)
|
||||
{
|
||||
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (!FileManager.DirectoryExists(*Path))
|
||||
if (!FileManager.DirectoryExists(*Path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!ShowDirectory && !ShowFile)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FString BasePath = FPaths::Combine(Path, TEXT("/"));
|
||||
FCustomFileVisitor CustomFileVisitor(BasePath, Nodes, Pattern, ShowFile, ShowDirectory);
|
||||
if (Recursive)
|
||||
{
|
||||
return FileManager.IterateDirectoryRecursively(*Path, CustomFileVisitor);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
return FileManager.IterateDirectory(*Path, CustomFileVisitor);
|
||||
}
|
||||
@ -614,14 +627,14 @@ bool UFileHelperBPLibrary::DataTableToJSON(UDataTable* Table, FString& Output)
|
||||
UDataTable* UFileHelperBPLibrary::CSVToDataTable(FString CSV, UScriptStruct* Struct, bool& Success)
|
||||
{
|
||||
Success = false;
|
||||
if (Struct == nullptr)
|
||||
if (Struct == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
UDataTable* DataTable = NewObject<UDataTable>();
|
||||
DataTable->RowStruct = Struct;
|
||||
auto Result = DataTable->CreateTableFromCSVString(CSV);
|
||||
if (Result.Num() == 0)
|
||||
if (Result.Num() == 0)
|
||||
{
|
||||
Success = true;
|
||||
}
|
||||
@ -645,7 +658,7 @@ UDataTable* UFileHelperBPLibrary::JSONToDataTable(FString JSON, UScriptStruct* S
|
||||
return DataTable;
|
||||
}
|
||||
|
||||
void UFileHelperBPLibrary::ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue, bool bLoadFromDisk)
|
||||
void UFileHelperBPLibrary::ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue)
|
||||
{
|
||||
checkNoEntry();
|
||||
}
|
||||
@ -673,13 +686,17 @@ TArray<FString> UFileHelperBPLibrary::SplitString(FString String, FString Separa
|
||||
return Array;
|
||||
}
|
||||
|
||||
bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bWriteToDisk)
|
||||
bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray)
|
||||
{
|
||||
if (!GConfig)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
|
||||
|
||||
FConfigFile ConfigFile;
|
||||
FindOrCreateConfigFile(Filename, ConfigFile);
|
||||
|
||||
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
|
||||
{
|
||||
GConfig->SetBool(*Section, *Key, *(static_cast<bool*>(Value)), Filename);
|
||||
}
|
||||
@ -768,33 +785,19 @@ bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bWriteToDisk)
|
||||
{
|
||||
GConfig->Flush(false, Filename);
|
||||
}
|
||||
|
||||
return true;
|
||||
return SaveConfigFile(Filename);
|
||||
}
|
||||
|
||||
bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bLoadFromDisk)
|
||||
bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray)
|
||||
{
|
||||
if (!GConfig)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bLoadFromDisk)
|
||||
{
|
||||
FConfigFile* ConfigFile = GConfig->FindConfigFile(Filename);
|
||||
|
||||
if (!ConfigFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigFile->Read(Filename);
|
||||
}
|
||||
|
||||
FConfigFile ConfigFile;
|
||||
FindOrCreateConfigFile(Filename, ConfigFile);
|
||||
|
||||
bool Success = false;
|
||||
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
|
||||
{
|
||||
@ -868,32 +871,83 @@ bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FSt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
void UFileHelperBPLibrary::WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value, bool bWriteToDisk)
|
||||
void UFileHelperBPLibrary::FindOrCreateConfigFile(const FString& InFilePath, FConfigFile& OutConfigFile)
|
||||
{
|
||||
const bool bDisabled = GConfig->AreFileOperationsDisabled();
|
||||
|
||||
if (bDisabled)
|
||||
{
|
||||
GConfig->EnableFileOperations();
|
||||
}
|
||||
|
||||
FConfigFile* ConfigFile = GConfig->Find(InFilePath);
|
||||
|
||||
if (!ConfigFile)
|
||||
{
|
||||
FConfigFile NewConfigFile;
|
||||
NewConfigFile.Read(InFilePath);
|
||||
OutConfigFile = GConfig->Add(InFilePath, NewConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutConfigFile = *ConfigFile;
|
||||
}
|
||||
|
||||
if (bDisabled)
|
||||
{
|
||||
GConfig->DisableFileOperations();
|
||||
}
|
||||
}
|
||||
|
||||
bool UFileHelperBPLibrary::SaveConfigFile(const FString& InFilePath)
|
||||
{
|
||||
if (FConfigFile* ConfigFile = GConfig->Find(InFilePath))
|
||||
{
|
||||
const bool bDisabled = GConfig->AreFileOperationsDisabled();
|
||||
|
||||
if (bDisabled)
|
||||
{
|
||||
GConfig->EnableFileOperations();
|
||||
}
|
||||
|
||||
GConfig->Flush(false, InFilePath);
|
||||
|
||||
if (bDisabled)
|
||||
{
|
||||
GConfig->DisableFileOperations();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UFileHelperBPLibrary::WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value)
|
||||
{
|
||||
checkNoEntry();
|
||||
}
|
||||
|
||||
bool UFileHelperBPLibrary::RemoveConfig(FString FilePath, FString Section, FString Key, bool bWriteToDisk)
|
||||
bool UFileHelperBPLibrary::RemoveConfig(FString FilePath, FString Section, FString Key)
|
||||
{
|
||||
if (!GConfig)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
FConfigFile ConfigFile;
|
||||
FindOrCreateConfigFile(FilePath, ConfigFile);
|
||||
|
||||
if (!GConfig->RemoveKey(*Section, *Key, *FilePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bWriteToDisk)
|
||||
{
|
||||
GConfig->Flush(false, FilePath);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
return SaveConfigFile(FilePath);
|
||||
}
|
||||
|
||||
// equivalent GetTableAsCSV()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 RLoris
|
||||
// Copyright 2025 RLoris
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user