VRTowerDef/Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRSceneCaptureCubemap...

194 lines
6.6 KiB
C++
Raw Permalink Normal View History

2024-05-29 08:53:41 +00:00
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSceneCaptureCubemap.h"
#include "OculusXRHMDPrivate.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/World.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/TextureRenderTarget2D.h"
#include "TextureResource.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "XRThreadUtils.h"
#include "RenderingThread.h"
//-------------------------------------------------------------------------------------------------
// UOculusXRSceneCaptureCubemap
//-------------------------------------------------------------------------------------------------
UOculusXRSceneCaptureCubemap::UOculusXRSceneCaptureCubemap()
: Stage(None)
, CaptureBoxSideRes(2048)
, CaptureFormat(EPixelFormat::PF_A16B16G16R16)
, OverriddenLocation(FVector::ZeroVector)
, OverriddenOrientation(FQuat::Identity)
, CaptureOffset(FVector::ZeroVector)
{
}
void UOculusXRSceneCaptureCubemap::StartCapture(UWorld* World, uint32 InCaptureBoxSideRes, EPixelFormat InFormat)
{
CaptureBoxSideRes = InCaptureBoxSideRes;
CaptureFormat = InFormat;
FVector Location = OverriddenLocation;
FQuat Orientation = OverriddenOrientation;
APlayerController* CapturePlayerController = UGameplayStatics::GetPlayerController(GWorld, 0);
if (CapturePlayerController)
{
FRotator Rotation;
CapturePlayerController->GetPlayerViewPoint(Location, Rotation);
Rotation.Pitch = Rotation.Roll = 0;
Orientation = FQuat(Rotation);
Location += CaptureOffset;
}
if (!OverriddenOrientation.IsIdentity())
{
Orientation = OverriddenOrientation;
}
if (!OverriddenLocation.IsZero())
{
Location = OverriddenLocation;
}
const FVector ZAxis(0, 0, 1);
const FVector YAxis(0, 1, 0);
const FQuat FaceOrientations[] = { { ZAxis, PI / 2 }, { ZAxis, -PI / 2 }, // right, left
{ YAxis, -PI / 2 }, { YAxis, PI / 2 }, // top, bottom
{ ZAxis, 0 }, { ZAxis, -PI } }; // front, back
for (int i = 0; i < 6; ++i)
{
USceneCaptureComponent2D* CaptureComponent = NewObject<USceneCaptureComponent2D>();
CaptureComponent->SetVisibility(true);
CaptureComponent->SetHiddenInGame(false);
CaptureComponent->FOVAngle = 90.f;
CaptureComponent->bCaptureEveryFrame = true;
CaptureComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
const FName TargetName = MakeUniqueObjectName(this, UTextureRenderTarget2D::StaticClass(), TEXT("SceneCaptureTextureTarget"));
CaptureComponent->TextureTarget = NewObject<UTextureRenderTarget2D>(this, TargetName);
CaptureComponent->TextureTarget->InitCustomFormat(CaptureBoxSideRes, CaptureBoxSideRes, CaptureFormat, false);
CaptureComponents.Add(CaptureComponent);
CaptureComponent->RegisterComponentWithWorld(GWorld);
CaptureComponent->SetWorldLocationAndRotation(Location, Orientation * FaceOrientations[i]);
CaptureComponent->UpdateContent();
}
Stage = SettingPos;
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.bNoFail = true;
SpawnInfo.ObjectFlags = RF_Transient;
AStaticMeshActor* InGameActor;
InGameActor = World->SpawnActor<AStaticMeshActor>(SpawnInfo);
OutputDir = FPaths::ProjectSavedDir() + TEXT("/Cubemaps");
IFileManager::Get().MakeDirectory(*OutputDir);
}
void UOculusXRSceneCaptureCubemap::Tick(float DeltaTime)
{
ExecuteOnRenderThread([]() {
TickRenderingTickables();
});
if (Stage == SettingPos)
{
Stage = Capturing;
return;
}
//Read Whole Capture Buffer
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
TArray<FColor> OneFaceSurface, WholeCubemapData;
OneFaceSurface.AddUninitialized(CaptureBoxSideRes * CaptureBoxSideRes);
WholeCubemapData.AddUninitialized(CaptureBoxSideRes * 6 * CaptureBoxSideRes);
// Read pixels
for (int cubeFaceIdx = 0; cubeFaceIdx < 6; ++cubeFaceIdx)
{
auto RenderTarget = CaptureComponents[cubeFaceIdx]->TextureTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixelsPtr(OneFaceSurface.GetData(), FReadSurfaceDataFlags());
// enforce alpha to be 1
for (FColor& Color : OneFaceSurface)
{
Color.A = 255;
}
// copy subimage into whole cubemap array
const uint32 Stride = CaptureBoxSideRes * 6;
const uint32 XOff = cubeFaceIdx * CaptureBoxSideRes;
const uint32 StripSizeInBytes = CaptureBoxSideRes * sizeof(FColor);
for (uint32 y = 0; y < CaptureBoxSideRes; ++y)
{
FMemory::Memcpy(WholeCubemapData.GetData() + XOff + y * Stride, OneFaceSurface.GetData() + y * CaptureBoxSideRes, StripSizeInBytes);
}
}
ImageWrapper->SetRaw(WholeCubemapData.GetData(), WholeCubemapData.GetAllocatedSize(), CaptureBoxSideRes * 6, CaptureBoxSideRes, ERGBFormat::BGRA, 8);
const TArray64<uint8>& PNGData = ImageWrapper->GetCompressed(100);
const FString Filename = OutputDir + FString::Printf(TEXT("/Cubemap-%d-%s.png"), CaptureBoxSideRes, *FDateTime::Now().ToString(TEXT("%m.%d-%H.%M.%S")));
FFileHelper::SaveArrayToFile(PNGData, *Filename);
check(Stage == Capturing);
Stage = Finished;
for (int i = 0; i < CaptureComponents.Num(); ++i)
{
CaptureComponents[i]->UnregisterComponent();
}
CaptureComponents.SetNum(0);
RemoveFromRoot(); // We're done here, so remove ourselves from the root set. @TODO: Fix this later
}
#if !UE_BUILD_SHIPPING
void UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
bool bCreateOculusMobileCubemap = false;
FVector CaptureOffset(FVector::ZeroVector);
float Yaw = 0.f;
for (const FString& Arg : Args)
{
FParse::Value(*Arg, TEXT("XOFF="), CaptureOffset.X);
FParse::Value(*Arg, TEXT("YOFF="), CaptureOffset.Y);
FParse::Value(*Arg, TEXT("ZOFF="), CaptureOffset.Z);
FParse::Value(*Arg, TEXT("YAW="), Yaw);
if (Arg.Equals(TEXT("MOBILE"), ESearchCase::IgnoreCase))
{
bCreateOculusMobileCubemap = true;
}
}
UOculusXRSceneCaptureCubemap* CubemapCapturer = NewObject<UOculusXRSceneCaptureCubemap>();
CubemapCapturer->AddToRoot(); // TODO: Don't add the object to the GC root
CubemapCapturer->SetOffset((FVector)CaptureOffset);
if (Yaw != 0.f)
{
FRotator Rotation(FRotator::ZeroRotator);
Rotation.Yaw = Yaw;
const FQuat Orient(Rotation);
CubemapCapturer->SetInitialOrientation(Orient);
}
const uint32 CaptureHeight = 2048;
CubemapCapturer->StartCapture(World, bCreateOculusMobileCubemap ? CaptureHeight / 2 : CaptureHeight);
}
#endif