FileHelperScreenshotAction.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright 2023 RLoris
  2. #include "FileHelperScreenshotAction.h"
  3. #include "Components/SceneCaptureComponent2D.h"
  4. #include "Engine/TextureRenderTarget2D.h"
  5. #include "ImageUtils.h"
  6. #include "Kismet/GameplayStatics.h"
  7. #include "TextureResource.h"
  8. #include "UnrealClient.h"
  9. #include "Camera/CameraActor.h"
  10. #include "Camera/CameraComponent.h"
  11. UFileHelperScreenshotAction* UFileHelperScreenshotAction::TakeScreenshot(UObject* InWorldContextObject, const FFileHelperScreenshotActionOptions& InOptions)
  12. {
  13. UFileHelperScreenshotAction* Node = NewObject<UFileHelperScreenshotAction>();
  14. Node->Options = InOptions;
  15. Node->Options.Filename = FPaths::GetBaseFilename(Node->Options.Filename);
  16. Node->bActive = false;
  17. Node->WorldContextObject = InWorldContextObject;
  18. // not garbage collected
  19. Node->AddToRoot();
  20. return Node;
  21. }
  22. UTexture2D* UFileHelperScreenshotAction::LoadScreenshot(const FString& InFilePath)
  23. {
  24. return FImageUtils::ImportFileAsTexture2D(InFilePath);
  25. }
  26. void UFileHelperScreenshotAction::Activate()
  27. {
  28. if (bActive)
  29. {
  30. FFrame::KismetExecutionMessage(TEXT("ScreenshotUtility is already running"), ELogVerbosity::Warning);
  31. OnTaskFailed();
  32. return;
  33. }
  34. if (!WorldContextObject || !WorldContextObject->GetWorld() || !WorldContextObject->GetWorld()->GetGameViewport())
  35. {
  36. FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject. Cannot execute ScreenshotUtility"), ELogVerbosity::Error);
  37. OnTaskFailed();
  38. return;
  39. }
  40. FText ErrorFilename;
  41. if (!FFileHelper::IsFilenameValidForSaving(Options.Filename, ErrorFilename))
  42. {
  43. FFrame::KismetExecutionMessage(TEXT("Filename is not valid"), ELogVerbosity::Warning);
  44. OnTaskFailed();
  45. return;
  46. }
  47. const FString FinalFilename = (Options.bPrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + Options.Filename;
  48. if (!FScreenshotRequest::IsScreenshotRequested())
  49. {
  50. bActive = true;
  51. ScreenshotTexture = nullptr;
  52. if (!Options.CustomCameraActor)
  53. {
  54. constexpr bool bAddFilenameSuffix = false;
  55. const FViewport* Viewport = WorldContextObject->GetWorld()->GetGameViewport()->Viewport;
  56. const bool bHDREnabled = Viewport->GetSceneHDREnabled();
  57. FScreenshotRequest::Reset();
  58. FScreenshotRequest::RequestScreenshot(FinalFilename, Options.bShowUI, bAddFilenameSuffix, bHDREnabled && Options.bWithHDR);
  59. FilePath = FScreenshotRequest::GetFilename();
  60. FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
  61. FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UFileHelperScreenshotAction::OnTaskCompleted);
  62. }
  63. else
  64. {
  65. FilePath = FPaths::ScreenShotDir() + FinalFilename + TEXT(".png");
  66. CreateCustomCameraScreenshot();
  67. }
  68. }
  69. }
  70. void UFileHelperScreenshotAction::OnTaskCompleted()
  71. {
  72. IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
  73. if (!ScreenshotTexture && FileManager.FileExists(*FilePath))
  74. {
  75. ScreenshotTexture = FImageUtils::ImportFileAsTexture2D(FilePath);
  76. }
  77. if (ScreenshotTexture)
  78. {
  79. Completed.Broadcast(ScreenshotTexture, FilePath);
  80. }
  81. else
  82. {
  83. OnTaskFailed();
  84. }
  85. Reset();
  86. }
  87. void UFileHelperScreenshotAction::OnTaskFailed()
  88. {
  89. Reset();
  90. Failed.Broadcast(ScreenshotTexture, FilePath);
  91. }
  92. void UFileHelperScreenshotAction::CreateCustomCameraScreenshot()
  93. {
  94. UWorld* World = WorldContextObject->GetWorld();
  95. if (!World || !World->GetGameViewport() || !World->GetGameViewport()->Viewport)
  96. {
  97. OnTaskFailed();
  98. return;
  99. }
  100. const ACameraActor* Camera = Options.CustomCameraActor;
  101. if (!Camera)
  102. {
  103. OnTaskFailed();
  104. return;
  105. }
  106. const UCameraComponent* CameraComponent = Camera->GetCameraComponent();
  107. if (!CameraComponent)
  108. {
  109. OnTaskFailed();
  110. return;
  111. }
  112. /*const APlayerCameraManager* PlayerCamera = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0);
  113. if (!PlayerCamera)
  114. {
  115. OnTaskFailed();
  116. return;
  117. }*/
  118. const FViewport* GameViewport = World->GetGameViewport()->Viewport;
  119. const FIntRect ViewRect(0, 0, GameViewport->GetSizeXY().X, GameViewport->GetSizeXY().Y);
  120. const FVector CameraLocation = Camera->GetActorLocation();
  121. const FRotator CameraRotation = Camera->GetActorRotation();
  122. USceneCaptureComponent2D* SceneComponent = NewObject<USceneCaptureComponent2D>(this, TEXT("SceneComponent"));
  123. SceneComponent->RegisterComponentWithWorld(World);
  124. SceneComponent->bCaptureEveryFrame = false;
  125. SceneComponent->bCaptureOnMovement = false;
  126. SceneComponent->bAlwaysPersistRenderingState = true;
  127. SceneComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
  128. SceneComponent->FOVAngle = CameraComponent->FieldOfView;
  129. SceneComponent->ProjectionType = CameraComponent->ProjectionMode;
  130. SceneComponent->OrthoWidth = CameraComponent->OrthoWidth;
  131. SceneComponent->SetWorldLocationAndRotation(CameraLocation, CameraRotation);
  132. UTextureRenderTarget2D* TextureRenderTarget = NewObject<UTextureRenderTarget2D>();
  133. TextureRenderTarget->InitCustomFormat(ViewRect.Width(),ViewRect.Height(),PF_B8G8R8A8,false);
  134. TextureRenderTarget->UpdateResourceImmediate();
  135. SceneComponent->TextureTarget = TextureRenderTarget;
  136. SceneComponent->CaptureScene();
  137. TArray<FColor> OutColors;
  138. OutColors.Reserve(ViewRect.Width() * ViewRect.Height());
  139. TextureRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels(OutColors);
  140. OutColors.Shrink();
  141. SceneComponent->UnregisterComponent();
  142. if (OutColors.Num() == 0)
  143. {
  144. OnTaskFailed();
  145. return;
  146. }
  147. TArray<uint8> OutImage;
  148. FImageUtils::ThumbnailCompressImageArray(ViewRect.Width(), ViewRect.Height(), OutColors, OutImage);
  149. if (OutImage.Num() == 0)
  150. {
  151. OnTaskFailed();
  152. return;
  153. }
  154. if (!FFileHelper::SaveArrayToFile(OutImage, *FilePath))
  155. {
  156. OnTaskFailed();
  157. return;
  158. }
  159. ScreenshotTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
  160. OnTaskCompleted();
  161. }
  162. void UFileHelperScreenshotAction::Reset()
  163. {
  164. WorldContextObject = nullptr;
  165. ScreenshotTexture = nullptr;
  166. bActive = false;
  167. FilePath.Empty();
  168. FScreenshotRequest::Reset();
  169. FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
  170. RemoveFromRoot();
  171. }