Android build settings + metaxr
This commit is contained in:
77
Plugins/MetaXR/Source/OculusXRMR/OculusXRMR.Build.cs
Normal file
77
Plugins/MetaXR/Source/OculusXRMR/OculusXRMR.Build.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class OculusXRMR : ModuleRules
|
||||
{
|
||||
public OculusXRMR(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
bUseUnity = true;
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"InputDevice", // For IInputDevice.h
|
||||
"HeadMountedDisplay", // For IMotionController.h
|
||||
"ImageWrapper",
|
||||
"Engine"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"RHI",
|
||||
"VulkanRHI",
|
||||
"RenderCore",
|
||||
"MediaAssets",
|
||||
"HeadMountedDisplay",
|
||||
"OculusXRHMD",
|
||||
"OVRPluginXR",
|
||||
});
|
||||
|
||||
if (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 3))
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"XRBase",
|
||||
});
|
||||
}
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"OculusXRHMD/Private",
|
||||
"OculusXRInput/Private",
|
||||
});
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"Runtime/Renderer/Private",
|
||||
"Runtime/Engine/Classes/Components",
|
||||
"Runtime/MediaAssets/Private",
|
||||
});
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||
{
|
||||
PublicDelayLoadDLLs.Add("OVRPluginXR.dll");
|
||||
}
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
|
||||
}
|
||||
|
||||
if (Target.bBuildEditor == true)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRMRFunctionLibrary.h"
|
||||
#include "OculusXRMRPrivate.h"
|
||||
#include "OculusXRMRModule.h"
|
||||
#include "OculusXRMR_CastingCameraActor.h"
|
||||
#include "OculusXRMR_State.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRHMDPrivate.h"
|
||||
#include "IHeadMountedDisplay.h"
|
||||
#include "IXRTrackingSystem.h"
|
||||
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// UOculusXRFunctionLibrary
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
UOculusXRMRFunctionLibrary::UOculusXRMRFunctionLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void UOculusXRMRFunctionLibrary::GetAllTrackedCamera(TArray<FOculusXRTrackedCamera>& TrackedCameras, bool bCalibratedOnly)
|
||||
{
|
||||
TrackedCameras.Empty();
|
||||
|
||||
if (!FOculusXRMRModule::IsAvailable() || !FOculusXRMRModule::Get().IsInitialized())
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("OculusXRMR not available"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized() == ovrpBool_False)
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("OVRPlugin not initialized"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().UpdateExternalCamera()))
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("FOculusXRHMDModule::GetPluginWrapper().UpdateExternalCamera failure"));
|
||||
return;
|
||||
}
|
||||
|
||||
int cameraCount = 0;
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraCount(&cameraCount)))
|
||||
{
|
||||
UE_LOG(LogMR, Log, TEXT("FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraCount failure"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cameraCount; ++i)
|
||||
{
|
||||
char cameraName[OVRP_EXTERNAL_CAMERA_NAME_SIZE];
|
||||
ovrpCameraIntrinsics cameraIntrinsics;
|
||||
ovrpCameraExtrinsics cameraExtrinsics;
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraName(i, cameraName);
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraIntrinsics(i, &cameraIntrinsics);
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraExtrinsics(i, &cameraExtrinsics);
|
||||
if ((bCalibratedOnly == false || cameraExtrinsics.CameraStatus == ovrpCameraStatus_Calibrated) && cameraIntrinsics.IsValid && cameraExtrinsics.IsValid)
|
||||
{
|
||||
FOculusXRTrackedCamera camera;
|
||||
camera.Index = i;
|
||||
camera.Name = cameraName;
|
||||
camera.FieldOfView = FMath::RadiansToDegrees(FMath::Atan(cameraIntrinsics.FOVPort.LeftTan) + FMath::Atan(cameraIntrinsics.FOVPort.RightTan));
|
||||
camera.SizeX = cameraIntrinsics.ImageSensorPixelResolution.w;
|
||||
camera.SizeY = cameraIntrinsics.ImageSensorPixelResolution.h;
|
||||
camera.AttachedTrackedDevice = OculusXRHMD::ToEOculusXRTrackedDeviceType(cameraExtrinsics.AttachedToNode);
|
||||
OculusXRHMD::FPose Pose;
|
||||
static_cast<OculusXRHMD::FOculusXRHMD*>(GEngine->XRSystem.Get())->ConvertPose(cameraExtrinsics.RelativePose, Pose);
|
||||
camera.CalibratedRotation = Pose.Orientation.Rotator();
|
||||
camera.CalibratedOffset = Pose.Position;
|
||||
camera.UserRotation = FRotator::ZeroRotator;
|
||||
camera.UserOffset = FVector::ZeroVector;
|
||||
TrackedCameras.Add(camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> UOculusXRMRFunctionLibrary::GetTrackingSystem()
|
||||
{
|
||||
#if OCULUS_HMD_SUPPORTED_PLATFORMS
|
||||
if (GEngine && GEngine->XRSystem.IsValid())
|
||||
{
|
||||
return GEngine->XRSystem;
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UOculusXRMRFunctionLibrary::GetTrackingReferenceLocationAndRotationInWorldSpace(USceneComponent* TrackingReferenceComponent, FVector& TRLocation, FRotator& TRRotation)
|
||||
{
|
||||
if (!TrackingReferenceComponent)
|
||||
{
|
||||
APlayerController* PlayerController = GWorld->GetFirstPlayerController();
|
||||
if (!PlayerController)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
APawn* Pawn = PlayerController->GetPawn();
|
||||
if (!Pawn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TRLocation = Pawn->GetActorLocation();
|
||||
TRRotation = Pawn->GetActorRotation();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRLocation = TrackingReferenceComponent->GetComponentLocation();
|
||||
TRRotation = TrackingReferenceComponent->GetComponentRotation();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
UOculusXRMR_Settings* UOculusXRMRFunctionLibrary::GetOculusXRMRSettings()
|
||||
{
|
||||
UOculusXRMR_Settings* Settings = nullptr;
|
||||
if (FOculusXRMRModule::IsAvailable())
|
||||
{
|
||||
Settings = FOculusXRMRModule::Get().GetMRSettings();
|
||||
}
|
||||
return Settings;
|
||||
}
|
||||
|
||||
USceneComponent* UOculusXRMRFunctionLibrary::GetTrackingReferenceComponent()
|
||||
{
|
||||
USceneComponent* TrackingRef = nullptr;
|
||||
if (FOculusXRMRModule::IsAvailable())
|
||||
{
|
||||
TrackingRef = FOculusXRMRModule::Get().GetMRState()->TrackingReferenceComponent;
|
||||
}
|
||||
return TrackingRef;
|
||||
}
|
||||
|
||||
bool UOculusXRMRFunctionLibrary::SetTrackingReferenceComponent(USceneComponent* Component)
|
||||
{
|
||||
if (FOculusXRMRModule::IsAvailable())
|
||||
{
|
||||
FOculusXRMRModule::Get().GetMRState()->TrackingReferenceComponent = Component;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float UOculusXRMRFunctionLibrary::GetMrcScalingFactor()
|
||||
{
|
||||
if (FOculusXRMRModule::IsAvailable())
|
||||
{
|
||||
return FOculusXRMRModule::Get().GetMRState()->ScalingFactor;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool UOculusXRMRFunctionLibrary::SetMrcScalingFactor(float ScalingFactor)
|
||||
{
|
||||
if (FOculusXRMRModule::IsAvailable() && ScalingFactor > 0.0f)
|
||||
{
|
||||
FOculusXRMRModule::Get().GetMRState()->ScalingFactor = ScalingFactor;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UOculusXRMRFunctionLibrary::IsMrcEnabled()
|
||||
{
|
||||
return FOculusXRMRModule::IsAvailable() && FOculusXRMRModule::Get().IsInitialized();
|
||||
}
|
||||
|
||||
bool UOculusXRMRFunctionLibrary::IsMrcActive()
|
||||
{
|
||||
return FOculusXRMRModule::IsAvailable() && FOculusXRMRModule::Get().IsActive();
|
||||
}
|
||||
541
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRModule.cpp
Normal file
541
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRModule.cpp
Normal file
@@ -0,0 +1,541 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#include "OculusXRMRModule.h"
|
||||
|
||||
#include "Engine/Engine.h"
|
||||
#include "ISpectatorScreenController.h"
|
||||
#include "IXRTrackingSystem.h"
|
||||
#include "StereoRendering.h"
|
||||
#include "StereoRenderTargetManager.h"
|
||||
#include "SceneCaptureComponent2D.h"
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "PostProcess/SceneRenderTargets.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "OculusXRHMDModule.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRMRFunctionLibrary.h"
|
||||
#include "OculusXRMRPrivate.h"
|
||||
#include "OculusXRMR_Settings.h"
|
||||
#include "OculusXRMR_State.h"
|
||||
#include "OculusXRMR_CastingCameraActor.h"
|
||||
#include "AudioDevice.h"
|
||||
#if PLATFORM_ANDROID
|
||||
#include "IVulkanDynamicRHI.h"
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "Editor.h" // for FEditorDelegates::PostPIEStarted
|
||||
#endif
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMR"
|
||||
|
||||
FOculusXRMRModule::FOculusXRMRModule()
|
||||
: bInitialized(false)
|
||||
, MRSettings(nullptr)
|
||||
, MRState(nullptr)
|
||||
, MRActor(nullptr)
|
||||
, CurrentWorld(nullptr)
|
||||
, WorldAddedEventBinding()
|
||||
, WorldDestroyedEventBinding()
|
||||
, WorldLoadEventBinding()
|
||||
#if PLATFORM_ANDROID
|
||||
, bActivated(false)
|
||||
, InitialWorldAddedEventBinding()
|
||||
, InitialWorldLoadEventBinding()
|
||||
, PreWorldTickEventBinding()
|
||||
#endif
|
||||
#if WITH_EDITOR
|
||||
, PieBeginEventBinding()
|
||||
, PieStartedEventBinding()
|
||||
, PieEndedEventBinding()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
FOculusXRMRModule::~FOculusXRMRModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::StartupModule()
|
||||
{
|
||||
#if OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
#if PLATFORM_WINDOWS
|
||||
const TCHAR* CmdLine = FCommandLine::Get();
|
||||
const bool bAutoOpenFromParams = FParse::Param(CmdLine, TEXT("mixedreality"));
|
||||
|
||||
if (bAutoOpenFromParams && FOculusXRHMDModule::Get().PreInit())
|
||||
{
|
||||
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
|
||||
{
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().InitializeMixedReality()))
|
||||
{
|
||||
InitMixedRealityCapture();
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("ovrp_InitializeMixedReality() failed"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("OVRPlugin has not been initialized"));
|
||||
}
|
||||
}
|
||||
#elif PLATFORM_ANDROID
|
||||
// On Android, FOculusXRHMDModule::GetPluginWrapper().Media_Initialize() needs OVRPlugin to be initialized first, so we should handle that when the world is created
|
||||
if (GEngine)
|
||||
{
|
||||
InitialWorldAddedEventBinding = GEngine->OnWorldAdded().AddRaw(this, &FOculusXRMRModule::OnInitialWorldCreated);
|
||||
}
|
||||
InitialWorldLoadEventBinding = FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FOculusXRMRModule::OnInitialWorldCreated);
|
||||
#endif // PLATFORM_WINDOWS || PLATFORM_ANDROID
|
||||
#endif // OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::ShutdownModule()
|
||||
{
|
||||
#if OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
if (bInitialized)
|
||||
{
|
||||
if (GEngine)
|
||||
{
|
||||
GEngine->OnWorldAdded().Remove(WorldAddedEventBinding);
|
||||
GEngine->OnWorldDestroyed().Remove(WorldDestroyedEventBinding);
|
||||
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(WorldLoadEventBinding);
|
||||
#if WITH_EDITOR
|
||||
FEditorDelegates::PostPIEStarted.Remove(PieStartedEventBinding);
|
||||
FEditorDelegates::PrePIEEnded.Remove(PieEndedEventBinding);
|
||||
#else
|
||||
// Stop casting and close camera with module if it's the game
|
||||
MRSettings->SetIsCasting(false);
|
||||
#endif
|
||||
}
|
||||
#if PLATFORM_ANDROID
|
||||
ovrpBool mediaInit = false;
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_GetInitialized(&mediaInit)) && mediaInit == ovrpBool_True)
|
||||
{
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_Shutdown();
|
||||
}
|
||||
#endif
|
||||
FOculusXRHMDModule::GetPluginWrapper().ShutdownMixedReality();
|
||||
|
||||
if (MRSettings->IsRooted())
|
||||
{
|
||||
MRSettings->RemoveFromRoot();
|
||||
}
|
||||
if (MRState->IsRooted())
|
||||
{
|
||||
MRState->RemoveFromRoot();
|
||||
}
|
||||
}
|
||||
#if PLATFORM_ANDROID
|
||||
if (InitialWorldAddedEventBinding.IsValid() && GEngine)
|
||||
{
|
||||
GEngine->OnWorldAdded().Remove(InitialWorldAddedEventBinding);
|
||||
InitialWorldAddedEventBinding.Reset();
|
||||
}
|
||||
if (InitialWorldLoadEventBinding.IsValid())
|
||||
{
|
||||
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(InitialWorldLoadEventBinding);
|
||||
InitialWorldLoadEventBinding.Reset();
|
||||
}
|
||||
#endif
|
||||
#endif // OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
}
|
||||
|
||||
bool FOculusXRMRModule::IsActive()
|
||||
{
|
||||
bool bReturn = bInitialized && MRSettings && MRSettings->GetIsCasting();
|
||||
#if PLATFORM_ANDROID
|
||||
bReturn = bReturn && bActivated;
|
||||
#endif
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
UOculusXRMR_Settings* FOculusXRMRModule::GetMRSettings()
|
||||
{
|
||||
return MRSettings;
|
||||
}
|
||||
|
||||
UOculusXRMR_State* FOculusXRMRModule::GetMRState()
|
||||
{
|
||||
return MRState;
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnWorldCreated(UWorld* NewWorld)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
#if WITH_EDITORONLY_DATA
|
||||
const bool bIsGameInst = !IsRunningCommandlet() && NewWorld->IsGameWorld();
|
||||
if (bIsGameInst)
|
||||
#endif
|
||||
{
|
||||
CurrentWorld = NewWorld;
|
||||
SetupInGameCapture();
|
||||
}
|
||||
#endif
|
||||
#if PLATFORM_ANDROID
|
||||
CurrentWorld = NewWorld;
|
||||
// Check MRC activation state initially when loading world
|
||||
ChangeCaptureState();
|
||||
// Poll MRC activation state for future changes
|
||||
PreWorldTickEventBinding = FWorldDelegates::OnWorldPreActorTick.AddRaw(this, &FOculusXRMRModule::OnWorldTick);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnWorldDestroyed(UWorld* NewWorld)
|
||||
{
|
||||
CurrentWorld = nullptr;
|
||||
#if PLATFORM_ANDROID
|
||||
if (PreWorldTickEventBinding.IsValid())
|
||||
{
|
||||
FWorldDelegates::OnWorldPreActorTick.Remove(PreWorldTickEventBinding);
|
||||
PreWorldTickEventBinding.Reset();
|
||||
}
|
||||
#endif // PLATFORM_ANDROID
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::InitMixedRealityCapture()
|
||||
{
|
||||
bInitialized = true;
|
||||
|
||||
MRSettings = NewObject<UOculusXRMR_Settings>((UObject*)GetTransientPackage(), FName("OculusXRMR_Settings"), RF_MarkAsRootSet);
|
||||
MRState = NewObject<UOculusXRMR_State>((UObject*)GetTransientPackage(), FName("OculusXRMR_State"), RF_MarkAsRootSet);
|
||||
|
||||
// Always bind the event handlers in case devs call them without MRC on
|
||||
MRSettings->TrackedCameraIndexChangeDelegate.BindRaw(this, &FOculusXRMRModule::OnTrackedCameraIndexChanged);
|
||||
MRSettings->CompositionMethodChangeDelegate.BindRaw(this, &FOculusXRMRModule::OnCompositionMethodChanged);
|
||||
MRSettings->IsCastingChangeDelegate.BindRaw(this, &FOculusXRMRModule::OnIsCastingChanged);
|
||||
|
||||
ResetSettingsAndState();
|
||||
|
||||
WorldAddedEventBinding = GEngine->OnWorldAdded().AddRaw(this, &FOculusXRMRModule::OnWorldCreated);
|
||||
WorldDestroyedEventBinding = GEngine->OnWorldDestroyed().AddRaw(this, &FOculusXRMRModule::OnWorldDestroyed);
|
||||
WorldLoadEventBinding = FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FOculusXRMRModule::OnWorldCreated);
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Bind events on PIE start/end to open/close camera
|
||||
PieBeginEventBinding = FEditorDelegates::BeginPIE.AddRaw(this, &FOculusXRMRModule::OnPieBegin);
|
||||
PieStartedEventBinding = FEditorDelegates::PostPIEStarted.AddRaw(this, &FOculusXRMRModule::OnPieStarted);
|
||||
PieEndedEventBinding = FEditorDelegates::PrePIEEnded.AddRaw(this, &FOculusXRMRModule::OnPieEnded);
|
||||
#else // WITH_EDITOR
|
||||
// Start casting and open camera with the module if it's the game
|
||||
MRSettings->SetIsCasting(true);
|
||||
#endif // WITH_EDITOR
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::SetupExternalCamera()
|
||||
{
|
||||
using namespace OculusXRHMD;
|
||||
|
||||
if (!MRSettings->GetIsCasting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Always request the MRC actor to handle a camera state change on its end
|
||||
MRState->ChangeCameraStateRequested = true;
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::SetupInGameCapture()
|
||||
{
|
||||
// Don't do anything if we don't have a UWorld or if we are not casting
|
||||
if (CurrentWorld == nullptr || !MRSettings->GetIsCasting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the bind camera request to true
|
||||
MRState->BindToTrackedCameraIndexRequested = true;
|
||||
|
||||
// Don't add another actor if there's already a MRC camera actor
|
||||
for (TActorIterator<AOculusXRMR_CastingCameraActor> ActorIt(CurrentWorld); ActorIt; ++ActorIt)
|
||||
{
|
||||
if (IsValidChecked(*ActorIt) && !ActorIt->IsUnreachable() && ActorIt->IsValidLowLevel())
|
||||
{
|
||||
MRActor = *ActorIt;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn an MRC camera actor if one wasn't already there
|
||||
MRActor = CurrentWorld->SpawnActorDeferred<AOculusXRMR_CastingCameraActor>(AOculusXRMR_CastingCameraActor::StaticClass(), FTransform::Identity);
|
||||
MRActor->InitializeStates(MRSettings, MRState);
|
||||
UGameplayStatics::FinishSpawningActor(MRActor, FTransform::Identity);
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::CloseInGameCapture()
|
||||
{
|
||||
// Destory actor and close the camera when we turn MRC off
|
||||
if (MRActor != nullptr && MRActor->GetWorld() != nullptr)
|
||||
{
|
||||
MRActor->Destroy();
|
||||
MRActor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::ResetSettingsAndState()
|
||||
{
|
||||
// Reset MR State
|
||||
MRState->TrackedCamera = FOculusXRTrackedCamera();
|
||||
MRState->TrackingReferenceComponent = nullptr;
|
||||
MRState->ChangeCameraStateRequested = false;
|
||||
MRState->BindToTrackedCameraIndexRequested = false;
|
||||
|
||||
// Reset MR Settings
|
||||
const bool bAutoOpenInExternalComposition = FParse::Param(FCommandLine::Get(), TEXT("externalcomposition"));
|
||||
MRSettings->BindToTrackedCameraIndexIfAvailable(0);
|
||||
MRSettings->LoadFromIni();
|
||||
|
||||
// Save right after load to write defaults to the config if they weren't already there
|
||||
MRSettings->SaveToIni();
|
||||
|
||||
if (bAutoOpenInExternalComposition)
|
||||
{
|
||||
MRSettings->CompositionMethod = EOculusXRMR_CompositionMethod::ExternalComposition;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
void FOculusXRMRModule::ChangeCaptureState()
|
||||
{
|
||||
ovrpBool activated;
|
||||
// Set up or close in-game capture when activation state changes
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_Update()) && OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_IsMrcActivated(&activated)) && activated == ovrpBool_True)
|
||||
{
|
||||
if (!bActivated)
|
||||
{
|
||||
UE_LOG(LogMR, Log, TEXT("Activating MR Capture"))
|
||||
bActivated = true;
|
||||
|
||||
// UE resizes the main scene color and depth targets to the maximum dimensions of all rendertargets,
|
||||
// which causes rendering issues if it doesn't match the compositor-allocated eye textures. This is
|
||||
// a hacky fix by making sure that the scene capture rendertarget is no larger than the eye.
|
||||
int frameWidth;
|
||||
int frameHeight;
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_GetMrcFrameSize(&frameWidth, &frameHeight);
|
||||
uint32 maxWidth = frameWidth / 2;
|
||||
uint32 maxHeight = frameHeight;
|
||||
IStereoRenderTargetManager* const StereoRenderTargetManager = GEngine->StereoRenderingDevice->GetRenderTargetManager();
|
||||
if (StereoRenderTargetManager)
|
||||
{
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
StereoRenderTargetManager->CalculateRenderTargetSize(maxWidth, maxHeight);
|
||||
#else // WITH_OCULUS_BRANCH
|
||||
StereoRenderTargetManager->CalculateRenderTargetSize(*(FViewport*)GEngine->GameViewport->GetGameViewport(), maxWidth, maxHeight);
|
||||
#endif // WITH_OCULUS_BRANCH
|
||||
}
|
||||
maxWidth *= 2;
|
||||
frameWidth = frameWidth > maxWidth ? maxWidth : frameWidth;
|
||||
frameHeight = frameHeight > maxHeight ? maxHeight : frameHeight;
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetMrcFrameSize(frameWidth, frameHeight);
|
||||
UE_LOG(LogMR, Log, TEXT("MRC Frame width: %d height %d"), frameWidth, frameHeight);
|
||||
|
||||
SetupInGameCapture();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bActivated)
|
||||
{
|
||||
UE_LOG(LogMR, Log, TEXT("Deactivating MR Capture"))
|
||||
bActivated = false;
|
||||
CloseInGameCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnWorldTick(UWorld* World, ELevelTick Tick, float Delta)
|
||||
{
|
||||
// Poll MRC activation state
|
||||
if (CurrentWorld && World == CurrentWorld)
|
||||
{
|
||||
ChangeCaptureState();
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnInitialWorldCreated(UWorld* NewWorld)
|
||||
{
|
||||
// Remove the initial world load handlers
|
||||
if (InitialWorldAddedEventBinding.IsValid())
|
||||
{
|
||||
GEngine->OnWorldAdded().Remove(InitialWorldAddedEventBinding);
|
||||
InitialWorldAddedEventBinding.Reset();
|
||||
}
|
||||
if (InitialWorldLoadEventBinding.IsValid())
|
||||
{
|
||||
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(InitialWorldLoadEventBinding);
|
||||
InitialWorldLoadEventBinding.Reset();
|
||||
}
|
||||
|
||||
// Initialize and check if MRC is enabled
|
||||
if (FOculusXRHMDModule::Get().PreInit())
|
||||
{
|
||||
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
|
||||
{
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().InitializeMixedReality()))
|
||||
{
|
||||
ovrpBool mrcEnabled;
|
||||
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_Initialize()))
|
||||
{
|
||||
UE_LOG(LogMR, Log, TEXT("MRC Initialized"));
|
||||
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_IsMrcEnabled(&mrcEnabled)) && mrcEnabled == ovrpBool_True)
|
||||
{
|
||||
UE_LOG(LogMR, Log, TEXT("MRC Enabled"));
|
||||
|
||||
// Find a free queue index for vulkan
|
||||
if (RHIGetInterfaceType() == ERHIInterfaceType::Vulkan)
|
||||
{
|
||||
unsigned int queueIndex = 0;
|
||||
ExecuteOnRenderThread([&queueIndex]() {
|
||||
ExecuteOnRHIThread([&queueIndex]() {
|
||||
const uint32 GraphicsQueueIndex = GetIVulkanDynamicRHI()->RHIGetGraphicsQueueIndex();
|
||||
if (GraphicsQueueIndex == queueIndex)
|
||||
{
|
||||
++queueIndex;
|
||||
}
|
||||
});
|
||||
});
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetAvailableQueueIndexVulkan(queueIndex);
|
||||
}
|
||||
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetMrcInputVideoBufferType(ovrpMediaInputVideoBufferType_TextureHandle);
|
||||
|
||||
FAudioDeviceHandle AudioDevice = FAudioDevice::GetMainAudioDevice();
|
||||
if (AudioDevice.GetAudioDevice())
|
||||
{
|
||||
float SampleRate = AudioDevice->GetSampleRate();
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetMrcAudioSampleRate((int)SampleRate);
|
||||
}
|
||||
|
||||
InitMixedRealityCapture();
|
||||
OnWorldCreated(NewWorld);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shut down if MRC not enabled or the media couldn't be enabled
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_Shutdown();
|
||||
FOculusXRHMDModule::GetPluginWrapper().ShutdownMixedReality();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shut down if MRC not enabled or the media couldn't be enabled
|
||||
FOculusXRHMDModule::GetPluginWrapper().ShutdownMixedReality();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("ovrp_InitializeMixedReality() failed"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("OVRPlugin has not been initialized"));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void FOculusXRMRModule::OnTrackedCameraIndexChanged(int OldVal, int NewVal)
|
||||
{
|
||||
if (OldVal == NewVal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MRState->BindToTrackedCameraIndexRequested = true;
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnCompositionMethodChanged(EOculusXRMR_CompositionMethod OldVal, EOculusXRMR_CompositionMethod NewVal)
|
||||
{
|
||||
if (OldVal == NewVal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetupExternalCamera();
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnIsCastingChanged(bool OldVal, bool NewVal)
|
||||
{
|
||||
if (OldVal == NewVal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (NewVal == true)
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetMrcActivationMode(ovrpMediaMrcActivationMode_Automatic);
|
||||
#endif
|
||||
// Initialize everything again if we turn MRC on
|
||||
SetupExternalCamera();
|
||||
SetupInGameCapture();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetMrcActivationMode(ovrpMediaMrcActivationMode_Disabled);
|
||||
#endif
|
||||
CloseInGameCapture();
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnUseDynamicLightingChanged(bool OldVal, bool NewVal)
|
||||
{
|
||||
if (OldVal == NewVal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetupExternalCamera();
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnDepthQualityChanged(EOculusXRMR_DepthQuality OldVal, EOculusXRMR_DepthQuality NewVal)
|
||||
{
|
||||
if (OldVal == NewVal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetupExternalCamera();
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void FOculusXRMRModule::OnPieBegin(bool bIsSimulating)
|
||||
{
|
||||
// Reset all the parameters and start casting when PIE starts but before the game is initialized
|
||||
if (!bIsSimulating)
|
||||
{
|
||||
|
||||
ResetSettingsAndState();
|
||||
|
||||
// Always start casting with PIE (since this can only be reached if the command line param is on)
|
||||
MRSettings->SetIsCasting(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnPieStarted(bool bIsSimulating)
|
||||
{
|
||||
// Handle the PIE world as a normal game world
|
||||
UWorld* PieWorld = GEditor->GetPIEWorldContext()->World();
|
||||
if (!bIsSimulating && PieWorld)
|
||||
{
|
||||
OnWorldCreated(PieWorld);
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRMRModule::OnPieEnded(bool bIsSimulating)
|
||||
{
|
||||
UWorld* PieWorld = GEditor->GetPIEWorldContext()->World();
|
||||
if (!bIsSimulating && PieWorld)
|
||||
{
|
||||
// Stop casting when PIE ends
|
||||
MRSettings->SetIsCasting(false);
|
||||
OnWorldDestroyed(PieWorld);
|
||||
}
|
||||
}
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
IMPLEMENT_MODULE(FOculusXRMRModule, OculusXRMR)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
97
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRModule.h
Normal file
97
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRModule.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "IOculusXRMRModule.h"
|
||||
#include "Engine/EngineBaseTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMR"
|
||||
|
||||
enum class EOculusXRMR_CompositionMethod : uint8;
|
||||
enum class EOculusXRMR_DepthQuality : uint8;
|
||||
|
||||
class UOculusXRMR_Settings;
|
||||
class AOculusXRMR_CastingCameraActor;
|
||||
class UOculusXRMR_State;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInputModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class FOculusXRMRModule : public IOculusXRMRModule
|
||||
{
|
||||
public:
|
||||
FOculusXRMRModule();
|
||||
~FOculusXRMRModule();
|
||||
|
||||
static inline FOculusXRMRModule& Get()
|
||||
{
|
||||
return FModuleManager::GetModuleChecked<FOculusXRMRModule>("OculusXRMR");
|
||||
}
|
||||
|
||||
// IOculusXRMRModule
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
bool IsInitialized() { return bInitialized; }
|
||||
|
||||
bool IsActive();
|
||||
UOculusXRMR_Settings* GetMRSettings();
|
||||
UOculusXRMR_State* GetMRState();
|
||||
|
||||
private:
|
||||
bool bInitialized;
|
||||
UOculusXRMR_Settings* MRSettings;
|
||||
UOculusXRMR_State* MRState;
|
||||
AOculusXRMR_CastingCameraActor* MRActor;
|
||||
UWorld* CurrentWorld;
|
||||
|
||||
FDelegateHandle WorldAddedEventBinding;
|
||||
FDelegateHandle WorldDestroyedEventBinding;
|
||||
FDelegateHandle WorldLoadEventBinding;
|
||||
|
||||
void InitMixedRealityCapture();
|
||||
|
||||
/** Initialize the tracked physical camera */
|
||||
void SetupExternalCamera();
|
||||
/** Set up the needed settings and actors for MRC in-game */
|
||||
void SetupInGameCapture();
|
||||
/** Destroy actors for MRC in-game */
|
||||
void CloseInGameCapture();
|
||||
/** Reset all the MRC settings and state to the config and default */
|
||||
void ResetSettingsAndState();
|
||||
|
||||
/** Handle changes on specific settings */
|
||||
void OnCompositionMethodChanged(EOculusXRMR_CompositionMethod OldVal, EOculusXRMR_CompositionMethod NewVal);
|
||||
void OnIsCastingChanged(bool OldVal, bool NewVal);
|
||||
void OnUseDynamicLightingChanged(bool OldVal, bool NewVal);
|
||||
void OnDepthQualityChanged(EOculusXRMR_DepthQuality OldVal, EOculusXRMR_DepthQuality NewVal);
|
||||
void OnTrackedCameraIndexChanged(int OldVal, int NewVal);
|
||||
|
||||
void OnWorldCreated(UWorld* NewWorld);
|
||||
void OnWorldDestroyed(UWorld* NewWorld);
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
bool bActivated;
|
||||
|
||||
FDelegateHandle InitialWorldAddedEventBinding;
|
||||
FDelegateHandle InitialWorldLoadEventBinding;
|
||||
FDelegateHandle PreWorldTickEventBinding;
|
||||
|
||||
void ChangeCaptureState();
|
||||
void OnWorldTick(UWorld* World, ELevelTick Tick, float Delta);
|
||||
void OnInitialWorldCreated(UWorld* NewWorld);
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
FDelegateHandle PieBeginEventBinding;
|
||||
FDelegateHandle PieStartedEventBinding;
|
||||
FDelegateHandle PieEndedEventBinding;
|
||||
|
||||
void OnPieBegin(bool bIsSimulating);
|
||||
void OnPieStarted(bool bIsSimulating);
|
||||
void OnPieEnded(bool bIsSimulating);
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
12
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRPrivate.h
Normal file
12
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMRPrivate.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "IOculusXRMRModule.h"
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
|
||||
#if OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogMR, Log, All);
|
||||
#endif // OCULUS_MR_SUPPORTED_PLATFORMS
|
||||
@@ -0,0 +1,869 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRMR_CastingCameraActor.h"
|
||||
|
||||
#include "OculusXRMRPrivate.h"
|
||||
#include "OculusXRHMD_Settings.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRHMD_SpectatorScreenController.h"
|
||||
#include "OculusXRMRModule.h"
|
||||
#include "OculusXRMR_Settings.h"
|
||||
#include "OculusXRMR_State.h"
|
||||
#include "OculusXRMR_PlaneMeshComponent.h"
|
||||
#include "OculusXRMRFunctionLibrary.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Components/SceneCaptureComponent2D.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/WorldSettings.h"
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Rendering/Texture2DResource.h"
|
||||
#include "RenderingThread.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "VRNotificationsComponent.h"
|
||||
#include "RenderUtils.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "RHIDefinitions.h"
|
||||
#include "DataDrivenShaderPlatformInfo.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMR_CastingCameraActor"
|
||||
|
||||
// Possibly add 2=Limited in a future update
|
||||
static TAutoConsoleVariable<int32> CEnableExternalCompositionPostProcess(TEXT("oculus.mr.ExternalCompositionPostProcess"), 0, TEXT("Enable MR external composition post process: 0=Off, 1=On"));
|
||||
static TAutoConsoleVariable<int32> COverrideMixedRealityParametersVar(TEXT("oculus.mr.OverrideParameters"), 0, TEXT("Use the Mixed Reality console variables"));
|
||||
|
||||
namespace
|
||||
{
|
||||
bool GetCameraTrackedObjectPoseInTrackingSpace(OculusXRHMD::FOculusXRHMD* OculusXRHMD, const FOculusXRTrackedCamera& TrackedCamera, OculusXRHMD::FPose& CameraTrackedObjectPose)
|
||||
{
|
||||
using namespace OculusXRHMD;
|
||||
|
||||
CameraTrackedObjectPose = FPose(FQuat::Identity, FVector::ZeroVector);
|
||||
|
||||
if (TrackedCamera.AttachedTrackedDevice != EOculusXRTrackedDeviceType::None)
|
||||
{
|
||||
ovrpResult result = ovrpSuccess;
|
||||
ovrpPoseStatef cameraPoseState;
|
||||
ovrpNode deviceNode = ToOvrpNode(TrackedCamera.AttachedTrackedDevice);
|
||||
ovrpBool nodePresent = ovrpBool_False;
|
||||
result = FOculusXRHMDModule::GetPluginWrapper().GetNodePresent2(deviceNode, &nodePresent);
|
||||
if (OVRP_FAILURE(result))
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to check if AttachedTrackedDevice is present"));
|
||||
return false;
|
||||
}
|
||||
if (!nodePresent)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("AttachedTrackedDevice is not present"));
|
||||
return false;
|
||||
}
|
||||
|
||||
OculusXRHMD::FGameFrame* CurrentFrame;
|
||||
if (IsInGameThread())
|
||||
{
|
||||
CurrentFrame = OculusXRHMD->GetNextFrameToRender();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentFrame = OculusXRHMD->GetFrame_RenderThread();
|
||||
}
|
||||
|
||||
result = CurrentFrame ? FOculusXRHMDModule::GetPluginWrapper().GetNodePoseState3(ovrpStep_Render, CurrentFrame->FrameNumber, deviceNode, &cameraPoseState) : ovrpFailure;
|
||||
if (OVRP_FAILURE(result))
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to retrieve AttachedTrackedDevice pose state"));
|
||||
return false;
|
||||
}
|
||||
OculusXRHMD->ConvertPose(cameraPoseState.Pose, CameraTrackedObjectPose);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ACastingCameraActor
|
||||
|
||||
AOculusXRMR_CastingCameraActor::AOculusXRMR_CastingCameraActor(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, TrackedCameraCalibrationRequired(false)
|
||||
, HasTrackedCameraCalibrationCalibrated(false)
|
||||
, RefreshBoundaryMeshCounter(3)
|
||||
, ForegroundLayerBackgroundColor(FColor::Green)
|
||||
, ForegroundMaxDistance(300.0f)
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
PrimaryActorTick.bTickEvenWhenPaused = true;
|
||||
|
||||
VRNotificationComponent = CreateDefaultSubobject<UVRNotificationsComponent>(TEXT("VRNotificationComponent"));
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
PlaneMeshComponent = CreateDefaultSubobject<UOculusXRMR_PlaneMeshComponent>(TEXT("PlaneMeshComponent"));
|
||||
PlaneMeshComponent->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
|
||||
PlaneMeshComponent->ResetRelativeTransform();
|
||||
PlaneMeshComponent->SetVisibility(false);
|
||||
#endif
|
||||
|
||||
OpaqueColoredMaterial = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), nullptr, TEXT("/OculusXR/Materials/OculusMR_OpaqueColoredMaterial")));
|
||||
if (!OpaqueColoredMaterial)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Invalid OpaqueColoredMaterial"));
|
||||
}
|
||||
|
||||
// Structure to hold one-time initialization
|
||||
struct FConstructorStatics
|
||||
{
|
||||
ConstructorHelpers::FObjectFinder<UTexture2D> WhiteSquareTexture;
|
||||
|
||||
FConstructorStatics()
|
||||
: WhiteSquareTexture(TEXT("/Engine/EngineResources/WhiteSquareTexture"))
|
||||
{
|
||||
}
|
||||
};
|
||||
static FConstructorStatics ConstructorStatics;
|
||||
|
||||
DefaultTexture_White = ConstructorStatics.WhiteSquareTexture.Object;
|
||||
check(DefaultTexture_White);
|
||||
|
||||
ForegroundCaptureActor = nullptr;
|
||||
|
||||
// Set the render targets for background and foreground to copies of the default texture
|
||||
#if PLATFORM_WINDOWS
|
||||
BackgroundRenderTargets.SetNum(1);
|
||||
ForegroundRenderTargets.SetNum(1);
|
||||
|
||||
BackgroundRenderTargets[0] = NewObject<UTextureRenderTarget2D>();
|
||||
BackgroundRenderTargets[0]->RenderTargetFormat = RTF_RGBA8_SRGB;
|
||||
|
||||
ForegroundRenderTargets[0] = NewObject<UTextureRenderTarget2D>();
|
||||
ForegroundRenderTargets[0]->RenderTargetFormat = RTF_RGBA8_SRGB;
|
||||
#elif PLATFORM_ANDROID
|
||||
BackgroundRenderTargets.SetNum(NumRTs);
|
||||
ForegroundRenderTargets.SetNum(NumRTs);
|
||||
AudioBuffers.SetNum(NumRTs);
|
||||
AudioTimes.SetNum(NumRTs);
|
||||
PoseTimes.SetNum(NumRTs);
|
||||
|
||||
for (unsigned int i = 0; i < NumRTs; ++i)
|
||||
{
|
||||
BackgroundRenderTargets[i] = NewObject<UTextureRenderTarget2D>();
|
||||
BackgroundRenderTargets[i]->RenderTargetFormat = RTF_RGBA8_SRGB;
|
||||
|
||||
ForegroundRenderTargets[i] = NewObject<UTextureRenderTarget2D>();
|
||||
ForegroundRenderTargets[i]->RenderTargetFormat = RTF_RGBA8_SRGB;
|
||||
|
||||
AudioTimes[i] = 0.0;
|
||||
PoseTimes[i] = 0.0;
|
||||
}
|
||||
|
||||
SyncId = -1;
|
||||
RenderedRTs = 0;
|
||||
CaptureIndex = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::BeginDestroy()
|
||||
{
|
||||
CloseTrackedCamera();
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
bool AOculusXRMR_CastingCameraActor::RefreshExternalCamera()
|
||||
{
|
||||
using namespace OculusXRHMD;
|
||||
if (MRState->TrackedCamera.Index >= 0)
|
||||
{
|
||||
int cameraCount;
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraCount(&cameraCount)))
|
||||
{
|
||||
cameraCount = 0;
|
||||
}
|
||||
if (MRState->TrackedCamera.Index >= cameraCount)
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("Invalid TrackedCamera Index"));
|
||||
return false;
|
||||
}
|
||||
FOculusXRHMD* OculusXRHMD = GEngine->XRSystem.IsValid() ? (FOculusXRHMD*)(GEngine->XRSystem->GetHMDDevice()) : nullptr;
|
||||
if (!OculusXRHMD)
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("Unable to retrieve OculusXRHMD"));
|
||||
return false;
|
||||
}
|
||||
ovrpResult result = ovrpSuccess;
|
||||
ovrpCameraExtrinsics cameraExtrinsics;
|
||||
result = FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraExtrinsics(MRState->TrackedCamera.Index, &cameraExtrinsics);
|
||||
if (OVRP_FAILURE(result))
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("FOculusXRHMDModule::GetPluginWrapper().GetExternalCameraExtrinsics failed"));
|
||||
return false;
|
||||
}
|
||||
MRState->TrackedCamera.AttachedTrackedDevice = OculusXRHMD::ToEOculusXRTrackedDeviceType(cameraExtrinsics.AttachedToNode);
|
||||
OculusXRHMD::FPose Pose;
|
||||
OculusXRHMD->ConvertPose(cameraExtrinsics.RelativePose, Pose);
|
||||
MRState->TrackedCamera.CalibratedRotation = Pose.Orientation.Rotator();
|
||||
MRState->TrackedCamera.CalibratedOffset = Pose.Position;
|
||||
MRState->TrackedCamera.UpdateTime = cameraExtrinsics.LastChangedTimeSeconds;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
SetupTrackedCamera();
|
||||
RequestTrackedCameraCalibration();
|
||||
SetupMRCScreen();
|
||||
|
||||
FScriptDelegate Delegate;
|
||||
Delegate.BindUFunction(this, FName(TEXT("OnHMDRecentered")));
|
||||
VRNotificationComponent->HMDRecenteredDelegate.Add(Delegate);
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
FAudioDeviceHandle AudioDevice = FAudioDevice::GetMainAudioDevice();
|
||||
if (AudioDevice.GetAudioDevice())
|
||||
{
|
||||
AudioDevice->StartRecording(nullptr, 0.1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::EndPlay(EEndPlayReason::Type Reason)
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
FAudioDeviceHandle AudioDevice = FAudioDevice::GetMainAudioDevice();
|
||||
if (AudioDevice.GetAudioDevice())
|
||||
{
|
||||
float NumChannels = 2;
|
||||
float SampleRate = AudioDevice->GetSampleRate();
|
||||
AudioDevice->StopRecording(nullptr, NumChannels, SampleRate);
|
||||
}
|
||||
#endif
|
||||
|
||||
VRNotificationComponent->HMDRecenteredDelegate.Remove(this, FName(TEXT("OnHMDRecentered")));
|
||||
|
||||
MRState->TrackingReferenceComponent = nullptr;
|
||||
|
||||
CloseMRCScreen();
|
||||
|
||||
CloseTrackedCamera();
|
||||
Super::EndPlay(Reason);
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (MRState->BindToTrackedCameraIndexRequested)
|
||||
{
|
||||
Execute_BindToTrackedCameraIndexIfAvailable();
|
||||
}
|
||||
|
||||
if (!RefreshExternalCamera())
|
||||
{
|
||||
CloseTrackedCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset capturing components if the composition method changes
|
||||
if (MRState->ChangeCameraStateRequested)
|
||||
{
|
||||
CloseTrackedCamera();
|
||||
CloseMRCScreen();
|
||||
SetupTrackedCamera();
|
||||
SetupMRCScreen();
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (MRSettings->GetCompositionMethod() == EOculusXRMR_CompositionMethod::ExternalComposition)
|
||||
#endif
|
||||
{
|
||||
if (ForegroundLayerBackgroundColor != MRSettings->BackdropColor)
|
||||
{
|
||||
ForegroundLayerBackgroundColor = MRSettings->BackdropColor;
|
||||
SetBackdropMaterialColor();
|
||||
}
|
||||
// Enable external composition post process based on setting
|
||||
bool bPostProcess = MRSettings->ExternalCompositionPostProcessEffects != EOculusXRMR_PostProcessEffects::PPE_Off;
|
||||
if (COverrideMixedRealityParametersVar.GetValueOnAnyThread() > 0)
|
||||
{
|
||||
bPostProcess = CEnableExternalCompositionPostProcess.GetValueOnAnyThread() > 0;
|
||||
}
|
||||
GetCaptureComponent2D()->ShowFlags.PostProcessing = bPostProcess;
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->ShowFlags.PostProcessing = bPostProcess;
|
||||
}
|
||||
}
|
||||
|
||||
if (TrackedCameraCalibrationRequired)
|
||||
{
|
||||
CalibrateTrackedCameraPose();
|
||||
}
|
||||
|
||||
UpdateTrackedCameraPosition();
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
RepositionPlaneMesh();
|
||||
#endif
|
||||
|
||||
UpdateRenderTargetSize();
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GEngine->XRSystem.IsValid() ? (OculusXRHMD::FOculusXRHMD*)(GEngine->XRSystem->GetHMDDevice()) : nullptr;
|
||||
if (OculusXRHMD)
|
||||
{
|
||||
ovrpPosef OvrpPose, OvrpHeadPose, OvrpLeftHandPose, OvrpRightHandPose;
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetTrackingTransformRelativePose(&OvrpPose, ovrpTrackingOrigin_Stage);
|
||||
OculusXRHMD::FPose StageToLocalPose;
|
||||
OculusXRHMD->ConvertPose(OvrpPose, StageToLocalPose);
|
||||
OculusXRHMD::FPose LocalToStagePose = StageToLocalPose.Inverse();
|
||||
|
||||
OculusXRHMD::FPose HeadPose;
|
||||
OculusXRHMD->GetCurrentPose(OculusXRHMD::ToExternalDeviceId(ovrpNode_Head), HeadPose.Orientation, HeadPose.Position);
|
||||
HeadPose = LocalToStagePose * HeadPose;
|
||||
OculusXRHMD->ConvertPose(HeadPose, OvrpHeadPose);
|
||||
|
||||
OculusXRHMD::FPose LeftHandPose;
|
||||
OculusXRHMD->GetCurrentPose(OculusXRHMD::ToExternalDeviceId(ovrpNode_HandLeft), HeadPose.Orientation, HeadPose.Position);
|
||||
LeftHandPose = LocalToStagePose * LeftHandPose;
|
||||
OculusXRHMD->ConvertPose(LeftHandPose, OvrpLeftHandPose);
|
||||
|
||||
OculusXRHMD::FPose RightHandPose;
|
||||
OculusXRHMD->GetCurrentPose(OculusXRHMD::ToExternalDeviceId(ovrpNode_HandRight), HeadPose.Orientation, HeadPose.Position);
|
||||
RightHandPose = LocalToStagePose * RightHandPose;
|
||||
OculusXRHMD->ConvertPose(RightHandPose, OvrpRightHandPose);
|
||||
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SetHeadsetControllerPose(OvrpHeadPose, OvrpLeftHandPose, OvrpRightHandPose);
|
||||
}
|
||||
|
||||
// Alternate foreground and background captures by nulling the capture component texture target
|
||||
if (GetCaptureComponent2D()->IsVisible())
|
||||
{
|
||||
GetCaptureComponent2D()->SetVisibility(false);
|
||||
|
||||
// Encode a texture the frame before we render to it again to ensure completed render at the cost of latency
|
||||
unsigned int EncodeIndex = (CaptureIndex + 1) % NumRTs;
|
||||
|
||||
// Skip encoding for the first few frames before they have completed rendering
|
||||
if (RenderedRTs > EncodeIndex)
|
||||
{
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_SyncMrcFrame(SyncId);
|
||||
|
||||
int NumChannels = 2;
|
||||
double AudioTime = AudioTimes[EncodeIndex];
|
||||
void* BackgroundTexture;
|
||||
void* ForegroundTexture;
|
||||
|
||||
if (IsVulkanPlatform(GMaxRHIShaderPlatform))
|
||||
{
|
||||
ExecuteOnRenderThread([this, EncodeIndex, &BackgroundTexture, &ForegroundTexture]() {
|
||||
ExecuteOnRHIThread([this, EncodeIndex, &BackgroundTexture, &ForegroundTexture]() {
|
||||
// The Vulkan RHI's implementation of GetNativeResource is different and returns the VkImage cast
|
||||
// as a void* instead of a pointer to the VkImage, so we need this workaround
|
||||
BackgroundTexture = (void*)BackgroundRenderTargets[EncodeIndex]->GetResource()->TextureRHI->GetNativeResource();
|
||||
ForegroundTexture = (void*)ForegroundRenderTargets[EncodeIndex]->GetResource()->TextureRHI->GetNativeResource();
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteOnRenderThread([this, EncodeIndex, &BackgroundTexture, &ForegroundTexture]() {
|
||||
ExecuteOnRHIThread([this, EncodeIndex, &BackgroundTexture, &ForegroundTexture]() {
|
||||
BackgroundTexture = *((void**)BackgroundRenderTargets[EncodeIndex]->GetResource()->TextureRHI->GetNativeResource());
|
||||
ForegroundTexture = *((void**)ForegroundRenderTargets[EncodeIndex]->GetResource()->TextureRHI->GetNativeResource());
|
||||
});
|
||||
});
|
||||
}
|
||||
FOculusXRHMDModule::GetPluginWrapper().Media_EncodeMrcFrameDualTexturesWithPoseTime(BackgroundTexture, ForegroundTexture, AudioBuffers[EncodeIndex].GetData(), AudioBuffers[EncodeIndex].Num() * sizeof(float), NumChannels, AudioTime, PoseTimes[CaptureIndex], &SyncId);
|
||||
}
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->SetVisibility(true);
|
||||
}
|
||||
else if (ForegroundCaptureActor && ForegroundCaptureActor->GetCaptureComponent2D()->IsVisible())
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->SetVisibility(false);
|
||||
|
||||
// Increment scene captures to next texture
|
||||
CaptureIndex = (CaptureIndex + 1) % NumRTs;
|
||||
GetCaptureComponent2D()->TextureTarget = BackgroundRenderTargets[CaptureIndex];
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->TextureTarget = ForegroundRenderTargets[CaptureIndex];
|
||||
GetCaptureComponent2D()->SetVisibility(true);
|
||||
|
||||
FAudioDeviceHandle AudioDevice = FAudioDevice::GetMainAudioDevice();
|
||||
if (AudioDevice.GetAudioDevice())
|
||||
{
|
||||
float NumChannels, SampleRate;
|
||||
NumChannels = 2;
|
||||
SampleRate = AudioDevice->GetSampleRate();
|
||||
AudioBuffers[CaptureIndex] = AudioDevice->StopRecording(nullptr, NumChannels, SampleRate);
|
||||
AudioTimes[CaptureIndex] = AudioDevice->GetAudioTime();
|
||||
// UE_LOG(LogMR, Error, TEXT("SampleRate: %f, NumChannels: %f, Time: %f, Buffer Length: %d, Buffer: %p"), SampleRate, NumChannels, AudioDevice->GetAudioTime(), AudioBuffers[EncodeIndex].Num(), AudioBuffers[EncodeIndex].GetData());
|
||||
AudioDevice->StartRecording(nullptr, 0.1);
|
||||
}
|
||||
|
||||
// PoseTimes[CaptureIndex] = MRState->TrackedCamera.UpdateTime;
|
||||
|
||||
// Increment this counter for the initial cycle through "swapchain"
|
||||
if (RenderedRTs < NumRTs)
|
||||
{
|
||||
RenderedRTs++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::Execute_BindToTrackedCameraIndexIfAvailable()
|
||||
{
|
||||
if (!MRState->BindToTrackedCameraIndexRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FOculusXRTrackedCamera TempTrackedCamera;
|
||||
if (MRSettings->GetBindToTrackedCameraIndex() >= 0)
|
||||
{
|
||||
TArray<FOculusXRTrackedCamera> TrackedCameras;
|
||||
UOculusXRMRFunctionLibrary::GetAllTrackedCamera(TrackedCameras);
|
||||
int i;
|
||||
for (i = 0; i < TrackedCameras.Num(); ++i)
|
||||
{
|
||||
if (TrackedCameras[i].Index == MRSettings->GetBindToTrackedCameraIndex())
|
||||
{
|
||||
TempTrackedCamera = TrackedCameras[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == TrackedCameras.Num())
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to find TrackedCamera at index %d, use TempTrackedCamera"), MRSettings->GetBindToTrackedCameraIndex());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("BindToTrackedCameraIndex == %d, use TempTrackedCamera"), MRSettings->GetBindToTrackedCameraIndex());
|
||||
}
|
||||
|
||||
MRState->TrackedCamera = TempTrackedCamera;
|
||||
if (MRState->TrackedCamera.Index < 0)
|
||||
{
|
||||
SetTrackedCameraUserPoseWithCameraTransform();
|
||||
}
|
||||
|
||||
MRState->BindToTrackedCameraIndexRequested = false;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::RequestTrackedCameraCalibration()
|
||||
{
|
||||
TrackedCameraCalibrationRequired = true;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::CalibrateTrackedCameraPose()
|
||||
{
|
||||
SetTrackedCameraInitialPoseWithPlayerTransform();
|
||||
HasTrackedCameraCalibrationCalibrated = true;
|
||||
TrackedCameraCalibrationRequired = false;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetTrackedCameraInitialPoseWithPlayerTransform()
|
||||
{
|
||||
using namespace OculusXRHMD;
|
||||
|
||||
FOculusXRHMD* OculusXRHMD = GEngine->XRSystem.IsValid() ? (FOculusXRHMD*)(GEngine->XRSystem->GetHMDDevice()) : nullptr;
|
||||
if (!OculusXRHMD)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to retrieve OculusXRHMD"));
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraTrackedObjectPose;
|
||||
if (!GetCameraTrackedObjectPoseInTrackingSpace(OculusXRHMD, MRState->TrackedCamera, CameraTrackedObjectPose))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraPose = CameraTrackedObjectPose * FPose(MRState->TrackedCamera.CalibratedRotation.Quaternion(), MRState->TrackedCamera.CalibratedOffset);
|
||||
CameraPose = CameraPose * FPose(MRState->TrackedCamera.UserRotation.Quaternion(), MRState->TrackedCamera.UserOffset);
|
||||
|
||||
FQuat TROrientation;
|
||||
FVector TRLocation;
|
||||
FRotator TRRotation;
|
||||
if (!UOculusXRMRFunctionLibrary::GetTrackingReferenceLocationAndRotationInWorldSpace(MRState->TrackingReferenceComponent, TRLocation, TRRotation))
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Could not get player position"));
|
||||
return;
|
||||
}
|
||||
|
||||
TROrientation = TRRotation.Quaternion();
|
||||
FPose FinalPose = FPose(TROrientation, TRLocation) * CameraPose;
|
||||
|
||||
InitialCameraAbsoluteOrientation = FinalPose.Orientation;
|
||||
InitialCameraAbsolutePosition = FinalPose.Position;
|
||||
InitialCameraRelativeOrientation = CameraPose.Orientation;
|
||||
InitialCameraRelativePosition = CameraPose.Position;
|
||||
|
||||
GetCaptureComponent2D()->FOVAngle = MRState->TrackedCamera.FieldOfView;
|
||||
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->FOVAngle = MRState->TrackedCamera.FieldOfView;
|
||||
}
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetTrackedCameraUserPoseWithCameraTransform()
|
||||
{
|
||||
using namespace OculusXRHMD;
|
||||
|
||||
FOculusXRHMD* OculusXRHMD = GEngine->XRSystem.IsValid() ? (FOculusXRHMD*)(GEngine->XRSystem->GetHMDDevice()) : nullptr;
|
||||
if (!OculusXRHMD)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to retrieve OculusXRHMD"));
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraTrackedObjectPose;
|
||||
if (!GetCameraTrackedObjectPoseInTrackingSpace(OculusXRHMD, MRState->TrackedCamera, CameraTrackedObjectPose))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraPose = CameraTrackedObjectPose * FPose(MRState->TrackedCamera.CalibratedRotation.Quaternion(), MRState->TrackedCamera.CalibratedOffset);
|
||||
|
||||
FQuat TROrientation;
|
||||
FVector TRLocation;
|
||||
FRotator TRRotation;
|
||||
if (!UOculusXRMRFunctionLibrary::GetTrackingReferenceLocationAndRotationInWorldSpace(MRState->TrackingReferenceComponent, TRLocation, TRRotation))
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Could not get player position"));
|
||||
return;
|
||||
}
|
||||
TROrientation = TRRotation.Quaternion();
|
||||
FPose PlayerPose(TROrientation, TRLocation);
|
||||
FPose CurrentCameraPose = PlayerPose * CameraPose;
|
||||
|
||||
FPose ExpectedCameraPose(GetCaptureComponent2D()->GetComponentRotation().Quaternion(), GetCaptureComponent2D()->GetComponentLocation());
|
||||
FPose UserPose = CurrentCameraPose.Inverse() * ExpectedCameraPose;
|
||||
|
||||
MRState->TrackedCamera.UserRotation = UserPose.Orientation.Rotator();
|
||||
MRState->TrackedCamera.UserOffset = UserPose.Position;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::UpdateTrackedCameraPosition()
|
||||
{
|
||||
check(HasTrackedCameraCalibrationCalibrated);
|
||||
|
||||
using namespace OculusXRHMD;
|
||||
|
||||
FOculusXRHMD* OculusXRHMD = GEngine->XRSystem.IsValid() ? (FOculusXRHMD*)(GEngine->XRSystem->GetHMDDevice()) : nullptr;
|
||||
if (!OculusXRHMD)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Unable to retrieve OculusXRHMD"));
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraTrackedObjectPose;
|
||||
if (!GetCameraTrackedObjectPoseInTrackingSpace(OculusXRHMD, MRState->TrackedCamera, CameraTrackedObjectPose))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FPose CameraTrackingSpacePose = FPose(MRState->TrackedCamera.CalibratedRotation.Quaternion(), MRState->TrackedCamera.CalibratedOffset);
|
||||
#if PLATFORM_ANDROID
|
||||
ovrpPosef OvrpPose;
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetTrackingTransformRelativePose(&OvrpPose, ovrpTrackingOrigin_Stage);
|
||||
FPose StageToLocalPose;
|
||||
OculusXRHMD->ConvertPose(OvrpPose, StageToLocalPose);
|
||||
CameraTrackingSpacePose = StageToLocalPose * CameraTrackingSpacePose;
|
||||
#endif
|
||||
FPose CameraPose = CameraTrackedObjectPose * CameraTrackingSpacePose;
|
||||
CameraPose = CameraPose * FPose(MRState->TrackedCamera.UserRotation.Quaternion(), MRState->TrackedCamera.UserOffset);
|
||||
CameraPose.Position = CameraPose.Position * MRState->ScalingFactor;
|
||||
|
||||
float Distance = 0.0f;
|
||||
if (MRSettings->ClippingReference == EOculusXRMR_ClippingReference::CR_TrackingReference)
|
||||
{
|
||||
Distance = -FVector::DotProduct(CameraPose.Orientation.GetForwardVector().GetSafeNormal2D(), CameraPose.Position);
|
||||
}
|
||||
else if (MRSettings->ClippingReference == EOculusXRMR_ClippingReference::CR_Head)
|
||||
{
|
||||
FQuat HeadOrientation;
|
||||
FVector HeadPosition;
|
||||
OculusXRHMD->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, HeadOrientation, HeadPosition);
|
||||
FVector HeadToCamera = HeadPosition - CameraPose.Position;
|
||||
Distance = FVector::DotProduct(CameraPose.Orientation.GetForwardVector().GetSafeNormal2D(), HeadToCamera);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkNoEntry();
|
||||
}
|
||||
ForegroundMaxDistance = FMath::Max(Distance, GMinClipZ);
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->MaxViewDistanceOverride = ForegroundMaxDistance;
|
||||
}
|
||||
|
||||
FPose FinalPose;
|
||||
FQuat TROrientation;
|
||||
FVector TRLocation;
|
||||
FRotator TRRotation;
|
||||
if (!UOculusXRMRFunctionLibrary::GetTrackingReferenceLocationAndRotationInWorldSpace(MRState->TrackingReferenceComponent, TRLocation, TRRotation))
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("Could not get player position"));
|
||||
return;
|
||||
}
|
||||
|
||||
TROrientation = TRRotation.Quaternion();
|
||||
FinalPose = FPose(TROrientation, TRLocation) * CameraPose;
|
||||
|
||||
FTransform FinalTransform(FinalPose.Orientation, FinalPose.Position);
|
||||
RootComponent->SetWorldTransform(FinalTransform);
|
||||
GetCaptureComponent2D()->FOVAngle = MRState->TrackedCamera.FieldOfView;
|
||||
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->FOVAngle = MRState->TrackedCamera.FieldOfView;
|
||||
}
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::InitializeStates(UOculusXRMR_Settings* MRSettingsIn, UOculusXRMR_State* MRStateIn)
|
||||
{
|
||||
MRSettings = MRSettingsIn;
|
||||
MRState = MRStateIn;
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetupTrackedCamera()
|
||||
{
|
||||
if (!RefreshExternalCamera())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RequestTrackedCameraCalibration();
|
||||
|
||||
// Unset this flag before we can return
|
||||
MRState->ChangeCameraStateRequested = false;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (MRSettings->GetCompositionMethod() == EOculusXRMR_CompositionMethod::ExternalComposition)
|
||||
{
|
||||
SetupBackdropMaterialInstance();
|
||||
}
|
||||
|
||||
RepositionPlaneMesh();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetBackdropMaterialColor()
|
||||
{
|
||||
if (BackdropMaterialInstance)
|
||||
{
|
||||
BackdropMaterialInstance->SetVectorParameterValue(FName(TEXT("Color")), GetForegroundLayerBackgroundColor());
|
||||
}
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetupBackdropMaterialInstance()
|
||||
{
|
||||
if (!BackdropMaterialInstance && OpaqueColoredMaterial)
|
||||
{
|
||||
BackdropMaterialInstance = UMaterialInstanceDynamic::Create(OpaqueColoredMaterial, this);
|
||||
BackdropMaterialInstance->SetScalarParameterValue(FName("Opacity"), 0.0f);
|
||||
}
|
||||
PlaneMeshComponent->SetMaterial(0, BackdropMaterialInstance);
|
||||
SetBackdropMaterialColor();
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::RepositionPlaneMesh()
|
||||
{
|
||||
FVector PlaneCenter = FVector::ForwardVector * ForegroundMaxDistance;
|
||||
FVector PlaneUp = FVector::UpVector;
|
||||
FVector PlaneNormal = -FVector::ForwardVector;
|
||||
int ViewWidth = MRSettings->bUseTrackedCameraResolution ? MRState->TrackedCamera.SizeX : MRSettings->WidthPerView;
|
||||
int ViewHeight = MRSettings->bUseTrackedCameraResolution ? MRState->TrackedCamera.SizeY : MRSettings->HeightPerView;
|
||||
float Width = ForegroundMaxDistance * FMath::Tan(FMath::DegreesToRadians(GetCaptureComponent2D()->FOVAngle) * 0.5f) * 2.0f;
|
||||
float Height = Width * ViewHeight / ViewWidth;
|
||||
FVector2D PlaneSize = FVector2D(Width, Height);
|
||||
PlaneMeshComponent->Place(PlaneCenter, PlaneUp, PlaneNormal, PlaneSize);
|
||||
PlaneMeshComponent->ResetRelativeTransform();
|
||||
PlaneMeshComponent->SetVisibility(true);
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::OnHMDRecentered()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
RefreshBoundaryMesh();
|
||||
#endif
|
||||
RequestTrackedCameraCalibration();
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::RefreshBoundaryMesh()
|
||||
{
|
||||
RefreshBoundaryMeshCounter = 3;
|
||||
}
|
||||
|
||||
void BuildProjectionMatrix(float YMultiplier, float FOV, float FarClipPlane, FMatrix& ProjectionMatrix)
|
||||
{
|
||||
if (FarClipPlane < GNearClippingPlane)
|
||||
{
|
||||
FarClipPlane = GNearClippingPlane;
|
||||
}
|
||||
|
||||
if ((int32)ERHIZBuffer::IsInverted)
|
||||
{
|
||||
ProjectionMatrix = FReversedZPerspectiveMatrix(
|
||||
FOV,
|
||||
FOV,
|
||||
1.0f,
|
||||
YMultiplier,
|
||||
GNearClippingPlane,
|
||||
FarClipPlane);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectionMatrix = FPerspectiveMatrix(
|
||||
FOV,
|
||||
FOV,
|
||||
1.0f,
|
||||
YMultiplier,
|
||||
GNearClippingPlane,
|
||||
FarClipPlane);
|
||||
}
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::UpdateRenderTargetSize()
|
||||
{
|
||||
int ViewWidth = MRSettings->bUseTrackedCameraResolution ? MRState->TrackedCamera.SizeX : MRSettings->WidthPerView;
|
||||
int ViewHeight = MRSettings->bUseTrackedCameraResolution ? MRState->TrackedCamera.SizeY : MRSettings->HeightPerView;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
BackgroundRenderTargets[0]->ResizeTarget(ViewWidth, ViewHeight);
|
||||
if (ForegroundRenderTargets[0])
|
||||
{
|
||||
ForegroundRenderTargets[0]->ResizeTarget(ViewWidth, ViewHeight);
|
||||
}
|
||||
#endif
|
||||
#if PLATFORM_ANDROID
|
||||
FIntPoint CameraTargetSize = FIntPoint(ViewWidth, ViewHeight);
|
||||
float FOV = GetCaptureComponent2D()->FOVAngle * (float)PI / 360.0f;
|
||||
|
||||
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().Media_GetMrcFrameSize(&ViewWidth, &ViewHeight)))
|
||||
{
|
||||
// Frame size is doublewide, so divide by 2
|
||||
ViewWidth /= 2;
|
||||
|
||||
for (unsigned int i = 0; i < NumRTs; ++i)
|
||||
{
|
||||
BackgroundRenderTargets[i]->ResizeTarget(ViewWidth, ViewHeight);
|
||||
if (ForegroundRenderTargets[i])
|
||||
{
|
||||
ForegroundRenderTargets[i]->ResizeTarget(ViewWidth, ViewHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Use custom projection matrix for far clip plane and to use camera aspect ratio instead of rendertarget aspect ratio
|
||||
float YMultiplier = (float)CameraTargetSize.X / (float)CameraTargetSize.Y;
|
||||
GetCaptureComponent2D()->bUseCustomProjectionMatrix = true;
|
||||
BuildProjectionMatrix(YMultiplier, FOV, GNearClippingPlane, GetCaptureComponent2D()->CustomProjectionMatrix);
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->bUseCustomProjectionMatrix = true;
|
||||
BuildProjectionMatrix(YMultiplier, FOV, ForegroundMaxDistance, ForegroundCaptureActor->GetCaptureComponent2D()->CustomProjectionMatrix);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::SetupMRCScreen()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
OculusXRHMD::FSpectatorScreenController* SpecScreen = nullptr;
|
||||
IHeadMountedDisplay* HMD = GEngine->XRSystem.IsValid() ? GEngine->XRSystem->GetHMDDevice() : nullptr;
|
||||
if (HMD)
|
||||
{
|
||||
SpecScreen = (OculusXRHMD::FSpectatorScreenController*)HMD->GetSpectatorScreenController();
|
||||
}
|
||||
if (SpecScreen)
|
||||
{
|
||||
#endif
|
||||
UpdateRenderTargetSize();
|
||||
|
||||
// LDR for gamma correction and post process
|
||||
GetCaptureComponent2D()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
|
||||
|
||||
// Render scene capture 2D output to spectator screen
|
||||
GetCaptureComponent2D()->TextureTarget = BackgroundRenderTargets[0];
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
if (MRSettings->GetCompositionMethod() == EOculusXRMR_CompositionMethod::ExternalComposition)
|
||||
#endif
|
||||
{
|
||||
ForegroundCaptureActor = GetWorld()->SpawnActor<ASceneCapture2D>();
|
||||
|
||||
// LDR for gamma correction and post process
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
|
||||
#if PLATFORM_ANDROID
|
||||
// Start with foreground capture actor off on android
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->SetVisibility(false);
|
||||
#endif
|
||||
|
||||
// Don't render anything past the foreground for performance
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->MaxViewDistanceOverride = ForegroundMaxDistance;
|
||||
|
||||
ForegroundCaptureActor->GetCaptureComponent2D()->TextureTarget = ForegroundRenderTargets[0];
|
||||
#if PLATFORM_WINDOWS
|
||||
// Render use split foreground/background rendering to spectator screen
|
||||
SpecScreen->SetMRForeground(ForegroundRenderTargets[0]);
|
||||
SpecScreen->SetMRBackground(BackgroundRenderTargets[0]);
|
||||
SpecScreen->SetMRSpectatorScreenMode(OculusXRHMD::EMRSpectatorScreenMode::ExternalComposition);
|
||||
|
||||
// Set the plane mesh to only render to foreground target
|
||||
PlaneMeshComponent->SetPlaneRenderTarget(ForegroundRenderTargets[0]);
|
||||
#endif
|
||||
// Set foreground capture to match background capture
|
||||
ForegroundCaptureActor->AttachToActor(this, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true));
|
||||
}
|
||||
#if PLATFORM_WINDOWS
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogMR, Error, TEXT("Cannot find spectator screen"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::CloseMRCScreen()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
OculusXRHMD::FSpectatorScreenController* SpecScreen = nullptr;
|
||||
IHeadMountedDisplay* HMD = GEngine->XRSystem.IsValid() ? GEngine->XRSystem->GetHMDDevice() : nullptr;
|
||||
if (HMD)
|
||||
{
|
||||
SpecScreen = (OculusXRHMD::FSpectatorScreenController*)HMD->GetSpectatorScreenController();
|
||||
}
|
||||
// Restore original spectator screen mode
|
||||
if (SpecScreen)
|
||||
{
|
||||
SpecScreen->SetMRSpectatorScreenMode(OculusXRHMD::EMRSpectatorScreenMode::Default);
|
||||
SpecScreen->SetMRForeground(nullptr);
|
||||
SpecScreen->SetMRBackground(nullptr);
|
||||
}
|
||||
#endif
|
||||
if (ForegroundCaptureActor)
|
||||
{
|
||||
ForegroundCaptureActor->Destroy();
|
||||
ForegroundCaptureActor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AOculusXRMR_CastingCameraActor::CloseTrackedCamera()
|
||||
{
|
||||
if (PlaneMeshComponent)
|
||||
{
|
||||
PlaneMeshComponent->SetVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,131 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Engine/SceneCapture2D.h"
|
||||
#include "AudioResampler.h"
|
||||
#include "AudioDefines.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "AudioMixer.h"
|
||||
#include "OculusXRMR_CastingCameraActor.generated.h"
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
#define MRC_SWAPCHAIN_LENGTH 3
|
||||
#endif
|
||||
|
||||
class UOculusXRMR_PlaneMeshComponent;
|
||||
class UMaterial;
|
||||
class AOculusXRMR_BoundaryActor;
|
||||
class UTextureRenderTarget2D;
|
||||
class UOculusXRMR_Settings;
|
||||
class UOculusXRMR_State;
|
||||
|
||||
/**
|
||||
* The camera actor in the level that tracks the binded physical camera in game
|
||||
*/
|
||||
UCLASS(ClassGroup = OculusXRMR, NotPlaceable, NotBlueprintable)
|
||||
class AOculusXRMR_CastingCameraActor : public ASceneCapture2D
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AOculusXRMR_CastingCameraActor(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** Initialize the MRC settings and states */
|
||||
void InitializeStates(UOculusXRMR_Settings* MRSettingsIn, UOculusXRMR_State* MRStateIn);
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(EEndPlayReason::Type Reason) override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
UPROPERTY()
|
||||
class UVRNotificationsComponent* VRNotificationComponent;
|
||||
|
||||
UPROPERTY()
|
||||
UOculusXRMR_PlaneMeshComponent* PlaneMeshComponent;
|
||||
|
||||
UPROPERTY()
|
||||
UMaterial* OpaqueColoredMaterial;
|
||||
|
||||
UPROPERTY()
|
||||
UMaterialInstanceDynamic* BackdropMaterialInstance;
|
||||
|
||||
UPROPERTY()
|
||||
class UTexture2D* DefaultTexture_White;
|
||||
|
||||
bool TrackedCameraCalibrationRequired;
|
||||
bool HasTrackedCameraCalibrationCalibrated;
|
||||
FQuat InitialCameraAbsoluteOrientation;
|
||||
FVector InitialCameraAbsolutePosition;
|
||||
FQuat InitialCameraRelativeOrientation;
|
||||
FVector InitialCameraRelativePosition;
|
||||
|
||||
int32 RefreshBoundaryMeshCounter;
|
||||
|
||||
private:
|
||||
/** Move the casting camera to follow the tracking reference (i.e. player) */
|
||||
void RequestTrackedCameraCalibration();
|
||||
|
||||
bool RefreshExternalCamera();
|
||||
|
||||
void CalibrateTrackedCameraPose();
|
||||
void SetTrackedCameraUserPoseWithCameraTransform();
|
||||
void SetTrackedCameraInitialPoseWithPlayerTransform();
|
||||
void UpdateTrackedCameraPosition();
|
||||
|
||||
/** Initialize the tracked physical camera */
|
||||
void SetupTrackedCamera();
|
||||
|
||||
/** Close the tracked physical camera */
|
||||
void CloseTrackedCamera();
|
||||
|
||||
void OnHMDRecentered();
|
||||
|
||||
const FColor& GetForegroundLayerBackgroundColor() const { return ForegroundLayerBackgroundColor; }
|
||||
|
||||
void SetBackdropMaterialColor();
|
||||
void SetupBackdropMaterialInstance();
|
||||
void RepositionPlaneMesh();
|
||||
void RefreshBoundaryMesh();
|
||||
void UpdateRenderTargetSize();
|
||||
void SetupMRCScreen();
|
||||
void CloseMRCScreen();
|
||||
|
||||
void Execute_BindToTrackedCameraIndexIfAvailable();
|
||||
|
||||
FColor ForegroundLayerBackgroundColor;
|
||||
float ForegroundMaxDistance;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<UTextureRenderTarget2D*> BackgroundRenderTargets;
|
||||
|
||||
UPROPERTY()
|
||||
ASceneCapture2D* ForegroundCaptureActor;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<UTextureRenderTarget2D*> ForegroundRenderTargets;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<double> PoseTimes;
|
||||
|
||||
UPROPERTY()
|
||||
UOculusXRMR_Settings* MRSettings;
|
||||
|
||||
UPROPERTY()
|
||||
UOculusXRMR_State* MRState;
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
TArray<Audio::AlignedFloatBuffer> AudioBuffers;
|
||||
TArray<double> AudioTimes;
|
||||
|
||||
int SyncId;
|
||||
|
||||
const unsigned int NumRTs = MRC_SWAPCHAIN_LENGTH;
|
||||
unsigned int RenderedRTs;
|
||||
unsigned int CaptureIndex;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,270 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRMR_PlaneMeshComponent.h"
|
||||
#include "RenderingThread.h"
|
||||
#include "RenderResource.h"
|
||||
#include "PrimitiveViewRelevance.h"
|
||||
#include "PrimitiveSceneProxy.h"
|
||||
#include "VertexFactory.h"
|
||||
#include "Engine/CollisionProfile.h"
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "LocalVertexFactory.h"
|
||||
#include "SceneManagement.h"
|
||||
#include "DynamicMeshBuilder.h"
|
||||
#include "EngineGlobals.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "MaterialShared.h"
|
||||
#include "SceneInterface.h"
|
||||
#include "TextureResource.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Materials/MaterialRenderProxy.h"
|
||||
|
||||
/** Scene proxy */
|
||||
class FOculusXRMR_PlaneMeshSceneProxy : public FPrimitiveSceneProxy
|
||||
{
|
||||
public:
|
||||
FOculusXRMR_PlaneMeshSceneProxy(UOculusXRMR_PlaneMeshComponent* Component, UTextureRenderTarget2D* RenderTarget)
|
||||
: FPrimitiveSceneProxy(Component)
|
||||
, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
|
||||
, PlaneRenderTarget(RenderTarget)
|
||||
{
|
||||
const FColor VertexColor(255, 255, 255);
|
||||
|
||||
const int32 NumTris = Component->CustomMeshTris.Num();
|
||||
Vertices.AddUninitialized(NumTris * 3);
|
||||
Indices.AddUninitialized(NumTris * 3);
|
||||
// Add each triangle to the vertex/index buffer
|
||||
for (int32 TriIdx = 0; TriIdx < NumTris; TriIdx++)
|
||||
{
|
||||
FOculusXRMR_PlaneMeshTriangle& Tri = Component->CustomMeshTris[TriIdx];
|
||||
|
||||
const FVector Edge01 = (Tri.Vertex1 - Tri.Vertex0);
|
||||
const FVector Edge02 = (Tri.Vertex2 - Tri.Vertex0);
|
||||
|
||||
const FVector TangentX = Edge01.GetSafeNormal();
|
||||
const FVector TangentZ = (Edge02 ^ Edge01).GetSafeNormal();
|
||||
const FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal();
|
||||
|
||||
FDynamicMeshVertex Vert;
|
||||
|
||||
Vert.Color = VertexColor;
|
||||
Vert.SetTangents((FVector3f)TangentX, (FVector3f)TangentY, (FVector3f)TangentZ);
|
||||
|
||||
Vert.Position = (FVector3f)Tri.Vertex0;
|
||||
Vert.TextureCoordinate[0] = FVector2f(Tri.UV0); // LWC_TODO: Precision loss
|
||||
Vertices[TriIdx * 3 + 0] = Vert;
|
||||
Indices[TriIdx * 3 + 0] = TriIdx * 3 + 0;
|
||||
|
||||
Vert.Position = (FVector3f)Tri.Vertex1;
|
||||
Vert.TextureCoordinate[0] = FVector2f(Tri.UV1); // LWC_TODO: Precision loss
|
||||
Vertices[TriIdx * 3 + 1] = Vert;
|
||||
Indices[TriIdx * 3 + 1] = TriIdx * 3 + 1;
|
||||
|
||||
Vert.Position = (FVector3f)Tri.Vertex2;
|
||||
Vert.TextureCoordinate[0] = FVector2f(Tri.UV2); // LWC_TODO: Precision loss
|
||||
Vertices[TriIdx * 3 + 2] = Vert;
|
||||
Indices[TriIdx * 3 + 2] = TriIdx * 3 + 2;
|
||||
}
|
||||
|
||||
// Grab material
|
||||
Material = Component->GetMaterial(0);
|
||||
if (Material == nullptr)
|
||||
{
|
||||
Material = UMaterial::GetDefaultMaterial(MD_Surface);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~FOculusXRMR_PlaneMeshSceneProxy()
|
||||
{
|
||||
}
|
||||
|
||||
SIZE_T GetTypeHash() const override
|
||||
{
|
||||
static size_t UniquePointer;
|
||||
return reinterpret_cast<size_t>(&UniquePointer);
|
||||
}
|
||||
|
||||
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
|
||||
{
|
||||
QUICK_SCOPE_CYCLE_COUNTER(STAT_OculusXRMR_PlaneMeshSceneProxy_GetDynamicMeshElements);
|
||||
|
||||
// the mesh is only visible inside the CastingViewport, and the Full CastingLayer (the Composition mode)
|
||||
if (PlaneRenderTarget && ViewFamily.RenderTarget == PlaneRenderTarget->GetRenderTargetResource())
|
||||
{
|
||||
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
|
||||
|
||||
FMaterialRenderProxy* MaterialProxy = nullptr;
|
||||
if (bWireframe)
|
||||
{
|
||||
auto WireframeMaterialInstance = new FColoredMaterialRenderProxy(
|
||||
GEngine->WireframeMaterial->GetRenderProxy(),
|
||||
FLinearColor(0, 0.5f, 1.f));
|
||||
|
||||
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
|
||||
MaterialProxy = WireframeMaterialInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
MaterialProxy = Material->GetRenderProxy();
|
||||
}
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||||
{
|
||||
if (VisibilityMap & (1 << ViewIndex))
|
||||
{
|
||||
const FSceneView* View = Views[ViewIndex];
|
||||
|
||||
FDynamicMeshBuilder DynamicMeshBuilder(View->GetFeatureLevel());
|
||||
DynamicMeshBuilder.AddVertices(Vertices);
|
||||
DynamicMeshBuilder.AddTriangles(Indices);
|
||||
|
||||
DynamicMeshBuilder.GetMesh(GetLocalToWorld(), MaterialProxy, SDPG_World, true, false, ViewIndex, Collector);
|
||||
|
||||
// -- Original draw code for reference --
|
||||
// FMeshBatch& Mesh = Collector.AllocateMesh();
|
||||
// FMeshBatchElement& BatchElement = Mesh.Elements[0];
|
||||
// BatchElement.IndexBuffer = &IndexBuffer;
|
||||
// Mesh.bWireframe = bWireframe;
|
||||
// Mesh.VertexFactory = &VertexFactory;
|
||||
// Mesh.MaterialRenderProxy = MaterialProxy;
|
||||
// BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, DrawsVelocity());
|
||||
// BatchElement.FirstIndex = 0;
|
||||
// BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
|
||||
// BatchElement.MinVertexIndex = 0;
|
||||
// BatchElement.MaxVertexIndex = VertexBuffer.Vertices.Num() - 1;
|
||||
// Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
|
||||
// Mesh.Type = PT_TriangleList;
|
||||
// Mesh.DepthPriorityGroup = SDPG_World;
|
||||
// Mesh.bCanApplyViewModeOverrides = false;
|
||||
// Collector.AddMesh(ViewIndex, Mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
||||
{
|
||||
FPrimitiveViewRelevance Result;
|
||||
Result.bDrawRelevance = IsShown(View);
|
||||
Result.bShadowRelevance = IsShadowCast(View);
|
||||
Result.bDynamicRelevance = true;
|
||||
Result.bRenderInMainPass = ShouldRenderInMainPass();
|
||||
Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
|
||||
Result.bRenderCustomDepth = ShouldRenderCustomDepth();
|
||||
MaterialRelevance.SetPrimitiveViewRelevance(Result);
|
||||
return Result;
|
||||
}
|
||||
|
||||
virtual bool CanBeOccluded() const override
|
||||
{
|
||||
return !MaterialRelevance.bDisableDepthTest;
|
||||
}
|
||||
|
||||
virtual uint32 GetMemoryFootprint(void) const override { return (sizeof(*this) + GetAllocatedSize()); }
|
||||
|
||||
uint32 GetAllocatedSize(void) const { return (FPrimitiveSceneProxy::GetAllocatedSize()); }
|
||||
|
||||
private:
|
||||
UMaterialInterface* Material;
|
||||
TArray<FDynamicMeshVertex> Vertices;
|
||||
TArray<uint32> Indices;
|
||||
FMaterialRelevance MaterialRelevance;
|
||||
UTextureRenderTarget2D* PlaneRenderTarget;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UOculusXRMR_PlaneMeshComponent::UOculusXRMR_PlaneMeshComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
SetCollisionProfileName(UCollisionProfile::BlockAllDynamic_ProfileName);
|
||||
|
||||
bRenderCustomDepth = true;
|
||||
}
|
||||
|
||||
bool UOculusXRMR_PlaneMeshComponent::SetCustomMeshTriangles(const TArray<FOculusXRMR_PlaneMeshTriangle>& Triangles)
|
||||
{
|
||||
CustomMeshTris = Triangles;
|
||||
|
||||
// Need to recreate scene proxy to send it over
|
||||
MarkRenderStateDirty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UOculusXRMR_PlaneMeshComponent::AddCustomMeshTriangles(const TArray<FOculusXRMR_PlaneMeshTriangle>& Triangles)
|
||||
{
|
||||
CustomMeshTris.Append(Triangles);
|
||||
|
||||
// Need to recreate scene proxy to send it over
|
||||
MarkRenderStateDirty();
|
||||
}
|
||||
|
||||
void UOculusXRMR_PlaneMeshComponent::ClearCustomMeshTriangles()
|
||||
{
|
||||
CustomMeshTris.Reset();
|
||||
|
||||
// Need to recreate scene proxy to send it over
|
||||
MarkRenderStateDirty();
|
||||
}
|
||||
|
||||
void UOculusXRMR_PlaneMeshComponent::Place(const FVector& Center, const FVector& Up, const FVector& Normal, const FVector2D& Size)
|
||||
{
|
||||
FVector Right = FVector::CrossProduct(Up, Normal);
|
||||
|
||||
FVector Up_N = Up.GetUnsafeNormal();
|
||||
FVector Right_N = Right.GetUnsafeNormal();
|
||||
|
||||
FVector V0 = Center - Right_N * Size.X * 0.5f - Up_N * Size.Y * 0.5f;
|
||||
FVector2D UV0(1, 1);
|
||||
FVector V1 = Center + Right_N * Size.X * 0.5f - Up_N * Size.Y * 0.5f;
|
||||
FVector2D UV1(0, 1);
|
||||
FVector V2 = Center - Right_N * Size.X * 0.5f + Up_N * Size.Y * 0.5f;
|
||||
FVector2D UV2(1, 0);
|
||||
FVector V3 = Center + Right_N * Size.X * 0.5f + Up_N * Size.Y * 0.5f;
|
||||
FVector2D UV3(0, 0);
|
||||
|
||||
FOculusXRMR_PlaneMeshTriangle Tri0, Tri1;
|
||||
Tri0.Vertex0 = V1;
|
||||
Tri0.UV0 = UV1;
|
||||
Tri0.Vertex1 = V0;
|
||||
Tri0.UV1 = UV0;
|
||||
Tri0.Vertex2 = V2;
|
||||
Tri0.UV2 = UV2;
|
||||
Tri1.Vertex0 = V1;
|
||||
Tri1.UV0 = UV1;
|
||||
Tri1.Vertex1 = V2;
|
||||
Tri1.UV1 = UV2;
|
||||
Tri1.Vertex2 = V3;
|
||||
Tri1.UV2 = UV3;
|
||||
|
||||
SetCustomMeshTriangles({ Tri0, Tri1 });
|
||||
}
|
||||
|
||||
FPrimitiveSceneProxy* UOculusXRMR_PlaneMeshComponent::CreateSceneProxy()
|
||||
{
|
||||
FPrimitiveSceneProxy* Proxy = nullptr;
|
||||
if (CustomMeshTris.Num() > 0)
|
||||
{
|
||||
Proxy = new FOculusXRMR_PlaneMeshSceneProxy(this, PlaneRenderTarget);
|
||||
}
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
int32 UOculusXRMR_PlaneMeshComponent::GetNumMaterials() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
FBoxSphereBounds UOculusXRMR_PlaneMeshComponent::CalcBounds(const FTransform& LocalToWorld) const
|
||||
{
|
||||
FBoxSphereBounds NewBounds;
|
||||
NewBounds.Origin = FVector::ZeroVector;
|
||||
NewBounds.BoxExtent = FVector(HALF_WORLD_MAX, HALF_WORLD_MAX, HALF_WORLD_MAX);
|
||||
NewBounds.SphereRadius = FMath::Sqrt(3.0f * FMath::Square(HALF_WORLD_MAX));
|
||||
return NewBounds;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Components/MeshComponent.h"
|
||||
#include "OculusXRMR_PlaneMeshComponent.generated.h"
|
||||
|
||||
class FPrimitiveSceneProxy;
|
||||
class UTextureRenderTarget2D;
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FOculusXRMR_PlaneMeshTriangle
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FVector Vertex0 = FVector(0.0f);
|
||||
|
||||
UPROPERTY()
|
||||
FVector2D UV0 = FVector2D(0.0f);
|
||||
|
||||
UPROPERTY()
|
||||
FVector Vertex1 = FVector(0.0f);
|
||||
|
||||
UPROPERTY()
|
||||
FVector2D UV1 = FVector2D(0.0f);
|
||||
|
||||
UPROPERTY()
|
||||
FVector Vertex2 = FVector(0.0f);
|
||||
|
||||
UPROPERTY()
|
||||
FVector2D UV2 = FVector2D(0.0f);
|
||||
};
|
||||
|
||||
/** Component that allows you to specify custom triangle mesh geometry */
|
||||
UCLASS(hidecategories = (Object, LOD, Physics, Collision), editinlinenew, ClassGroup = Rendering, NotPlaceable, NotBlueprintable)
|
||||
class UOculusXRMR_PlaneMeshComponent : public UMeshComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
/** Set the geometry to use on this triangle mesh */
|
||||
UFUNCTION(BlueprintCallable, Category = "Components|CustomMesh")
|
||||
bool SetCustomMeshTriangles(const TArray<FOculusXRMR_PlaneMeshTriangle>& Triangles);
|
||||
|
||||
/** Add to the geometry to use on this triangle mesh. This may cause an allocation. Use SetCustomMeshTriangles() instead when possible to reduce allocations. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Components|CustomMesh")
|
||||
void AddCustomMeshTriangles(const TArray<FOculusXRMR_PlaneMeshTriangle>& Triangles);
|
||||
|
||||
/** Removes all geometry from this triangle mesh. Does not deallocate memory, allowing new geometry to reuse the existing allocation. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Components|CustomMesh")
|
||||
void ClearCustomMeshTriangles();
|
||||
|
||||
void Place(const FVector& Center, const FVector& Up, const FVector& Normal, const FVector2D& Size);
|
||||
|
||||
void SetPlaneRenderTarget(UTextureRenderTarget2D* RT) { PlaneRenderTarget = RT; }
|
||||
|
||||
private:
|
||||
//~ Begin UPrimitiveComponent Interface.
|
||||
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
|
||||
//~ End UPrimitiveComponent Interface.
|
||||
|
||||
//~ Begin UMeshComponent Interface.
|
||||
virtual int32 GetNumMaterials() const override;
|
||||
//~ End UMeshComponent Interface.
|
||||
|
||||
//~ Begin USceneComponent Interface.
|
||||
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
|
||||
//~ Begin USceneComponent Interface.
|
||||
|
||||
TArray<FOculusXRMR_PlaneMeshTriangle> CustomMeshTris;
|
||||
|
||||
UTextureRenderTarget2D* PlaneRenderTarget;
|
||||
|
||||
friend class FOculusXRMR_PlaneMeshSceneProxy;
|
||||
};
|
||||
142
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMR_Settings.cpp
Normal file
142
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMR_Settings.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRMR_Settings.h"
|
||||
#include "OculusXRMRPrivate.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
UOculusXRMR_Settings::UOculusXRMR_Settings(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, ClippingReference(EOculusXRMR_ClippingReference::CR_Head)
|
||||
, bUseTrackedCameraResolution(true)
|
||||
, WidthPerView(960)
|
||||
, HeightPerView(540)
|
||||
, CastingLatency(0.0f)
|
||||
, BackdropColor(FColor::Green)
|
||||
, ExternalCompositionPostProcessEffects(EOculusXRMR_PostProcessEffects::PPE_Off)
|
||||
, bIsCasting(false)
|
||||
, CompositionMethod(EOculusXRMR_CompositionMethod::ExternalComposition)
|
||||
, BindToTrackedCameraIndex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::SetCompositionMethod(EOculusXRMR_CompositionMethod val)
|
||||
{
|
||||
if (CompositionMethod == val)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto old = CompositionMethod;
|
||||
CompositionMethod = val;
|
||||
CompositionMethodChangeDelegate.Execute(old, val);
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::SetCapturingCamera(EOculusXRMR_CameraDeviceEnum val)
|
||||
{
|
||||
// deprecated
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::SetIsCasting(bool val)
|
||||
{
|
||||
if (bIsCasting == val)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto old = bIsCasting;
|
||||
bIsCasting = val;
|
||||
IsCastingChangeDelegate.Execute(old, val);
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::BindToTrackedCameraIndexIfAvailable(int InTrackedCameraIndex)
|
||||
{
|
||||
if (BindToTrackedCameraIndex == InTrackedCameraIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto old = BindToTrackedCameraIndex;
|
||||
BindToTrackedCameraIndex = InTrackedCameraIndex;
|
||||
TrackedCameraIndexChangeDelegate.Execute(old, InTrackedCameraIndex);
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::LoadFromIni()
|
||||
{
|
||||
if (!GConfig)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("GConfig is NULL"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Flushing the GEngineIni is necessary to get the settings reloaded at the runtime, but the manual flushing
|
||||
// could cause an assert when loading audio settings if launching through editor at the 2nd time. Disabled temporarily.
|
||||
// GConfig->Flush(true, GEngineIni);
|
||||
|
||||
const TCHAR* OculusXRMRSettings = TEXT("Oculus.Settings.MixedReality");
|
||||
bool v;
|
||||
float f;
|
||||
int32 i;
|
||||
|
||||
FColor color;
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("CompositionMethod"), i, GEngineIni))
|
||||
{
|
||||
SetCompositionMethod((EOculusXRMR_CompositionMethod)i);
|
||||
}
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("ClippingReference"), i, GEngineIni))
|
||||
{
|
||||
ClippingReference = (EOculusXRMR_ClippingReference)i;
|
||||
}
|
||||
if (GConfig->GetBool(OculusXRMRSettings, TEXT("bUseTrackedCameraResolution"), v, GEngineIni))
|
||||
{
|
||||
bUseTrackedCameraResolution = v;
|
||||
}
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("WidthPerView"), i, GEngineIni))
|
||||
{
|
||||
WidthPerView = i;
|
||||
}
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("HeightPerView"), i, GEngineIni))
|
||||
{
|
||||
HeightPerView = i;
|
||||
}
|
||||
if (GConfig->GetFloat(OculusXRMRSettings, TEXT("CastingLatency"), f, GEngineIni))
|
||||
{
|
||||
CastingLatency = f;
|
||||
}
|
||||
if (GConfig->GetColor(OculusXRMRSettings, TEXT("BackdropColor"), color, GEngineIni))
|
||||
{
|
||||
BackdropColor = color;
|
||||
}
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("BindToTrackedCameraIndex"), i, GEngineIni))
|
||||
{
|
||||
BindToTrackedCameraIndexIfAvailable(i);
|
||||
}
|
||||
if (GConfig->GetInt(OculusXRMRSettings, TEXT("ExternalCompositionPostProcessEffects"), i, GEngineIni))
|
||||
{
|
||||
ExternalCompositionPostProcessEffects = (EOculusXRMR_PostProcessEffects)i;
|
||||
}
|
||||
|
||||
UE_LOG(LogMR, Log, TEXT("MixedReality settings loaded from Engine.ini"));
|
||||
}
|
||||
|
||||
void UOculusXRMR_Settings::SaveToIni() const
|
||||
{
|
||||
if (!GConfig)
|
||||
{
|
||||
UE_LOG(LogMR, Warning, TEXT("GConfig is NULL"));
|
||||
return;
|
||||
}
|
||||
|
||||
const TCHAR* OculusXRMRSettings = TEXT("Oculus.Settings.MixedReality");
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("CompositionMethod"), (int32)CompositionMethod, GEngineIni);
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("ClippingReference"), (int32)ClippingReference, GEngineIni);
|
||||
GConfig->SetBool(OculusXRMRSettings, TEXT("bUseTrackedCameraResolution"), bUseTrackedCameraResolution, GEngineIni);
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("WidthPerView"), WidthPerView, GEngineIni);
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("HeightPerView"), HeightPerView, GEngineIni);
|
||||
GConfig->SetFloat(OculusXRMRSettings, TEXT("CastingLatency"), CastingLatency, GEngineIni);
|
||||
GConfig->SetColor(OculusXRMRSettings, TEXT("BackdropColor"), BackdropColor, GEngineIni);
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("BindToTrackedCameraIndex"), (int32)BindToTrackedCameraIndex, GEngineIni);
|
||||
GConfig->SetInt(OculusXRMRSettings, TEXT("ExternalCompositionPostProcessEffects"), (int32)ExternalCompositionPostProcessEffects, GEngineIni);
|
||||
|
||||
GConfig->Flush(false, GEngineIni);
|
||||
|
||||
UE_LOG(LogMR, Log, TEXT("MixedReality settings saved to Engine.ini"));
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#include "OculusXRMR_State.h"
|
||||
#include "OculusXRMRFunctionLibrary.h"
|
||||
|
||||
UOculusXRMR_State::UOculusXRMR_State(const FObjectInitializer& ObjectInitializer)
|
||||
: TrackedCamera()
|
||||
, TrackingReferenceComponent(nullptr)
|
||||
, ScalingFactor(1.0f)
|
||||
, ChangeCameraStateRequested(false)
|
||||
, BindToTrackedCameraIndexRequested(false)
|
||||
{
|
||||
}
|
||||
117
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMR_State.h
Normal file
117
Plugins/MetaXR/Source/OculusXRMR/Private/OculusXRMR_State.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "OculusXRFunctionLibrary.h"
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
|
||||
#include "OculusXRMR_State.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct FOculusXRTrackedCamera
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/** >=0: the index of the external camera
|
||||
* -1: not bind to any external camera (and would be setup to match the manual CastingCameraActor placement)
|
||||
*/
|
||||
UPROPERTY()
|
||||
int32 Index;
|
||||
|
||||
/** The external camera name set through the CameraTool */
|
||||
UPROPERTY()
|
||||
FString Name;
|
||||
|
||||
/** The time that this camera was updated */
|
||||
UPROPERTY()
|
||||
double UpdateTime;
|
||||
|
||||
/** The horizontal FOV, in degrees */
|
||||
UPROPERTY(meta = (UIMin = "5.0", UIMax = "170", ClampMin = "0.001", ClampMax = "360.0", Units = deg))
|
||||
float FieldOfView;
|
||||
|
||||
/** The resolution of the camera frame */
|
||||
UPROPERTY()
|
||||
int32 SizeX;
|
||||
|
||||
/** The resolution of the camera frame */
|
||||
UPROPERTY()
|
||||
int32 SizeY;
|
||||
|
||||
/** The tracking node the external camera is bound to */
|
||||
UPROPERTY()
|
||||
EOculusXRTrackedDeviceType AttachedTrackedDevice;
|
||||
|
||||
/** The relative pose of the camera to the attached tracking device */
|
||||
UPROPERTY()
|
||||
FRotator CalibratedRotation;
|
||||
|
||||
/** The relative pose of the camera to the attached tracking device */
|
||||
UPROPERTY()
|
||||
FVector CalibratedOffset;
|
||||
|
||||
/** (optional) The user pose is provided to fine tuning the relative camera pose at the run-time */
|
||||
UPROPERTY()
|
||||
FRotator UserRotation;
|
||||
|
||||
/** (optional) The user pose is provided to fine tuning the relative camera pose at the run-time */
|
||||
UPROPERTY()
|
||||
FVector UserOffset;
|
||||
|
||||
/** The raw pose of the camera to the attached tracking device (Deprecated) */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "All camera pose info is now in stage space, do not use raw pose data."))
|
||||
FRotator RawRotation_DEPRECATED;
|
||||
|
||||
/** The raw pose of the camera to the attached tracking device (Deprecated) */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "All camera pose info is now in stage space, do not use raw pose data."))
|
||||
FVector RawOffset_DEPRECATED;
|
||||
|
||||
FOculusXRTrackedCamera()
|
||||
: Index(-1)
|
||||
, Name(TEXT("Unknown"))
|
||||
, UpdateTime(0.0f)
|
||||
, FieldOfView(90.0f)
|
||||
, SizeX(1280)
|
||||
, SizeY(720)
|
||||
, AttachedTrackedDevice(EOculusXRTrackedDeviceType::None)
|
||||
, CalibratedRotation(EForceInit::ForceInitToZero)
|
||||
, CalibratedOffset(EForceInit::ForceInitToZero)
|
||||
, UserRotation(EForceInit::ForceInitToZero)
|
||||
, UserOffset(EForceInit::ForceInitToZero)
|
||||
, RawRotation_DEPRECATED(EForceInit::ForceInitToZero)
|
||||
, RawOffset_DEPRECATED(EForceInit::ForceInitToZero)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Object to hold the state of MR capture and capturing camera
|
||||
*/
|
||||
UCLASS(ClassGroup = OculusXRMR, NotPlaceable, NotBlueprintable)
|
||||
class UOculusXRMR_State : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UOculusXRMR_State(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
UPROPERTY()
|
||||
FOculusXRTrackedCamera TrackedCamera;
|
||||
|
||||
// Component at the tracking origin that the camera calibration is applied to
|
||||
UPROPERTY()
|
||||
class USceneComponent* TrackingReferenceComponent;
|
||||
|
||||
// A multiplier on the camera distance, should be based on the scaling of the player component
|
||||
UPROPERTY()
|
||||
double ScalingFactor;
|
||||
|
||||
/** Flag indicating a change in the tracked camera state for the camera actor to consume */
|
||||
UPROPERTY()
|
||||
bool ChangeCameraStateRequested;
|
||||
|
||||
/** Flag indicating a change in the tracked camera index for the camera actor to consume */
|
||||
UPROPERTY()
|
||||
bool BindToTrackedCameraIndexRequested;
|
||||
};
|
||||
38
Plugins/MetaXR/Source/OculusXRMR/Public/IOculusXRMRModule.h
Normal file
38
Plugins/MetaXR/Source/OculusXRMR/Public/IOculusXRMRModule.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
// Oculus support is not available on Windows XP
|
||||
#define OCULUS_MR_SUPPORTED_PLATFORMS ((PLATFORM_WINDOWS && WINVER > 0x0502) || PLATFORM_ANDROID)
|
||||
|
||||
/**
|
||||
* The public interface to this module. In most cases, this interface is only public to sibling modules
|
||||
* within this plugin.
|
||||
*/
|
||||
class IOculusXRMRModule : public IModuleInterface
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline IOculusXRMRModule& Get()
|
||||
{
|
||||
return FModuleManager::GetModuleChecked<IOculusXRMRModule>("OculusXRMR");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static inline bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("OculusXRMR");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "OculusXRMRFunctionLibrary.generated.h"
|
||||
|
||||
class USceneComponent;
|
||||
class UOculusXRMR_Settings;
|
||||
struct FOculusXRTrackedCamera;
|
||||
|
||||
namespace OculusXRHMD
|
||||
{
|
||||
class FOculusXRHMD;
|
||||
}
|
||||
|
||||
UCLASS()
|
||||
class OCULUSXRMR_API UOculusXRMRFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
// Get the OculusXRMR settings object
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR", meta = (DisplayName = "Get Oculus MR Settings"))
|
||||
static UOculusXRMR_Settings* GetOculusXRMRSettings();
|
||||
|
||||
// Get the component that the OculusXRMR camera is tracking. When this is null, the camera will track the player pawn.
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR")
|
||||
static USceneComponent* GetTrackingReferenceComponent();
|
||||
|
||||
// Set the component for the OculusXRMR camera to track. If this is set to null, the camera will track the player pawn.
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR")
|
||||
static bool SetTrackingReferenceComponent(USceneComponent* Component);
|
||||
|
||||
// Get the scaling factor for the MRC configuration. Returns 0 if not available.
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR", meta = (DisplayName = "Get MRC Scaling Factor"))
|
||||
static float GetMrcScalingFactor();
|
||||
|
||||
// Set the scaling factor for the MRC configuration. This should be a positive value set to the same scaling as the VR player pawn so that the game capture and camera video are aligned.
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR", meta = (DisplayName = "Set MRC Scaling Factor"))
|
||||
static bool SetMrcScalingFactor(float ScalingFactor = 1.0f);
|
||||
|
||||
// Check if MRC is enabled
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR")
|
||||
static bool IsMrcEnabled();
|
||||
|
||||
// Check if MRC is enabled and actively capturing
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|MR")
|
||||
static bool IsMrcActive();
|
||||
|
||||
public:
|
||||
static class TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> GetTrackingSystem();
|
||||
|
||||
/** Retrieve an array of all (calibrated) tracked cameras which were calibrated through the CameraTool */
|
||||
static void GetAllTrackedCamera(TArray<FOculusXRTrackedCamera>& TrackedCameras, bool bCalibratedOnly = true);
|
||||
|
||||
static bool GetTrackingReferenceLocationAndRotationInWorldSpace(USceneComponent* TrackingReferenceComponent, FVector& TRLocation, FRotator& TRRotation);
|
||||
};
|
||||
175
Plugins/MetaXR/Source/OculusXRMR/Public/OculusXRMR_Settings.h
Normal file
175
Plugins/MetaXR/Source/OculusXRMR/Public/OculusXRMR_Settings.h
Normal file
@@ -0,0 +1,175 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "UObject/ObjectMacros.h"
|
||||
|
||||
#include "OculusXRMR_Settings.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRMR_CameraDeviceEnum : uint8 // Deprecated
|
||||
{
|
||||
CD_None_DEPRECATED UMETA(DisplayName = "None"),
|
||||
CD_WebCamera0_DEPRECATED UMETA(DisplayName = "Web Camera 0"),
|
||||
CD_WebCamera1_DEPRECATED UMETA(DisplayName = "Web Camera 1"),
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRMR_ClippingReference : uint8
|
||||
{
|
||||
CR_TrackingReference UMETA(DisplayName = "Tracking Reference"),
|
||||
CR_Head UMETA(DisplayName = "Head"),
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRMR_PostProcessEffects : uint8
|
||||
{
|
||||
PPE_Off UMETA(DisplayName = "Off"),
|
||||
PPE_On UMETA(DisplayName = "On"),
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRMR_CompositionMethod : uint8
|
||||
{
|
||||
/* Generate both foreground and background views for compositing with 3rd-party software like OBS. */
|
||||
ExternalComposition UMETA(DisplayName = "External Composition"),
|
||||
/* (Deprecated) Composite the camera stream directly to the output with the proper depth.*/
|
||||
DirectComposition_DEPRECATED UMETA(DisplayName = "Direct Composition (DEPRECATED)")
|
||||
};
|
||||
|
||||
UCLASS(ClassGroup = OculusXRMR, Blueprintable)
|
||||
class UOculusXRMR_Settings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UOculusXRMR_Settings(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/** Specify the distance to the camera which divide the background and foreground in MxR casting.
|
||||
* Set it to CR_TrackingReference to use the distance to the Tracking Reference, which works better
|
||||
* in the stationary experience. Set it to CR_Head would use the distance to the HMD, which works better
|
||||
* in the room scale experience.
|
||||
*/
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
EOculusXRMR_ClippingReference ClippingReference;
|
||||
|
||||
/** The casting viewports would use the same resolution of the camera which used in the calibration process. */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
bool bUseTrackedCameraResolution;
|
||||
|
||||
/** When bUseTrackedCameraResolution is false, the width of each casting viewport */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
int WidthPerView;
|
||||
|
||||
/** When bUseTrackedCameraResolution is false, the height of each casting viewport */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
int HeightPerView;
|
||||
|
||||
/** When CompositionMethod is External Composition, the latency of the casting output which could be adjusted to
|
||||
* match the camera latency in the external composition application */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite, meta = (UIMin = "0.0", UIMax = "0.1"))
|
||||
float CastingLatency;
|
||||
|
||||
/** When CompositionMethod is External Composition, the color of the backdrop in the foreground view */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
FColor BackdropColor;
|
||||
|
||||
/** When CompositionMethod is Direct Composition, you could adjust this latency to delay the virtual
|
||||
* hand movement by a small amount of time to match the camera latency */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
float HandPoseStateLatency_DEPRECATED;
|
||||
|
||||
/** [Green-screen removal] Chroma Key Color. Apply when CompositionMethod is DirectComposition */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
FColor ChromaKeyColor_DEPRECATED;
|
||||
|
||||
/** [Green-screen removal] Chroma Key Similarity. Apply when CompositionMethod is DirectComposition */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
float ChromaKeySimilarity_DEPRECATED;
|
||||
|
||||
/** [Green-screen removal] Chroma Key Smooth Range. Apply when CompositionMethod is DirectComposition */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
float ChromaKeySmoothRange_DEPRECATED;
|
||||
|
||||
/** [Green-screen removal] Chroma Key Spill Range. Apply when CompositionMethod is DirectComposition */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
float ChromaKeySpillRange_DEPRECATED;
|
||||
|
||||
/** Set the amount of post process effects in the MR view for external composition */
|
||||
UPROPERTY(Category = MetaXR, EditAnywhere, BlueprintReadWrite)
|
||||
EOculusXRMR_PostProcessEffects ExternalCompositionPostProcessEffects;
|
||||
|
||||
/** ExternalComposition: The casting window includes the background and foreground view
|
||||
* DirectComposition: The game scene would be composited with the camera frame directly
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
EOculusXRMR_CompositionMethod GetCompositionMethod() { return CompositionMethod; }
|
||||
|
||||
/** ExternalComposition: The casting window includes the background and foreground view
|
||||
* DirectComposition: The game scene would be composited with the camera frame directly
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
void SetCompositionMethod(EOculusXRMR_CompositionMethod val);
|
||||
|
||||
/** When CompositionMethod is DirectComposition, the physical camera device which provide the frame */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR, meta = (DeprecatedFunction, DeprecationMessage = "Direct Composition deprecated."))
|
||||
EOculusXRMR_CameraDeviceEnum GetCapturingCamera() { return EOculusXRMR_CameraDeviceEnum::CD_None_DEPRECATED; }
|
||||
|
||||
/** When CompositionMethod is DirectComposition, the physical camera device which provide the frame */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR, meta = (DeprecatedFunction, DeprecationMessage = "Direct Composition deprecated."))
|
||||
void SetCapturingCamera(EOculusXRMR_CameraDeviceEnum val);
|
||||
|
||||
/** Is MRC on and off */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
bool GetIsCasting() { return bIsCasting; }
|
||||
|
||||
/** Turns MRC on and off */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
void SetIsCasting(bool val);
|
||||
|
||||
/** Bind the casting camera to the calibrated external camera.
|
||||
* (Requires a calibrated external camera)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
void BindToTrackedCameraIndexIfAvailable(int InTrackedCameraIndex);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
int GetBindToTrackedCameraIndex() { return BindToTrackedCameraIndex; }
|
||||
|
||||
/** Load settings from the config file */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
void LoadFromIni();
|
||||
|
||||
/** Save settings to the config file */
|
||||
UFUNCTION(BlueprintCallable, Category = MetaXR)
|
||||
void SaveToIni() const;
|
||||
|
||||
private:
|
||||
/** Turns MRC on and off (does not get saved to or loaded from ini) */
|
||||
UPROPERTY()
|
||||
bool bIsCasting;
|
||||
|
||||
/** ExternalComposition: The casting window includes the background and foreground view
|
||||
* DirectComposition: The game scene would be composited with the camera frame directly
|
||||
*/
|
||||
UPROPERTY()
|
||||
EOculusXRMR_CompositionMethod CompositionMethod;
|
||||
|
||||
/** When CompositionMethod is DirectComposition, the physical camera device which provide the frame */
|
||||
UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Direct Composition deprecated."))
|
||||
EOculusXRMR_CameraDeviceEnum CapturingCamera_DEPRECATED;
|
||||
|
||||
/** Tracked camera that we want to bind the in-game MR camera to*/
|
||||
int BindToTrackedCameraIndex;
|
||||
|
||||
DECLARE_DELEGATE_TwoParams(OnCompositionMethodChangeDelegate, EOculusXRMR_CompositionMethod, EOculusXRMR_CompositionMethod);
|
||||
DECLARE_DELEGATE_TwoParams(OnBooleanSettingChangeDelegate, bool, bool);
|
||||
DECLARE_DELEGATE_TwoParams(OnIntegerSettingChangeDelegate, int, int);
|
||||
|
||||
OnIntegerSettingChangeDelegate TrackedCameraIndexChangeDelegate;
|
||||
OnCompositionMethodChangeDelegate CompositionMethodChangeDelegate;
|
||||
OnBooleanSettingChangeDelegate IsCastingChangeDelegate;
|
||||
|
||||
// Give the OculusXRMR module access to the delegates so that
|
||||
friend class FOculusXRMRModule;
|
||||
};
|
||||
Reference in New Issue
Block a user