Config for building for Quest

This commit is contained in:
2024-05-29 11:53:41 +03:00
parent 15cbcf8752
commit 0db31c34d1
353 changed files with 74095 additions and 3 deletions

View File

@@ -0,0 +1,35 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
namespace UnrealBuildTool.Rules
{
public class OculusXRAnchors : ModuleRules
{
public OculusXRAnchors(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"OculusXRHMD",
"OVRPluginXR",
"ProceduralMeshComponent",
});
PrivateIncludePaths.AddRange(
new string[] {
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
"OculusXRHMD/Private",
});
PublicIncludePaths.AddRange(
new string[] {
"Runtime/Engine/Classes/Components",
});
}
}
}

View File

@@ -0,0 +1,225 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRHMD.h"
#include "OculusXRSpatialAnchorComponent.h"
#include "OculusXRAnchorsPrivate.h"
#include "OculusXRRoomLayoutManager.h"
#include "OculusXRAnchorManager.h"
#include "Kismet/BlueprintFunctionLibrary.h"
AActor* UOculusXRAnchorBPFunctionLibrary::SpawnActorWithAnchorHandle(UObject* WorldContextObject, FOculusXRUInt64 Handle, FOculusXRUUID UUID, EOculusXRSpaceStorageLocation Location, UClass* ActorClass,
AActor* Owner, APawn* Instigator, ESpawnActorCollisionHandlingMethod CollisionHandlingMethod)
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.Owner = Owner;
SpawnInfo.Instigator = Instigator;
SpawnInfo.ObjectFlags |= RF_Transient;
SpawnInfo.SpawnCollisionHandlingOverride = CollisionHandlingMethod;
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (World == nullptr)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid WorldContext Object for SpawnActorWithAnchorHandle."));
return nullptr;
}
AActor* NewSpatialAnchorActor = World->SpawnActor(ActorClass, nullptr, nullptr, SpawnInfo);
if (NewSpatialAnchorActor == nullptr)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to spawn Actor in SpawnActorWithAnchorHandle"));
return nullptr;
}
UOculusXRSpatialAnchorComponent* SpatialAnchorComponent = NewSpatialAnchorActor->FindComponentByClass<UOculusXRSpatialAnchorComponent>();
if (SpatialAnchorComponent == nullptr)
{
SpatialAnchorComponent = Cast<UOculusXRSpatialAnchorComponent>(NewSpatialAnchorActor->AddComponentByClass(UOculusXRSpatialAnchorComponent::StaticClass(), false, FTransform::Identity, false));
}
if (!IsValid(SpatialAnchorComponent))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to find or spawn Spatial Anchor component in SpawnActorWithAnchorHandle"));
return nullptr;
}
SpatialAnchorComponent->SetHandle(Handle);
SpatialAnchorComponent->SetUUID(UUID);
SpatialAnchorComponent->SetStoredLocation(Location, true);
return NewSpatialAnchorActor;
}
AActor* UOculusXRAnchorBPFunctionLibrary::SpawnActorWithAnchorQueryResults(UObject* WorldContextObject, const FOculusXRSpaceQueryResult& QueryResult, UClass* ActorClass, AActor* Owner, APawn* Instigator, ESpawnActorCollisionHandlingMethod CollisionHandlingMethod)
{
return SpawnActorWithAnchorHandle(WorldContextObject, QueryResult.Space, QueryResult.UUID, QueryResult.Location, ActorClass, Owner, Instigator, CollisionHandlingMethod);
}
bool UOculusXRAnchorBPFunctionLibrary::GetAnchorComponentStatus(AActor* TargetActor, EOculusXRSpaceComponentType ComponentType, bool& bIsEnabled)
{
UOculusXRAnchorComponent* AnchorComponent = Cast<UOculusXRAnchorComponent>(TargetActor->GetComponentByClass(UOculusXRAnchorComponent::StaticClass()));
if (!IsValid(AnchorComponent))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid Anchor Component provided to GetAnchorComponentStatus"));
bIsEnabled = false;
return false;
}
bool bOutIsEnabled = false;
bool bIsChangePending = false;
EOculusXRAnchorResult::Type AnchorResult;
bool bDidCallStart = OculusXRAnchors::FOculusXRAnchors::GetAnchorComponentStatus(AnchorComponent, ComponentType, bOutIsEnabled, bIsChangePending, AnchorResult);
if (!bDidCallStart)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start call to internal GetAnchorComponentStatus"));
bIsEnabled = false;
return false;
}
bIsEnabled = bOutIsEnabled;
return bIsEnabled;
}
bool UOculusXRAnchorBPFunctionLibrary::GetAnchorTransformByHandle(const FOculusXRUInt64& Handle, FTransform& OutTransform)
{
FOculusXRAnchorLocationFlags AnchorFlags(0);
return TryGetAnchorTransformByHandle(Handle, OutTransform, AnchorFlags);
}
bool UOculusXRAnchorBPFunctionLibrary::TryGetAnchorTransformByHandle(const FOculusXRUInt64& Handle, FTransform& OutTransform, FOculusXRAnchorLocationFlags& OutLocationFlags)
{
OculusXRHMD::FOculusXRHMD* OutHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (!OutHMD)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve OculusXRHMD, cannot calculate anchor transform."));
return false;
}
ovrpTrackingOrigin ovrpOrigin = ovrpTrackingOrigin_EyeLevel;
const bool bTrackingOriginSuccess = FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetTrackingOriginType2(&ovrpOrigin));
if (!bTrackingOriginSuccess)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to get tracking origin, cannot calculate anchor transform."));
return false;
}
OutTransform = FTransform::Identity;
OutLocationFlags = FOculusXRAnchorLocationFlags(0);
const ovrpUInt64 ovrpSpace = Handle.GetValue();
ovrpSpaceLocationf ovrpSpaceLocation;
const bool bSuccess = FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().LocateSpace2(&ovrpSpaceLocation, &ovrpSpace, ovrpOrigin));
if (bSuccess)
{
OutLocationFlags = FOculusXRAnchorLocationFlags(ovrpSpaceLocation.locationFlags);
if (OutLocationFlags.IsValid())
{
OculusXRHMD::FPose Pose;
OutHMD->ConvertPose(ovrpSpaceLocation.pose, Pose);
const FTransform trackingToWorld = OutHMD->GetLastTrackingToWorld();
OutTransform.SetLocation(trackingToWorld.TransformPosition(Pose.Position));
OutTransform.SetRotation(FRotator(trackingToWorld.TransformRotation(FQuat(Pose.Orientation))).Quaternion());
}
else
{
return false;
}
}
return bSuccess;
}
FString UOculusXRAnchorBPFunctionLibrary::AnchorHandleToString(const FOculusXRUInt64 Value)
{
return FString::Printf(TEXT("%llu"), Value.Value);
}
FString UOculusXRAnchorBPFunctionLibrary::AnchorUUIDToString(const FOculusXRUUID& Value)
{
return Value.ToString();
}
FOculusXRUUID UOculusXRAnchorBPFunctionLibrary::StringToAnchorUUID(const FString& Value)
{
// Static size for the max length of the string, two chars per hex digit, 16 digits.
checkf(Value.Len() == 32, TEXT("'%s' is not a valid UUID"), *Value);
ovrpUuid newID;
HexToBytes(Value, newID.data);
return FOculusXRUUID(newID.data);
}
bool UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(EOculusXRAnchorResult::Type result)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return OVRP_SUCCESS(result);
#endif
return false;
}
const UOculusXRBaseAnchorComponent* UOculusXRAnchorBPFunctionLibrary::GetAnchorComponent(const FOculusXRSpaceQueryResult& QueryResult, EOculusXRSpaceComponentType ComponentType, UObject* Outer)
{
switch (ComponentType)
{
case EOculusXRSpaceComponentType::Locatable:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRLocatableAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::ScenePlane:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRPlaneAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::SceneVolume:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRVolumeAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::SemanticClassification:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRSemanticClassificationAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::RoomLayout:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRRoomLayoutAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::SpaceContainer:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRSpaceContainerAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::Sharable:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRSharableAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::Storable:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRStorableAnchorComponent>(QueryResult.Space.Value, Outer);
case EOculusXRSpaceComponentType::TriangleMesh:
return UOculusXRBaseAnchorComponent::FromSpace<UOculusXRTriangleMeshAnchorComponent>(QueryResult.Space.Value, Outer);
default:
return nullptr;
}
}
bool UOculusXRAnchorBPFunctionLibrary::GetRoomLayout(FOculusXRUInt64 Space, FOculusXRRoomLayout& RoomLayoutOut, int32 MaxWallsCapacity)
{
if (MaxWallsCapacity <= 0)
{
return false;
}
FOculusXRUUID OutCeilingUuid;
FOculusXRUUID OutFloorUuid;
TArray<FOculusXRUUID> OutWallsUuid;
const bool bSuccess = OculusXRAnchors::FOculusXRRoomLayoutManager::GetSpaceRoomLayout(Space.Value, static_cast<uint32>(MaxWallsCapacity), OutCeilingUuid, OutFloorUuid, OutWallsUuid);
if (bSuccess)
{
RoomLayoutOut.CeilingUuid = OutCeilingUuid;
RoomLayoutOut.FloorUuid = OutFloorUuid;
RoomLayoutOut.WallsUuid.InsertZeroed(0, OutWallsUuid.Num());
for (int32 i = 0; i < OutWallsUuid.Num(); ++i)
{
RoomLayoutOut.WallsUuid[i] = OutWallsUuid[i];
}
TArray<FOculusXRUUID> spaceUUIDs;
EOculusXRAnchorResult::Type result = OculusXRAnchors::FOculusXRAnchorManager::GetSpaceContainerUUIDs(Space, spaceUUIDs);
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(result))
{
RoomLayoutOut.RoomObjectUUIDs = spaceUUIDs;
}
}
return bSuccess;
}

View File

@@ -0,0 +1,208 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorComponent.h"
#include "OculusXRAnchors.h"
#include "OculusXRHMD.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRAnchorsPrivate.h"
#include "GameFramework/PlayerController.h"
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static TAutoConsoleVariable<int32> CVarOculusXRVerboseAnchorDebugXR(
TEXT("ovr.OculusXRVerboseAnchorDebug"),
0,
TEXT("Enables or disables verbose logging for Oculus anchors.\n")
TEXT("<=0: disabled (no printing)\n")
TEXT(" 1: enabled (verbose logging)\n"));
#endif
UOculusXRAnchorComponent::UOculusXRAnchorComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bUpdateHeadSpaceTransform(true)
, AnchorHandle(0)
, StorageLocations(0)
{
AnchorHandle = 0;
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
PrimaryComponentTick.TickGroup = TG_PostUpdateWork;
}
void UOculusXRAnchorComponent::BeginPlay()
{
Super::BeginPlay();
UWorld* World = GetWorld();
if (IsValid(World))
{
APlayerController* PlayerController = World->GetFirstPlayerController();
if (IsValid(PlayerController))
{
PlayerCameraManager = PlayerController->PlayerCameraManager;
}
}
}
void UOculusXRAnchorComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateAnchorTransform();
}
void UOculusXRAnchorComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (HasValidHandle())
{
EOculusXRAnchorResult::Type AnchorResult;
OculusXRAnchors::FOculusXRAnchors::DestroyAnchor(AnchorHandle.GetValue(), AnchorResult);
}
}
FOculusXRUInt64 UOculusXRAnchorComponent::GetHandle() const
{
return AnchorHandle;
}
void UOculusXRAnchorComponent::SetHandle(FOculusXRUInt64 Handle)
{
AnchorHandle = Handle;
}
bool UOculusXRAnchorComponent::HasValidHandle() const
{
return AnchorHandle != FOculusXRUInt64(0);
}
FOculusXRUUID UOculusXRAnchorComponent::GetUUID() const
{
return AnchorUUID;
}
void UOculusXRAnchorComponent::SetUUID(FOculusXRUUID NewUUID)
{
if (AnchorUUID.IsValidUUID())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor component already has valid UUID, cannot re-assign a new UUID. Component: %s -- Space: %llu -- UUID: %s"),
*GetName(), AnchorHandle, *AnchorUUID.ToString());
return;
}
if (!NewUUID.IsValidUUID())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("New UUID provided to component is invalid, cannot assign. Component: %s -- Space: %llu"), *GetName(), AnchorHandle);
return;
}
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Assigned new Oculus UUID: %s"), *NewUUID.ToString());
AnchorUUID = NewUUID;
}
bool UOculusXRAnchorComponent::IsStoredAtLocation(EOculusXRSpaceStorageLocation Location) const
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Anchor UUID: %s - Saved Local: %d - Saved Cloud: %d"),
*GetUUID().ToString(),
StorageLocations & static_cast<int32>(EOculusXRSpaceStorageLocation::Local),
StorageLocations & static_cast<int32>(EOculusXRSpaceStorageLocation::Cloud));
return (StorageLocations & static_cast<int32>(Location)) > 0;
}
void UOculusXRAnchorComponent::SetStoredLocation(EOculusXRSpaceStorageLocation Location, bool Stored)
{
if (Stored)
{
StorageLocations |= static_cast<int32>(Location);
}
else
{
StorageLocations = StorageLocations & ~static_cast<int32>(Location);
}
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Anchor UUID: %s - Saved Local: %d - Saved Cloud: %d"),
*GetUUID().ToString(),
StorageLocations & static_cast<int32>(EOculusXRSpaceStorageLocation::Local),
StorageLocations & static_cast<int32>(EOculusXRSpaceStorageLocation::Cloud));
}
bool UOculusXRAnchorComponent::IsSaved() const
{
return StorageLocations > 0;
}
void UOculusXRAnchorComponent::UpdateAnchorTransform() const
{
if (GetWorld() == nullptr)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve World Context"));
return;
}
AActor* Parent = GetOwner();
if (Parent)
{
if (AnchorHandle.Value)
{
FTransform OutTransform;
if (UOculusXRAnchorBPFunctionLibrary::GetAnchorTransformByHandle(AnchorHandle, OutTransform))
{
#if WITH_EDITOR
// Link only head-space transform update
if (bUpdateHeadSpaceTransform && PlayerCameraManager != nullptr)
{
FTransform MainCameraTransform;
MainCameraTransform.SetLocation(PlayerCameraManager->GetCameraLocation());
MainCameraTransform.SetRotation(FQuat(PlayerCameraManager->GetCameraRotation()));
if (!ToWorldSpacePose(MainCameraTransform, OutTransform))
{
UE_LOG(LogOculusXRAnchors, Display, TEXT("Was not able to transform anchor to world space pose"));
}
}
#endif
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (CVarOculusXRVerboseAnchorDebugXR.GetValueOnGameThread() > 0)
{
UE_LOG(LogOculusXRAnchors, Display, TEXT("UpdateAnchor Pos %s"), *OutTransform.GetLocation().ToString());
UE_LOG(LogOculusXRAnchors, Display, TEXT("UpdateAnchor Rot %s"), *OutTransform.GetRotation().ToString());
}
#endif
Parent->SetActorLocationAndRotation(OutTransform.GetLocation(), OutTransform.GetRotation(), false, 0, ETeleportType::ResetPhysics);
}
}
}
}
bool UOculusXRAnchorComponent::ToWorldSpacePose(FTransform CameraTransform, FTransform& OutTrackingSpaceTransform) const
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (!OculusXRHMD)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve OculusXRHMD, cannot calculate anchor world space pose."));
return false;
}
OculusXRHMD::FPose MainCameraPose(CameraTransform.GetRotation(), CameraTransform.GetLocation());
OculusXRHMD::FPose TrackingSpacePose(OutTrackingSpaceTransform.GetRotation(), OutTrackingSpaceTransform.GetLocation());
FVector OutHeadPosition;
FQuat OutHeadOrientation;
const bool bGetPose = OculusXRHMD->GetCurrentPose(OculusXRHMD->HMDDeviceId, OutHeadOrientation, OutHeadPosition);
if (!bGetPose)
return false;
OculusXRHMD::FPose HeadPose(OutHeadOrientation, OutHeadPosition);
OculusXRHMD::FPose poseInHeadSpace = HeadPose.Inverse() * TrackingSpacePose;
// To world space pose
const OculusXRHMD::FPose WorldTrackingSpacePose = MainCameraPose * poseInHeadSpace;
OutTrackingSpaceTransform.SetLocation(WorldTrackingSpacePose.Position);
OutTrackingSpaceTransform.SetRotation(WorldTrackingSpacePose.Orientation);
return true;
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorComponents.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRAnchorManager.h"
#include "OculusXRRoomLayoutManager.h"
#include "OculusXRSpatialAnchorComponent.h"
bool UOculusXRBaseAnchorComponent::IsComponentEnabled() const
{
bool OutEnabled;
bool OutChangePending;
auto OutResult = OculusXRAnchors::FOculusXRAnchorManager::GetSpaceComponentStatus(Space, Type, OutEnabled, OutChangePending);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult) && OutEnabled;
}
EOculusXRSpaceComponentType UOculusXRBaseAnchorComponent::GetType() const
{
return Type;
}
uint64 UOculusXRBaseAnchorComponent::GetSpace() const
{
return Space;
}
bool UOculusXRLocatableAnchorComponent::GetTransform(FTransform& outTransform) const
{
ensure(IsComponentEnabled());
if (!UOculusXRAnchorBPFunctionLibrary::GetAnchorTransformByHandle(Space, outTransform))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching transform failed."));
return false;
}
return true;
}
bool UOculusXRPlaneAnchorComponent::GetPositionAndSize(FVector& outPosition, FVector& outSize) const
{
ensure(IsComponentEnabled());
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OculusXRAnchors::FOculusXRAnchorManager::GetSpaceScenePlane(Space, outPosition, outSize)))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching scene plane failed."));
return false;
}
return true;
}
bool UOculusXRVolumeAnchorComponent::GetPositionAndSize(FVector& outPosition, FVector& outSize) const
{
ensure(IsComponentEnabled());
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OculusXRAnchors::FOculusXRAnchorManager::GetSpaceSceneVolume(Space, outPosition, outSize)))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching scene plane failed."));
return false;
}
return true;
}
bool UOculusXRSemanticClassificationAnchorComponent::GetSemanticClassifications(TArray<FString>& outClassifications) const
{
ensure(IsComponentEnabled());
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OculusXRAnchors::FOculusXRAnchorManager::GetSpaceSemanticClassification(Space, outClassifications)))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching scene volume failed."));
return false;
}
return true;
}
bool UOculusXRRoomLayoutAnchorComponent::GetRoomLayout(FOculusXRUUID& outFloorUUID, FOculusXRUUID& outCeilingUUID, TArray<FOculusXRUUID>& outWallsUUIDs) const
{
ensure(IsComponentEnabled());
if (!OculusXRAnchors::FOculusXRRoomLayoutManager::GetSpaceRoomLayout(Space, 64, outCeilingUUID, outFloorUUID, outWallsUUIDs))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching room layout failed."));
return false;
}
return true;
}
bool UOculusXRSpaceContainerAnchorComponent::GetUUIDs(TArray<FOculusXRUUID>& outUUIDs) const
{
ensure(IsComponentEnabled());
if (!OculusXRAnchors::FOculusXRAnchorManager::GetSpaceContainer(Space, outUUIDs))
{
UE_LOG(LogOculusSpatialAnchor, Warning, TEXT("Fetching container uuids failed."));
return false;
}
return true;
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorDelegates.h"
FOculusXRAnchorEventDelegates::FOculusXRSpatialAnchorCreateCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpatialAnchorCreateComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceSetComponentStatusCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceSetComponentStatusComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceQueryResultsDelegate FOculusXRAnchorEventDelegates::OculusSpaceQueryResults;
FOculusXRAnchorEventDelegates::FOculusXRSpaceQueryResultDelegate FOculusXRAnchorEventDelegates::OculusSpaceQueryResult;
FOculusXRAnchorEventDelegates::FOculusXRSpaceQueryCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceQueryComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceSaveCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceSaveComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceListSaveCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceListSaveComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceEraseCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceEraseComplete;
FOculusXRAnchorEventDelegates::FOculusXRSpaceShareCompleteDelegate FOculusXRAnchorEventDelegates::OculusSpaceShareComplete;
FOculusXRAnchorEventDelegates::FOculusXRSceneCaptureCompleteDelegate FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete;

View File

@@ -0,0 +1,581 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorLatentActions.h"
#include "OculusXRAnchorsPrivate.h"
#include "OculusXRHMD.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRRoomLayoutManager.h"
#include "OculusXRAnchorDelegates.h"
//
// Create Spatial Anchor
//
void UOculusXRAsyncAction_CreateSpatialAnchor::Activate()
{
if (!IsValid(TargetActor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid Target Actor passed to CreateSpatialAnchor latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::CreateSpatialAnchor(
AnchorTransform,
TargetActor,
FOculusXRSpatialAnchorCreateDelegate::CreateUObject(this, &UOculusXRAsyncAction_CreateSpatialAnchor::HandleCreateComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for CreateSpatialAnchor latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_CreateSpatialAnchor* UOculusXRAsyncAction_CreateSpatialAnchor::OculusXRAsyncCreateSpatialAnchor(AActor* TargetActor, const FTransform& AnchorTransform)
{
UOculusXRAsyncAction_CreateSpatialAnchor* Action = NewObject<UOculusXRAsyncAction_CreateSpatialAnchor>();
Action->TargetActor = TargetActor;
Action->AnchorTransform = AnchorTransform;
if (IsValid(TargetActor))
{
Action->RegisterWithGameInstance(TargetActor->GetWorld());
}
else
{
Action->RegisterWithGameInstance(GWorld);
}
return Action;
}
void UOculusXRAsyncAction_CreateSpatialAnchor::HandleCreateComplete(EOculusXRAnchorResult::Type CreateResult, UOculusXRAnchorComponent* Anchor)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(CreateResult))
{
Success.Broadcast(Anchor, CreateResult);
}
else
{
Failure.Broadcast(CreateResult);
}
SetReadyToDestroy();
}
//
// Erase Space
//
void UOculusXRAsyncAction_EraseAnchor::Activate()
{
if (!IsValid(TargetActor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid Target Actor passed to EraseSpace latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
UOculusXRAnchorComponent* AnchorComponent = TargetActor->FindComponentByClass<UOculusXRAnchorComponent>();
if (AnchorComponent == nullptr)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("No anchor on actor in EraseSpace latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::EraseAnchor(
AnchorComponent,
FOculusXRAnchorEraseDelegate::CreateUObject(this, &UOculusXRAsyncAction_EraseAnchor::HandleEraseAnchorComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for EraseSpace latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_EraseAnchor* UOculusXRAsyncAction_EraseAnchor::OculusXRAsyncEraseAnchor(AActor* TargetActor)
{
UOculusXRAsyncAction_EraseAnchor* Action = NewObject<UOculusXRAsyncAction_EraseAnchor>();
Action->TargetActor = TargetActor;
if (IsValid(TargetActor))
{
Action->RegisterWithGameInstance(TargetActor->GetWorld());
}
else
{
Action->RegisterWithGameInstance(GWorld);
}
return Action;
}
void UOculusXRAsyncAction_EraseAnchor::HandleEraseAnchorComplete(EOculusXRAnchorResult::Type EraseResult, FOculusXRUUID UUID)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(EraseResult))
{
Success.Broadcast(TargetActor, UUID, EraseResult);
}
else
{
Failure.Broadcast(EraseResult);
}
SetReadyToDestroy();
}
//
// Save Space
//
void UOculusXRAsyncAction_SaveAnchor::Activate()
{
if (!IsValid(TargetActor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid Target Actor passed to SaveSpace latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
UOculusXRAnchorComponent* AnchorComponent = TargetActor->FindComponentByClass<UOculusXRAnchorComponent>();
if (AnchorComponent == nullptr)
{
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
UE_LOG(LogOculusXRAnchors, Log, TEXT("Attempting to save anchor: %s to location %s"), IsValid(AnchorComponent) ? *AnchorComponent->GetName() : TEXT("INVALID ANCHOR"), *UEnum::GetValueAsString(StorageLocation));
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::SaveAnchor(
AnchorComponent,
StorageLocation,
FOculusXRAnchorSaveDelegate::CreateUObject(this, &UOculusXRAsyncAction_SaveAnchor::HandleSaveAnchorComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for SaveSpace latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_SaveAnchor* UOculusXRAsyncAction_SaveAnchor::OculusXRAsyncSaveAnchor(AActor* TargetActor, EOculusXRSpaceStorageLocation StorageLocation)
{
UOculusXRAsyncAction_SaveAnchor* Action = NewObject<UOculusXRAsyncAction_SaveAnchor>();
Action->TargetActor = TargetActor;
Action->StorageLocation = StorageLocation;
if (IsValid(TargetActor))
{
Action->RegisterWithGameInstance(TargetActor->GetWorld());
}
else
{
Action->RegisterWithGameInstance(GWorld);
}
return Action;
}
void UOculusXRAsyncAction_SaveAnchor::HandleSaveAnchorComplete(EOculusXRAnchorResult::Type SaveResult, UOculusXRAnchorComponent* Anchor)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(SaveResult))
{
Success.Broadcast(Anchor, SaveResult);
}
else
{
Failure.Broadcast(SaveResult);
}
SetReadyToDestroy();
}
//
// Save Anchor List
//
void UOculusXRAsyncAction_SaveAnchorList::Activate()
{
if (TargetAnchors.Num() == 0)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Empty Target Actor array passed to SaveSpaces latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::SaveAnchorList(
TargetAnchors,
StorageLocation,
FOculusXRAnchorSaveListDelegate::CreateUObject(this, &UOculusXRAsyncAction_SaveAnchorList::HandleSaveAnchorListComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for SaveSpaceList latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_SaveAnchorList* UOculusXRAsyncAction_SaveAnchorList::OculusXRAsyncSaveAnchorList(const TArray<AActor*>& TargetActors, EOculusXRSpaceStorageLocation StorageLocation)
{
UOculusXRAsyncAction_SaveAnchorList* Action = NewObject<UOculusXRAsyncAction_SaveAnchorList>();
auto ValidActorPtr = TargetActors.FindByPredicate([](AActor* Actor) { return IsValid(Actor); });
for (auto& it : TargetActors)
{
if (!IsValid(it))
{
continue;
}
UOculusXRAnchorComponent* AnchorComponent = it->FindComponentByClass<UOculusXRAnchorComponent>();
Action->TargetAnchors.Add(AnchorComponent);
}
Action->StorageLocation = StorageLocation;
if (ValidActorPtr != nullptr)
{
Action->RegisterWithGameInstance(*ValidActorPtr);
}
return Action;
}
void UOculusXRAsyncAction_SaveAnchorList::HandleSaveAnchorListComplete(EOculusXRAnchorResult::Type SaveResult, const TArray<UOculusXRAnchorComponent*>& SavedSpaces)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(SaveResult))
{
Success.Broadcast(SavedSpaces, SaveResult);
}
else
{
Failure.Broadcast(SaveResult);
}
SetReadyToDestroy();
}
//
// Query Spaces
//
void UOculusXRAsyncAction_QueryAnchors::Activate()
{
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::QueryAnchorsAdvanced(
QueryInfo,
FOculusXRAnchorQueryDelegate::CreateUObject(this, &UOculusXRAsyncAction_QueryAnchors::HandleQueryAnchorsResults),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for QuerySpaces latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_QueryAnchors* UOculusXRAsyncAction_QueryAnchors::OculusXRAsyncQueryAnchors(EOculusXRSpaceStorageLocation Location, const TArray<FOculusXRUUID>& UUIDs)
{
FOculusXRSpaceQueryInfo QueryInfo;
QueryInfo.FilterType = EOculusXRSpaceQueryFilterType::FilterByIds;
QueryInfo.IDFilter = UUIDs;
QueryInfo.Location = Location;
QueryInfo.MaxQuerySpaces = UUIDs.Num();
UOculusXRAsyncAction_QueryAnchors* Action = NewObject<UOculusXRAsyncAction_QueryAnchors>();
Action->QueryInfo = QueryInfo;
return Action;
}
UOculusXRAsyncAction_QueryAnchors* UOculusXRAsyncAction_QueryAnchors::OculusXRAsyncQueryAnchorsAdvanced(const FOculusXRSpaceQueryInfo& QueryInfo)
{
UOculusXRAsyncAction_QueryAnchors* Action = NewObject<UOculusXRAsyncAction_QueryAnchors>();
Action->QueryInfo = QueryInfo;
return Action;
}
void UOculusXRAsyncAction_QueryAnchors::HandleQueryAnchorsResults(EOculusXRAnchorResult::Type QueryResult, const TArray<FOculusXRSpaceQueryResult>& Results)
{
QueryResults = Results;
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(QueryResult))
{
Success.Broadcast(QueryResults, QueryResult);
}
else
{
Failure.Broadcast(QueryResult);
}
SetReadyToDestroy();
}
//
// Set Component Status with Anchor Actor
//
void UOculusXRAsyncAction_SetAnchorComponentStatus::Activate()
{
if (!IsValid(TargetActor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid Target Actor passed to SetComponentStatus latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
TargetAnchorComponent = TargetActor->FindComponentByClass<UOculusXRAnchorComponent>();
if (TargetAnchorComponent == nullptr)
{
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::SetAnchorComponentStatus(
TargetAnchorComponent,
ComponentType,
bEnabled,
0,
FOculusXRAnchorSetComponentStatusDelegate::CreateUObject(this, &UOculusXRAsyncAction_SetAnchorComponentStatus::HandleSetComponentStatusComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for SetComponentStatus latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_SetAnchorComponentStatus* UOculusXRAsyncAction_SetAnchorComponentStatus::OculusXRAsyncSetAnchorComponentStatus(AActor* TargetActor, EOculusXRSpaceComponentType ComponentType, bool bEnabled)
{
UOculusXRAsyncAction_SetAnchorComponentStatus* Action = NewObject<UOculusXRAsyncAction_SetAnchorComponentStatus>();
Action->TargetActor = TargetActor;
Action->ComponentType = ComponentType;
Action->bEnabled = bEnabled;
if (IsValid(TargetActor))
{
Action->RegisterWithGameInstance(TargetActor->GetWorld());
}
else
{
Action->RegisterWithGameInstance(GWorld);
}
return Action;
}
void UOculusXRAsyncAction_SetAnchorComponentStatus::HandleSetComponentStatusComplete(EOculusXRAnchorResult::Type SetStatusResult, uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool bResultEnabled)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(SetStatusResult))
{
Success.Broadcast(TargetAnchorComponent, SpaceComponentType, bResultEnabled, SetStatusResult);
}
else
{
Failure.Broadcast(SetStatusResult);
}
SetReadyToDestroy();
}
//
// Set Component Status
//
void UOculusXRAsyncAction_SetComponentStatus::Activate()
{
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::SetComponentStatus(
Component->GetSpace(),
Component->GetType(),
bEnabled,
0,
FOculusXRAnchorSetComponentStatusDelegate::CreateUObject(this, &UOculusXRAsyncAction_SetComponentStatus::HandleSetComponentStatusComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for SetComponentStatus latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_SetComponentStatus* UOculusXRAsyncAction_SetComponentStatus::OculusXRAsyncSetComponentStatus(UOculusXRBaseAnchorComponent* Component, bool bEnabled)
{
UOculusXRAsyncAction_SetComponentStatus* Action = NewObject<UOculusXRAsyncAction_SetComponentStatus>();
Action->Component = Component;
Action->bEnabled = bEnabled;
Action->RegisterWithGameInstance(GWorld);
return Action;
}
void UOculusXRAsyncAction_SetComponentStatus::HandleSetComponentStatusComplete(EOculusXRAnchorResult::Type SetStatusResult, uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool bResultEnabled)
{
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(SetStatusResult))
{
Success.Broadcast(Component, SetStatusResult);
}
else
{
Failure.Broadcast(SetStatusResult);
}
SetReadyToDestroy();
}
//
// Share Spaces
//
void UOculusXRAsyncAction_ShareAnchors::Activate()
{
if (TargetAnchors.Num() == 0)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Empty Target Actors array passed to ShareSpaces latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
if (ToShareWithIds.Num() == 0)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Empty Target Player IDs array passed to ShareSpaces latent action."));
Failure.Broadcast(EOculusXRAnchorResult::Failure);
return;
}
EOculusXRAnchorResult::Type Result;
bool bStartedAsync = OculusXRAnchors::FOculusXRAnchors::ShareAnchors(
TargetAnchors,
ToShareWithIds,
FOculusXRAnchorShareDelegate::CreateUObject(this, &UOculusXRAsyncAction_ShareAnchors::HandleShareAnchorsComplete),
Result);
if (!bStartedAsync)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async OVR Plugin call for ShareSpaces latent action."));
Failure.Broadcast(Result);
}
}
UOculusXRAsyncAction_ShareAnchors* UOculusXRAsyncAction_ShareAnchors::OculusXRAsyncShareAnchors(const TArray<AActor*>& TargetActors, const TArray<FString>& ToShareWithIds)
{
UOculusXRAsyncAction_ShareAnchors* Action = NewObject<UOculusXRAsyncAction_ShareAnchors>();
for (const auto& UserIDString : ToShareWithIds)
{
uint64 UserId = FCString::Strtoui64(*UserIDString, nullptr, 10);
if (UserId == 0)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("UserID provided to share anchors was invalid or unconvertable: %s"), *UserIDString);
}
Action->ToShareWithIds.Add(UserId);
}
for (auto& it : TargetActors)
{
if (!IsValid(it))
{
continue;
}
UOculusXRAnchorComponent* AnchorComponent = it->FindComponentByClass<UOculusXRAnchorComponent>();
Action->TargetAnchors.Add(AnchorComponent);
}
auto ValidActorPtr = TargetActors.FindByPredicate([](AActor* Actor) { return IsValid(Actor); });
if (ValidActorPtr != nullptr)
{
Action->RegisterWithGameInstance(*ValidActorPtr);
}
else
{
Action->RegisterWithGameInstance(GWorld);
}
return Action;
}
void UOculusXRAsyncAction_ShareAnchors::HandleShareAnchorsComplete(EOculusXRAnchorResult::Type ShareResult, const TArray<UOculusXRAnchorComponent*>& SharedAnchors, const TArray<uint64>& OculusUserIDs)
{
TArray<FString> OculusUserIDStrings;
for (const auto& it : OculusUserIDs)
{
OculusUserIDStrings.Add(FString::Printf(TEXT("%llu"), it));
}
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(ShareResult))
{
Success.Broadcast(SharedAnchors, OculusUserIDStrings, ShareResult);
}
else
{
Failure.Broadcast(ShareResult);
}
// Unbind and mark for destruction
SetReadyToDestroy();
}
UOculusXRAnchorLaunchCaptureFlow* UOculusXRAnchorLaunchCaptureFlow::LaunchCaptureFlowAsync(const UObject* WorldContext)
{
UWorld* World = GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::ReturnNull);
if (!ensureAlwaysMsgf(IsValid(WorldContext), TEXT("World Context was not valid.")))
{
return nullptr;
}
// Create a new UMyDelayAsyncAction, and store function arguments in it.
auto NewAction = NewObject<UOculusXRAnchorLaunchCaptureFlow>();
NewAction->RegisterWithGameInstance(World->GetGameInstance());
return NewAction;
}
void UOculusXRAnchorLaunchCaptureFlow::Activate()
{
Request = 0;
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.AddUObject(this, &UOculusXRAnchorLaunchCaptureFlow::OnCaptureFinish);
bool CaptureStarted = OculusXRAnchors::FOculusXRRoomLayoutManager::RequestSceneCapture(Request);
if (!CaptureStarted)
{
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.RemoveAll(this);
Failure.Broadcast();
}
}
void UOculusXRAnchorLaunchCaptureFlow::OnCaptureFinish(FOculusXRUInt64 RequestId, bool bSuccess)
{
if (Request != RequestId.GetValue())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("%llu request id doesn't match %llu. Ignoring request."), RequestId, Request);
return;
}
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.RemoveAll(this);
Success.Broadcast();
SetReadyToDestroy();
}

View File

@@ -0,0 +1,816 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorManager.h"
#include <vector>
#include "OculusXRHMD.h"
#include "OculusXRAnchorsModule.h"
#include "OculusXRAnchorDelegates.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRAnchorTypesPrivate.h"
namespace OculusXRAnchors
{
OculusXRHMD::FOculusXRHMD* GetHMD(bool& OutSuccessful)
{
OculusXRHMD::FOculusXRHMD* OutHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (!OutHMD)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve OculusXRHMD"));
OutSuccessful = false;
}
OutSuccessful = true;
return OutHMD;
}
ovrpUuid ConvertFOculusXRUUIDtoOvrpUuid(const FOculusXRUUID& UUID)
{
ovrpUuid Result;
FMemory::Memcpy(Result.data, UUID.UUIDBytes);
return Result;
}
ovrpSpaceQueryInfo ConvertToOVRPSpaceQueryInfo(const FOculusXRSpaceQueryInfo& UEQueryInfo)
{
static const int32 MaxIdsInFilter = 1024;
static const int32 MaxComponentTypesInFilter = 16;
ovrpSpaceQueryInfo Result;
Result.queryType = ovrpSpaceQueryType_Action;
Result.actionType = ovrpSpaceQueryActionType_Load;
Result.maxQuerySpaces = UEQueryInfo.MaxQuerySpaces;
Result.timeout = static_cast<double>(UEQueryInfo.Timeout); // Prevent compiler warnings, though there is a possible loss of data here.
switch (UEQueryInfo.Location)
{
case EOculusXRSpaceStorageLocation::Invalid:
Result.location = ovrpSpaceStorageLocation_Invalid;
break;
case EOculusXRSpaceStorageLocation::Local:
Result.location = ovrpSpaceStorageLocation_Local;
break;
case EOculusXRSpaceStorageLocation::Cloud:
Result.location = ovrpSpaceStorageLocation_Cloud;
break;
}
switch (UEQueryInfo.FilterType)
{
case EOculusXRSpaceQueryFilterType::None:
Result.filterType = ovrpSpaceQueryFilterType_None;
break;
case EOculusXRSpaceQueryFilterType::FilterByIds:
Result.filterType = ovrpSpaceQueryFilterType_Ids;
break;
case EOculusXRSpaceQueryFilterType::FilterByComponentType:
Result.filterType = ovrpSpaceQueryFilterType_Components;
break;
}
Result.IdInfo.numIds = FMath::Min(MaxIdsInFilter, UEQueryInfo.IDFilter.Num());
for (int i = 0; i < Result.IdInfo.numIds; ++i)
{
ovrpUuid OvrUuid = ConvertFOculusXRUUIDtoOvrpUuid(UEQueryInfo.IDFilter[i]);
Result.IdInfo.ids[i] = OvrUuid;
}
Result.componentsInfo.numComponents = FMath::Min(MaxComponentTypesInFilter, UEQueryInfo.ComponentFilter.Num());
for (int i = 0; i < Result.componentsInfo.numComponents; ++i)
{
Result.componentsInfo.components[i] = ConvertToOvrpComponentType(UEQueryInfo.ComponentFilter[i]);
}
return Result;
}
template <typename T>
void GetEventData(ovrpEventDataBuffer& Buffer, T& OutEventData)
{
unsigned char* BufData = Buffer.EventData;
BufData -= sizeof(uint64); //correct offset
memcpy(&OutEventData, BufData, sizeof(T));
}
void FOculusXRAnchorManager::OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult)
{
ovrpEventDataBuffer& buf = *EventDataBuffer;
EventPollResult = true;
switch (buf.EventType)
{
case ovrpEventType_SpatialAnchorCreateComplete:
{
ovrpEventDataSpatialAnchorCreateComplete AnchorCreateEvent;
GetEventData(buf, AnchorCreateEvent);
const FOculusXRUInt64 RequestId(AnchorCreateEvent.requestId);
const FOculusXRUInt64 Space(AnchorCreateEvent.space);
const FOculusXRUUID BPUUID(AnchorCreateEvent.uuid.data);
FOculusXRAnchorEventDelegates::OculusSpatialAnchorCreateComplete.Broadcast(RequestId, AnchorCreateEvent.result, Space, BPUUID);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpatialAnchorCreateComplete Request ID: %llu -- Space: %llu -- UUID: %s -- Result: %d"),
RequestId.GetValue(),
Space.GetValue(),
*BPUUID.ToString(),
AnchorCreateEvent.result);
break;
}
case ovrpEventType_SpaceSetComponentStatusComplete:
{
ovrpEventDataSpaceSetStatusComplete SetStatusEvent;
GetEventData(buf, SetStatusEvent);
//translate to BP types
const FOculusXRUInt64 RequestId(SetStatusEvent.requestId);
const FOculusXRUInt64 Space(SetStatusEvent.space);
EOculusXRSpaceComponentType BPSpaceComponentType = ConvertToUEComponentType(SetStatusEvent.componentType);
const FOculusXRUUID BPUUID(SetStatusEvent.uuid.data);
const bool bEnabled = (SetStatusEvent.enabled == ovrpBool_True);
FOculusXRAnchorEventDelegates::OculusSpaceSetComponentStatusComplete.Broadcast(
RequestId,
SetStatusEvent.result,
Space,
BPUUID,
BPSpaceComponentType,
bEnabled);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceSetComponentStatusComplete Request ID: %llu -- Type: %d -- Enabled: %d -- Space: %llu -- Result: %d"),
SetStatusEvent.requestId,
SetStatusEvent.componentType,
SetStatusEvent.enabled,
SetStatusEvent.space,
SetStatusEvent.result);
break;
}
case ovrpEventType_SpaceQueryResults:
{
ovrpEventSpaceQueryResults QueryEvent;
GetEventData(buf, QueryEvent);
const FOculusXRUInt64 RequestId(QueryEvent.requestId);
FOculusXRAnchorEventDelegates::OculusSpaceQueryResults.Broadcast(RequestId);
ovrpUInt32 ovrpOutCapacity = 0;
// First get capacity
const bool bGetCapacityResult = FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().RetrieveSpaceQueryResults(&QueryEvent.requestId, 0, &ovrpOutCapacity, nullptr));
UE_LOG(LogOculusXRAnchors, Log, TEXT("ovrpEventType_SpaceQueryResults Request ID: %llu -- Capacity: %d -- Result: %d"), QueryEvent.requestId, ovrpOutCapacity, bGetCapacityResult);
std::vector<ovrpSpaceQueryResult> ovrpResults(ovrpOutCapacity);
// Get Query Data
const bool bGetQueryDataResult = FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().RetrieveSpaceQueryResults(&QueryEvent.requestId, ovrpResults.size(), &ovrpOutCapacity, ovrpResults.data()));
for (auto queryResultElement : ovrpResults)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceQueryResult Space: %llu -- Result: %d"), queryResultElement.space, bGetQueryDataResult);
//translate types
FOculusXRUInt64 Space(queryResultElement.space);
FOculusXRUUID BPUUID(queryResultElement.uuid.data);
FOculusXRAnchorEventDelegates::OculusSpaceQueryResult.Broadcast(RequestId, Space, BPUUID);
}
break;
}
case ovrpEventType_SpaceQueryComplete:
{
ovrpEventSpaceQueryComplete QueryCompleteEvent;
GetEventData(buf, QueryCompleteEvent);
//translate to BP types
const FOculusXRUInt64 RequestId(QueryCompleteEvent.requestId);
const bool bSucceeded = QueryCompleteEvent.result >= 0;
FOculusXRAnchorEventDelegates::OculusSpaceQueryComplete.Broadcast(RequestId, QueryCompleteEvent.result);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceQueryComplete Request ID: %llu -- Result: %d"), QueryCompleteEvent.requestId, QueryCompleteEvent.result);
break;
}
case ovrpEventType_SpaceSaveComplete:
{
ovrpEventSpaceStorageSaveResult StorageResult;
GetEventData(buf, StorageResult);
//translate to BP types
const FOculusXRUUID uuid(StorageResult.uuid.data);
const FOculusXRUInt64 FSpace(StorageResult.space);
const FOculusXRUInt64 FRequest(StorageResult.requestId);
const bool bResult = StorageResult.result >= 0;
FOculusXRAnchorEventDelegates::OculusSpaceSaveComplete.Broadcast(FRequest, FSpace, bResult, StorageResult.result, uuid);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceSaveComplete Request ID: %llu -- Space: %llu -- Result: %d"), StorageResult.requestId, StorageResult.space, StorageResult.result);
break;
}
case ovrpEventType_SpaceListSaveResult:
{
ovrpEventSpaceListSaveResult SpaceListSaveResult;
GetEventData(buf, SpaceListSaveResult);
FOculusXRUInt64 RequestId(SpaceListSaveResult.requestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceListSaveResult Request ID: %llu -- Result: %d"), SpaceListSaveResult.requestId, SpaceListSaveResult.result);
FOculusXRAnchorEventDelegates::OculusSpaceListSaveComplete.Broadcast(RequestId, SpaceListSaveResult.result);
break;
}
case ovrpEventType_SpaceEraseComplete:
{
ovrpEventSpaceStorageEraseResult SpaceEraseEvent;
GetEventData(buf, SpaceEraseEvent);
//translate to BP types
const FOculusXRUUID uuid(SpaceEraseEvent.uuid.data);
const FOculusXRUInt64 FRequestId(SpaceEraseEvent.requestId);
const FOculusXRUInt64 FResult(SpaceEraseEvent.result);
const EOculusXRSpaceStorageLocation BPLocation = (SpaceEraseEvent.location == ovrpSpaceStorageLocation_Local) ? EOculusXRSpaceStorageLocation::Local : EOculusXRSpaceStorageLocation::Invalid;
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceEraseComplete Request ID: %llu -- Result: %d -- UUID: %s"), SpaceEraseEvent.requestId, SpaceEraseEvent.result, *UOculusXRAnchorBPFunctionLibrary::AnchorUUIDToString(SpaceEraseEvent.uuid.data));
FOculusXRAnchorEventDelegates::OculusSpaceEraseComplete.Broadcast(FRequestId, FResult.Value, uuid, BPLocation);
break;
}
case ovrpEventType_SpaceShareResult:
{
unsigned char* BufData = buf.EventData;
ovrpUInt64 OvrpRequestId = 0;
memcpy(&OvrpRequestId, BufData, sizeof(OvrpRequestId));
ovrpEventSpaceShareResult SpaceShareSpaceResult;
GetEventData(buf, SpaceShareSpaceResult);
FOculusXRUInt64 RequestId(SpaceShareSpaceResult.requestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ovrpEventType_SpaceShareSpaceResult Request ID: %llu -- Result: %d"),
SpaceShareSpaceResult.requestId,
SpaceShareSpaceResult.result);
FOculusXRAnchorEventDelegates::OculusSpaceShareComplete.Broadcast(RequestId, SpaceShareSpaceResult.result);
break;
}
case ovrpEventType_None:
default:
{
EventPollResult = false;
break;
}
}
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::CreateAnchor(const FTransform& InTransform, uint64& OutRequestId, const FTransform& CameraTransform)
{
bool bValidHMD;
OculusXRHMD::FOculusXRHMD* HMD = GetHMD(bValidHMD);
if (!bValidHMD)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("FOculusAnchorManager::CreateAnchor failed to retrieve HMD."));
return EOculusXRAnchorResult::Failure;
}
ovrpTrackingOrigin TrackingOriginType;
ovrpPosef Posef;
double Time = 0;
const FTransform TrackingToWorld = HMD->GetLastTrackingToWorld();
// convert to tracking space
const FQuat TrackingSpaceOrientation = TrackingToWorld.Inverse().TransformRotation(InTransform.Rotator().Quaternion());
const FVector TrackingSpacePosition = TrackingToWorld.Inverse().TransformPosition(InTransform.GetLocation());
const OculusXRHMD::FPose TrackingSpacePose(TrackingSpaceOrientation, TrackingSpacePosition);
#if WITH_EDITOR
// Link only head space position update
FVector OutHeadPosition;
FQuat OutHeadOrientation;
const bool bGetPose = HMD->GetCurrentPose(HMD->HMDDeviceId, OutHeadOrientation, OutHeadPosition);
if (!bGetPose)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("FOculusAnchorManager::CreateAnchor failed to get current headset pose."));
return EOculusXRAnchorResult::Failure;
}
OculusXRHMD::FPose HeadPose(OutHeadOrientation, OutHeadPosition);
OculusXRHMD::FPose MainCameraPose(CameraTransform.GetRotation(), CameraTransform.GetLocation());
OculusXRHMD::FPose PoseInHeadSpace = MainCameraPose.Inverse() * TrackingSpacePose;
// To world space pose
OculusXRHMD::FPose WorldPose = HeadPose * PoseInHeadSpace;
const bool bConverted = HMD->ConvertPose(WorldPose, Posef);
#else
const bool bConverted = HMD->ConvertPose(TrackingSpacePose, Posef);
#endif
if (!bConverted)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("FOculusAnchorManager::CreateAnchor failed to convert pose."));
return EOculusXRAnchorResult::Failure;
}
FOculusXRHMDModule::GetPluginWrapper().GetTrackingOriginType2(&TrackingOriginType);
FOculusXRHMDModule::GetPluginWrapper().GetTimeInSeconds(&Time);
const ovrpSpatialAnchorCreateInfo SpatialAnchorCreateInfo = {
TrackingOriginType,
Posef,
Time
};
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().CreateSpatialAnchor(&SpatialAnchorCreateInfo, &OutRequestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("CreateAnchor Request ID: %llu"), OutRequestId);
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("FOculusAnchorManager::CreateAnchor failed. Result: %d"), Result);
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::DestroySpace(uint64 Space)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().DestroySpace(static_cast<ovrpSpace*>(&Space));
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("DestroySpace Space ID: %llu"), Space);
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::SetSpaceComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, uint64& OutRequestId)
{
ovrpSpaceComponentType ovrpType = ConvertToOvrpComponentType(SpaceComponentType);
ovrpUInt64 OvrpOutRequestId = 0;
const ovrpUInt64 OVRPSpace = Space;
// validate existing status
ovrpBool isEnabled = false;
ovrpBool changePending = false;
const ovrpResult getComponentStatusResult = FOculusXRHMDModule::GetPluginWrapper().GetSpaceComponentStatus(&OVRPSpace, ovrpType, &isEnabled, &changePending);
bool isStatusChangingOrSame = (static_cast<bool>(isEnabled) == Enable && !changePending) || (static_cast<bool>(isEnabled) != Enable && changePending);
if (OVRP_SUCCESS(getComponentStatusResult) && isStatusChangingOrSame)
{
return EOculusXRAnchorResult::Success;
}
// set status
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().SetSpaceComponentStatus(
&OVRPSpace,
ovrpType,
Enable,
Timeout,
&OvrpOutRequestId);
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("SetSpaceComponentStatus Request ID: %llu"), OutRequestId);
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending)
{
const ovrpUInt64 OVRPSpace = Space;
ovrpBool OutOvrpEnabled = ovrpBool_False;
ovrpBool OutOvrpChangePending = ovrpBool_False;
ovrpSpaceComponentType ovrpType = ConvertToOvrpComponentType(SpaceComponentType);
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceComponentStatus(
&OVRPSpace,
ovrpType,
&OutOvrpEnabled,
&OutOvrpChangePending);
OutEnabled = (OutOvrpEnabled == ovrpBool_True);
OutChangePending = (OutOvrpChangePending == ovrpBool_True);
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSupportedAnchorComponents(uint64 Handle, TArray<EOculusXRSpaceComponentType>& OutSupportedTypes)
{
if (!FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
return EOculusXRAnchorResult::Failure;
}
ovrpSpace ovrSpace = Handle;
TArray<ovrpSpaceComponentType> ovrComponentTypes;
ovrpUInt32 input = 0;
ovrpUInt32 output = 0;
ovrpResult enumerateResult = FOculusXRHMDModule::GetPluginWrapper().EnumerateSpaceSupportedComponents(&ovrSpace, input, &output, nullptr);
if (!OVRP_SUCCESS(enumerateResult))
{
return static_cast<EOculusXRAnchorResult::Type>(enumerateResult);
}
input = output;
ovrComponentTypes.SetNumZeroed(output);
enumerateResult = FOculusXRHMDModule::GetPluginWrapper().EnumerateSpaceSupportedComponents(&ovrSpace, input, &output, ovrComponentTypes.GetData());
if (!OVRP_SUCCESS(enumerateResult))
{
return static_cast<EOculusXRAnchorResult::Type>(enumerateResult);
}
OutSupportedTypes.SetNumZeroed(ovrComponentTypes.Num());
for (int i = 0; i < ovrComponentTypes.Num(); ++i)
{
OutSupportedTypes[i] = ConvertToUEComponentType(ovrComponentTypes[i]);
}
return static_cast<EOculusXRAnchorResult::Type>(enumerateResult);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::SaveAnchor(uint64 Space,
EOculusXRSpaceStorageLocation StorageLocation,
EOculusXRSpaceStoragePersistenceMode StoragePersistenceMode, uint64& OutRequestId)
{
ovrpSpaceStorageLocation OvrpStorageLocation = ovrpSpaceStorageLocation_Local;
switch (StorageLocation)
{
case EOculusXRSpaceStorageLocation::Invalid:
OvrpStorageLocation = ovrpSpaceStorageLocation_Invalid;
break;
case EOculusXRSpaceStorageLocation::Local:
OvrpStorageLocation = ovrpSpaceStorageLocation_Local;
break;
case EOculusXRSpaceStorageLocation::Cloud:
OvrpStorageLocation = ovrpSpaceStorageLocation_Cloud;
break;
default:
break;
}
ovrpSpaceStoragePersistenceMode OvrpStoragePersistenceMode = ovrpSpaceStoragePersistenceMode_Invalid;
switch (StoragePersistenceMode)
{
case EOculusXRSpaceStoragePersistenceMode::Invalid:
OvrpStoragePersistenceMode = ovrpSpaceStoragePersistenceMode_Invalid;
break;
case EOculusXRSpaceStoragePersistenceMode::Indefinite:
OvrpStoragePersistenceMode = ovrpSpaceStoragePersistenceMode_Indefinite;
break;
default:
break;
}
ovrpUInt64 OvrpOutRequestId = 0;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().SaveSpace(&Space, OvrpStorageLocation, OvrpStoragePersistenceMode, &OvrpOutRequestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Saving space with: SpaceID: %llu -- Location: %d -- Persistence: %d -- OutID: %llu"), Space, OvrpStorageLocation, OvrpStoragePersistenceMode, OvrpOutRequestId);
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("FOculusXRHMD::SaveAnchor failed with: SpaceID: %llu -- Location: %d -- Persistence: %d"), Space, OvrpStorageLocation, OvrpStoragePersistenceMode);
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::SaveAnchorList(const TArray<uint64>& Spaces, EOculusXRSpaceStorageLocation StorageLocation, uint64& OutRequestId)
{
ovrpSpaceStorageLocation OvrpStorageLocation = ovrpSpaceStorageLocation_Local;
switch (StorageLocation)
{
case EOculusXRSpaceStorageLocation::Invalid:
OvrpStorageLocation = ovrpSpaceStorageLocation_Invalid;
break;
case EOculusXRSpaceStorageLocation::Local:
OvrpStorageLocation = ovrpSpaceStorageLocation_Local;
break;
case EOculusXRSpaceStorageLocation::Cloud:
OvrpStorageLocation = ovrpSpaceStorageLocation_Cloud;
break;
default:
break;
}
ovrpUInt64 OvrpOutRequestId = 0;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().SaveSpaceList(Spaces.GetData(), Spaces.Num(), OvrpStorageLocation, &OvrpOutRequestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Saving space list: Location: %d -- OutID: %llu"), OvrpStorageLocation, OvrpOutRequestId);
for (auto& it : Spaces)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("\tSpaceID: %llu"), it);
}
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("SaveSpaceList failed -- Result: %d"), Result);
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::EraseAnchor(uint64 AnchorHandle,
EOculusXRSpaceStorageLocation StorageLocation, uint64& OutRequestId)
{
ovrpSpaceStorageLocation ovrpStorageLocation = ovrpSpaceStorageLocation_Local;
switch (StorageLocation)
{
case EOculusXRSpaceStorageLocation::Invalid:
ovrpStorageLocation = ovrpSpaceStorageLocation_Invalid;
break;
case EOculusXRSpaceStorageLocation::Local:
ovrpStorageLocation = ovrpSpaceStorageLocation_Local;
break;
default:;
}
ovrpUInt64 OvrpOutRequestId = 0;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().EraseSpace(&AnchorHandle, ovrpStorageLocation, &OvrpOutRequestId);
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
UE_LOG(LogOculusXRAnchors, Log, TEXT("Erasing anchor -- Handle: %llu -- Location: %d -- OutID: %llu"), AnchorHandle, ovrpStorageLocation, OvrpOutRequestId);
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::QuerySpaces(const FOculusXRSpaceQueryInfo& QueryInfo, uint64& OutRequestId)
{
ovrpUInt64 OvrpOutRequestId = 0;
ovrpResult QuerySpacesResult = ovrpFailure;
ovrpSpaceQueryInfo ovrQueryInfo = ConvertToOVRPSpaceQueryInfo(QueryInfo);
QuerySpacesResult = FOculusXRHMDModule::GetPluginWrapper().QuerySpaces(&ovrQueryInfo, &OvrpOutRequestId);
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Query Spaces\n ovrpSpaceQueryInfo:\n\tQueryType: %d\n\tMaxQuerySpaces: %d\n\tTimeout: %f\n\tLocation: %d\n\tActionType: %d\n\tFilterType: %d\n\n\tRequest ID: %llu"),
ovrQueryInfo.queryType, ovrQueryInfo.maxQuerySpaces, (float)ovrQueryInfo.timeout, ovrQueryInfo.location, ovrQueryInfo.actionType, ovrQueryInfo.filterType, OutRequestId);
if (QueryInfo.FilterType == EOculusXRSpaceQueryFilterType::FilterByIds)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Query contains %d UUIDs"), QueryInfo.IDFilter.Num());
for (auto& it : QueryInfo.IDFilter)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("UUID: %s"), *it.ToString());
}
}
else if (QueryInfo.FilterType == EOculusXRSpaceQueryFilterType::FilterByComponentType)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Query contains %d Component Types"), QueryInfo.ComponentFilter.Num());
for (auto& it : QueryInfo.ComponentFilter)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("ComponentType: %s"), *UEnum::GetValueAsString(it));
}
}
return static_cast<EOculusXRAnchorResult::Type>(QuerySpacesResult);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::ShareSpaces(const TArray<uint64>& Spaces, const TArray<uint64>& UserIds, uint64& OutRequestId)
{
TArray<const char*> stringStorage;
TArray<ovrpUser> OvrpUsers;
for (const auto& UserId : UserIds)
{
ovrpUser OvrUser;
ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().CreateSpaceUser(&UserId, &OvrUser);
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to create space user from ID - %llu"), UserId);
continue;
}
OvrpUsers.Add(OvrUser);
}
ovrpUInt64 OvrpOutRequestId = 0;
const ovrpResult ShareSpacesResult = FOculusXRHMDModule::GetPluginWrapper().ShareSpaces(Spaces.GetData(), Spaces.Num(), OvrpUsers.GetData(), OvrpUsers.Num(), &OvrpOutRequestId);
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Sharing space list -- OutID: %llu"), OvrpOutRequestId);
for (auto& User : OvrpUsers)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("\tOvrpUser: %llu"), User);
ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().DestroySpaceUser(&User);
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Failed to destroy space user: %llu"), User);
continue;
}
}
for (auto& it : Spaces)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("\tSpaceID: %llu"), it);
}
memcpy(&OutRequestId, &OvrpOutRequestId, sizeof(uint64));
return static_cast<EOculusXRAnchorResult::Type>(ShareSpacesResult);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceContainerUUIDs(uint64 Space, TArray<FOculusXRUUID>& OutUUIDs)
{
TArray<ovrpUuid> ovrUuidArray;
// Get the number of elements in the container
ovrpSpaceContainer ovrSpaceContainer;
ovrSpaceContainer.uuidCapacityInput = 0;
ovrSpaceContainer.uuidCountOutput = 0;
ovrSpaceContainer.uuids = nullptr;
ovrpResult result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceContainer(&Space, &ovrSpaceContainer);
if (OVRP_FAILURE(result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to get space container %d"), result);
return static_cast<EOculusXRAnchorResult::Type>(result);
}
// Retrieve the actual array of UUIDs
ovrUuidArray.SetNum(ovrSpaceContainer.uuidCountOutput);
ovrSpaceContainer.uuidCapacityInput = ovrSpaceContainer.uuidCountOutput;
ovrSpaceContainer.uuids = ovrUuidArray.GetData();
result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceContainer(&Space, &ovrSpaceContainer);
if (OVRP_FAILURE(result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to get space container %d"), result);
return static_cast<EOculusXRAnchorResult::Type>(result);
}
// Write out the remaining UUIDs
OutUUIDs.Reserve(ovrUuidArray.Num());
for (auto& it : ovrUuidArray)
{
OutUUIDs.Add(FOculusXRUUID(it.data));
}
return EOculusXRAnchorResult::Success;
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceScenePlane(uint64 Space, FVector& OutPos, FVector& OutSize)
{
OutPos.X = OutPos.Y = OutPos.Z = 0.f;
OutSize.X = OutSize.Y = OutSize.Z = 0.f;
ovrpRectf rect;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceBoundingBox2D(&Space, &rect);
if (OVRP_SUCCESS(Result))
{
// Convert to UE4's coordinates system
OutPos.Y = rect.Pos.x;
OutPos.Z = rect.Pos.y;
OutSize.Y = rect.Size.w;
OutSize.Z = rect.Size.h;
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceSceneVolume(uint64 Space, FVector& OutPos, FVector& OutSize)
{
OutPos.X = OutPos.Y = OutPos.Z = 0.f;
OutSize.X = OutSize.Y = OutSize.Z = 0.f;
ovrpBoundsf bounds;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceBoundingBox3D(&Space, &bounds);
if (OVRP_SUCCESS(Result))
{
// Convert from OpenXR's right-handed to Unreal's left-handed coordinate system.
// OpenXR Unreal
// | y | z
// | |
//z <----+ +----> x
// / /
// x/ y/
//
OutPos.X = -bounds.Pos.z;
OutPos.Y = bounds.Pos.x;
OutPos.Z = bounds.Pos.y;
// The position represents the corner of the volume which has the lowest value
// of each axis. Since we flipped the sign of one of the axes we need to adjust
// the position to the other side of the volume
OutPos.X -= bounds.Size.d;
// We keep the size positive for all dimensions
OutSize.X = bounds.Size.d;
OutSize.Y = bounds.Size.w;
OutSize.Z = bounds.Size.h;
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceSemanticClassification(uint64 Space, TArray<FString>& OutSemanticClassifications)
{
OutSemanticClassifications.Empty();
const int32 maxByteSize = 1024;
char labelsChars[maxByteSize];
ovrpSemanticLabels labels;
labels.byteCapacityInput = maxByteSize;
labels.labels = labelsChars;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceSemanticLabels(&Space, &labels);
if (OVRP_SUCCESS(Result))
{
FString labelsStr(labels.byteCountOutput, labels.labels);
labelsStr.ParseIntoArray(OutSemanticClassifications, TEXT(","));
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceContainer(uint64 Space, TArray<FOculusXRUUID>& OutContainerUuids)
{
OutContainerUuids.Empty();
ovrpSpaceContainer container;
ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceContainer(&Space, &container);
if (OVRP_SUCCESS(Result))
{
TArray<ovrpUuid> uuids;
size_t size = container.uuidCountOutput;
uuids.InsertZeroed(0, size);
container.uuidCapacityInput = size;
container.uuids = uuids.GetData();
Result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceContainer(&Space, &container);
if (OVRP_SUCCESS(Result))
{
OutContainerUuids.InsertZeroed(0, size);
for (size_t i = 0; i < size; i++)
{
OutContainerUuids[i] = FOculusXRUUID(uuids[i].data);
}
}
}
return static_cast<EOculusXRAnchorResult::Type>(Result);
}
EOculusXRAnchorResult::Type FOculusXRAnchorManager::GetSpaceBoundary2D(uint64 Space, TArray<FVector2f>& OutVertices)
{
TArray<ovrpVector2f> vertices;
// Get the number of elements in the container
ovrpBoundary2D boundary;
boundary.vertexCapacityInput = 0;
boundary.vertexCountOutput = 0;
boundary.vertices = nullptr;
ovrpResult result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceBoundary2D(&Space, &boundary);
if (OVRP_FAILURE(result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to get space boundary 2d %d"), result);
return static_cast<EOculusXRAnchorResult::Type>(result);
}
// Retrieve the actual array of vertices
vertices.SetNum(boundary.vertexCountOutput);
boundary.vertexCapacityInput = boundary.vertexCountOutput;
boundary.vertices = vertices.GetData();
result = FOculusXRHMDModule::GetPluginWrapper().GetSpaceBoundary2D(&Space, &boundary);
if (OVRP_FAILURE(result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to get space boundary 2d %d"), result);
return static_cast<EOculusXRAnchorResult::Type>(result);
}
// Write out the vertices
OutVertices.Reserve(vertices.Num());
for (const auto& it : vertices)
{
OutVertices.Add(FVector2f(it.x, it.y));
}
return EOculusXRAnchorResult::Success;
}
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,33 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRAnchorComponent.h"
#include "OculusXRHMDPrivate.h"
namespace OculusXRAnchors
{
struct OCULUSXRANCHORS_API FOculusXRAnchorManager
{
static EOculusXRAnchorResult::Type CreateAnchor(const FTransform& InTransform, uint64& OutRequestId, const FTransform& CameraTransform);
static EOculusXRAnchorResult::Type DestroySpace(uint64 Space);
static EOculusXRAnchorResult::Type SetSpaceComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, uint64& OutRequestId);
static EOculusXRAnchorResult::Type GetSpaceComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending);
static EOculusXRAnchorResult::Type GetSupportedAnchorComponents(uint64 Handle, TArray<EOculusXRSpaceComponentType>& OutSupportedTypes);
static EOculusXRAnchorResult::Type SaveAnchor(uint64 Space, EOculusXRSpaceStorageLocation StorageLocation, EOculusXRSpaceStoragePersistenceMode StoragePersistenceMode, uint64& OutRequestId);
static EOculusXRAnchorResult::Type SaveAnchorList(const TArray<uint64>& Spaces, EOculusXRSpaceStorageLocation StorageLocation, uint64& OutRequestId);
static EOculusXRAnchorResult::Type EraseAnchor(uint64 AnchorHandle, EOculusXRSpaceStorageLocation StorageLocation, uint64& OutRequestId);
static EOculusXRAnchorResult::Type QuerySpaces(const FOculusXRSpaceQueryInfo& QueryInfo, uint64& OutRequestId);
static EOculusXRAnchorResult::Type ShareSpaces(const TArray<uint64>& Spaces, const TArray<uint64>& UserIds, uint64& OutRequestId);
static EOculusXRAnchorResult::Type GetSpaceContainerUUIDs(uint64 Space, TArray<FOculusXRUUID>& OutUUIDs);
static EOculusXRAnchorResult::Type GetSpaceScenePlane(uint64 Space, FVector& OutPos, FVector& OutSize);
static EOculusXRAnchorResult::Type GetSpaceSceneVolume(uint64 Space, FVector& OutPos, FVector& OutSize);
static EOculusXRAnchorResult::Type GetSpaceSemanticClassification(uint64 Space, TArray<FString>& OutSemanticClassification);
static EOculusXRAnchorResult::Type GetSpaceContainer(uint64 Space, TArray<FOculusXRUUID>& OutContainerUuids);
static EOculusXRAnchorResult::Type GetSpaceBoundary2D(uint64 Space, TArray<FVector2f>& OutVertices);
static void OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult);
};
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,96 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorTypes.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRAnchorsModule.h"
#include "OculusXRAnchorTypesPrivate.h"
bool FOculusXRUInt64::operator==(const FOculusXRUInt64& Right) const
{
return IsEqual(Right);
}
bool FOculusXRUInt64::operator!=(const FOculusXRUInt64& Right) const
{
return !IsEqual(Right);
}
FOculusXRUUID::FOculusXRUUID()
{
FMemory::Memzero(&UUIDBytes, OCULUSXR_UUID_SIZE);
}
FOculusXRUUID::FOculusXRUUID(const ovrpXRUuidArray& UuidArray)
{
FMemory::Memcpy(UUIDBytes, UuidArray);
}
bool FOculusXRUUID::operator==(const FOculusXRUUID& Right) const
{
return IsEqual(Right);
}
bool FOculusXRUUID::operator!=(const FOculusXRUUID& Right) const
{
return !IsEqual(Right);
}
bool FOculusXRUUID::IsValidUUID() const
{
static uint8 InvalidUUID[OCULUSXR_UUID_SIZE] = { 0 };
return FMemory::Memcmp(UUIDBytes, InvalidUUID, OCULUSXR_UUID_SIZE) != 0;
}
bool FOculusXRUUID::IsEqual(const FOculusXRUUID& Other) const
{
return FMemory::Memcmp(UUIDBytes, Other.UUIDBytes, OCULUSXR_UUID_SIZE) == 0;
}
uint32 GetTypeHash(const FOculusXRUUID& Other)
{
return FCrc::MemCrc32(&Other.UUIDBytes, sizeof(Other.UUIDBytes));
}
bool FOculusXRUUID::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
uint8 data[16] = { 0 };
for (uint8 i = 0; i < OCULUSXR_UUID_SIZE; ++i)
{
data[i] = UUIDBytes[i];
};
for (uint8 i = 0; i < OCULUSXR_UUID_SIZE; ++i)
{
Ar << data[i];
};
for (uint8 i = 0; i < OCULUSXR_UUID_SIZE; ++i)
{
UUIDBytes[i] = data[i];
};
bOutSuccess = true;
return true;
}
FArchive& operator<<(FArchive& Ar, FOculusXRUUID& UUID)
{
bool bOutSuccess = false;
UUID.NetSerialize(Ar, nullptr, bOutSuccess);
return Ar;
}
bool FOculusXRUUID::Serialize(FArchive& Ar)
{
Ar << *this;
return true;
}
FString FOculusXRUUID::ToString() const
{
return BytesToHex(UUIDBytes, OCULUSXR_UUID_SIZE);
}

View File

@@ -0,0 +1,79 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchorTypesPrivate.h"
ovrpSpaceComponentType ConvertToOvrpComponentType(const EOculusXRSpaceComponentType ComponentType)
{
ovrpSpaceComponentType ovrpType = ovrpSpaceComponentType_Max;
switch (ComponentType)
{
case EOculusXRSpaceComponentType::Locatable:
ovrpType = ovrpSpaceComponentType_Locatable;
break;
case EOculusXRSpaceComponentType::Sharable:
ovrpType = ovrpSpaceComponentType_Sharable;
break;
case EOculusXRSpaceComponentType::Storable:
ovrpType = ovrpSpaceComponentType_Storable;
break;
case EOculusXRSpaceComponentType::ScenePlane:
ovrpType = ovrpSpaceComponentType_Bounded2D;
break;
case EOculusXRSpaceComponentType::SceneVolume:
ovrpType = ovrpSpaceComponentType_Bounded3D;
break;
case EOculusXRSpaceComponentType::SemanticClassification:
ovrpType = ovrpSpaceComponentType_SemanticLabels;
break;
case EOculusXRSpaceComponentType::RoomLayout:
ovrpType = ovrpSpaceComponentType_RoomLayout;
break;
case EOculusXRSpaceComponentType::SpaceContainer:
ovrpType = ovrpSpaceComponentType_SpaceContainer;
break;
case EOculusXRSpaceComponentType::TriangleMesh:
ovrpType = ovrpSpaceComponentType_TriangleMesh;
break;
default:;
}
return ovrpType;
}
EOculusXRSpaceComponentType ConvertToUEComponentType(const ovrpSpaceComponentType ComponentType)
{
EOculusXRSpaceComponentType ueComponentType = EOculusXRSpaceComponentType::Undefined;
switch (ComponentType)
{
case ovrpSpaceComponentType_Locatable:
ueComponentType = EOculusXRSpaceComponentType::Locatable;
break;
case ovrpSpaceComponentType_Sharable:
ueComponentType = EOculusXRSpaceComponentType::Sharable;
break;
case ovrpSpaceComponentType_Storable:
ueComponentType = EOculusXRSpaceComponentType::Storable;
break;
case ovrpSpaceComponentType_Bounded2D:
ueComponentType = EOculusXRSpaceComponentType::ScenePlane;
break;
case ovrpSpaceComponentType_Bounded3D:
ueComponentType = EOculusXRSpaceComponentType::SceneVolume;
break;
case ovrpSpaceComponentType_SemanticLabels:
ueComponentType = EOculusXRSpaceComponentType::SemanticClassification;
break;
case ovrpSpaceComponentType_RoomLayout:
ueComponentType = EOculusXRSpaceComponentType::RoomLayout;
break;
case ovrpSpaceComponentType_SpaceContainer:
ueComponentType = EOculusXRSpaceComponentType::SpaceContainer;
break;
case ovrpSpaceComponentType_TriangleMesh:
ueComponentType = EOculusXRSpaceComponentType::TriangleMesh;
break;
default:;
}
return ueComponentType;
}

View File

@@ -0,0 +1,8 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRAnchorTypes.h"
#include "OVR_Plugin_Types.h"
ovrpSpaceComponentType ConvertToOvrpComponentType(const EOculusXRSpaceComponentType ComponentType);
EOculusXRSpaceComponentType ConvertToUEComponentType(const ovrpSpaceComponentType ComponentType);

View File

@@ -0,0 +1,732 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRAnchors.h"
#include "CoreMinimal.h"
#include "Camera/PlayerCameraManager.h"
#include "GameFramework/PlayerController.h"
#include "OculusXRAnchorsModule.h"
#include "OculusXRAnchorDelegates.h"
#include "OculusXRHMDModule.h"
#include "OculusXRAnchorManager.h"
#include "OculusXRSpatialAnchorComponent.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRTelemetryAnchorsEvents.h"
namespace OculusXRAnchors
{
void FOculusXRAnchors::Initialize()
{
DelegateHandleAnchorCreate = FOculusXRAnchorEventDelegates::OculusSpatialAnchorCreateComplete.AddRaw(this, &FOculusXRAnchors::HandleSpatialAnchorCreateComplete);
DelegateHandleAnchorErase = FOculusXRAnchorEventDelegates::OculusSpaceEraseComplete.AddRaw(this, &FOculusXRAnchors::HandleAnchorEraseComplete);
DelegateHandleSetComponentStatus = FOculusXRAnchorEventDelegates::OculusSpaceSetComponentStatusComplete.AddRaw(this, &FOculusXRAnchors::HandleSetComponentStatusComplete);
DelegateHandleAnchorSave = FOculusXRAnchorEventDelegates::OculusSpaceSaveComplete.AddRaw(this, &FOculusXRAnchors::HandleAnchorSaveComplete);
DelegateHandleAnchorSaveList = FOculusXRAnchorEventDelegates::OculusSpaceListSaveComplete.AddRaw(this, &FOculusXRAnchors::HandleAnchorSaveListComplete);
DelegateHandleQueryResultsBegin = FOculusXRAnchorEventDelegates::OculusSpaceQueryResults.AddRaw(this, &FOculusXRAnchors::HandleAnchorQueryResultsBegin);
DelegateHandleQueryResultElement = FOculusXRAnchorEventDelegates::OculusSpaceQueryResult.AddRaw(this, &FOculusXRAnchors::HandleAnchorQueryResultElement);
DelegateHandleQueryComplete = FOculusXRAnchorEventDelegates::OculusSpaceQueryComplete.AddRaw(this, &FOculusXRAnchors::HandleAnchorQueryComplete);
DelegateHandleAnchorShare = FOculusXRAnchorEventDelegates::OculusSpaceShareComplete.AddRaw(this, &FOculusXRAnchors::HandleAnchorSharingComplete);
}
void FOculusXRAnchors::Teardown()
{
FOculusXRAnchorEventDelegates::OculusSpatialAnchorCreateComplete.Remove(DelegateHandleAnchorCreate);
FOculusXRAnchorEventDelegates::OculusSpaceEraseComplete.Remove(DelegateHandleAnchorErase);
FOculusXRAnchorEventDelegates::OculusSpaceSetComponentStatusComplete.Remove(DelegateHandleSetComponentStatus);
FOculusXRAnchorEventDelegates::OculusSpaceSaveComplete.Remove(DelegateHandleAnchorSave);
FOculusXRAnchorEventDelegates::OculusSpaceListSaveComplete.Remove(DelegateHandleAnchorSaveList);
FOculusXRAnchorEventDelegates::OculusSpaceQueryResults.Remove(DelegateHandleQueryResultsBegin);
FOculusXRAnchorEventDelegates::OculusSpaceQueryResult.Remove(DelegateHandleQueryResultElement);
FOculusXRAnchorEventDelegates::OculusSpaceQueryComplete.Remove(DelegateHandleQueryComplete);
FOculusXRAnchorEventDelegates::OculusSpaceShareComplete.Remove(DelegateHandleAnchorShare);
}
FOculusXRAnchors* FOculusXRAnchors::GetInstance()
{
return FOculusXRAnchorsModule::GetOculusAnchors();
}
bool FOculusXRAnchors::CreateSpatialAnchor(const FTransform& InTransform, AActor* TargetActor, const FOculusXRSpatialAnchorCreateDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(TargetActor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid actor provided when attempting to create a spatial anchor."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
UWorld* World = TargetActor->GetWorld();
if (!IsValid(World))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve World Context while creating spatial anchor."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
APlayerController* PlayerController = World->GetFirstPlayerController();
if (!IsValid(PlayerController))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve Player Controller while creating spatial anchor"));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
APlayerCameraManager* PlayerCameraManager = PlayerController->PlayerCameraManager;
FTransform MainCameraTransform = FTransform::Identity;
if (IsValid(PlayerCameraManager))
{
MainCameraTransform.SetLocation(PlayerCameraManager->GetCameraLocation());
MainCameraTransform.SetRotation(FQuat(PlayerCameraManager->GetCameraRotation()));
}
UOculusXRAnchorComponent* Anchor = Cast<UOculusXRAnchorComponent>(TargetActor->GetComponentByClass(UOculusXRAnchorComponent::StaticClass()));
if (IsValid(Anchor) && Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Actor targeted to create anchor already has an anchor component with a valid handle."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::CreateAnchor(InTransform, RequestId, MainCameraTransform);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
OculusXRTelemetry::Events::FAnchorsCreateRequest Trace(static_cast<int>(GetTypeHash(RequestId)));
if (bAsyncStartSuccess)
{
CreateAnchorBinding AnchorData;
AnchorData.RequestId = RequestId;
AnchorData.Actor = TargetActor;
AnchorData.Binding = ResultCallback;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->CreateSpatialAnchorBindings.Add(RequestId, AnchorData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async create spatial anchor."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
Trace.SetResult(OculusXRTelemetry::EAction::Cancel).End();
}
return bAsyncStartSuccess;
}
bool FOculusXRAnchors::EraseAnchor(UOculusXRAnchorComponent* Anchor, const FOculusXRAnchorEraseDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(Anchor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to erase an anchor."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUUID());
return false;
}
if (!Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Cannot erase anchor with invalid handle."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUUID());
return false;
}
if (!Anchor->IsStoredAtLocation(EOculusXRSpaceStorageLocation::Local))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Only local anchors can be erased."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUUID());
return false;
}
uint64 RequestId = 0;
// Erase only supports local anchors
EOculusXRAnchorResult::Type Result = FOculusXRAnchorManager::EraseAnchor(Anchor->GetHandle(), EOculusXRSpaceStorageLocation::Local, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(Result);
OculusXRTelemetry::Events::FAnchorsEraseRequest Trace(static_cast<int>(GetTypeHash(RequestId)));
if (bAsyncStartSuccess)
{
EraseAnchorBinding EraseData;
EraseData.RequestId = RequestId;
EraseData.Binding = ResultCallback;
EraseData.Anchor = Anchor;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->EraseAnchorBindings.Add(RequestId, EraseData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async erase spatial anchor."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUUID());
Trace.SetResult(OculusXRTelemetry::EAction::Cancel).End();
}
return bAsyncStartSuccess;
}
bool FOculusXRAnchors::DestroyAnchor(uint64 AnchorHandle, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::DestroySpace(AnchorHandle);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::SetAnchorComponentStatus(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, const FOculusXRAnchorSetComponentStatusDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(Anchor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to set anchor component status."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUInt64(), EOculusXRSpaceComponentType::Undefined, false);
return false;
}
if (!Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor provided to set anchor component status has invalid handle."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUInt64(), EOculusXRSpaceComponentType::Undefined, false);
return false;
}
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::SetSpaceComponentStatus(Anchor->GetHandle(), SpaceComponentType, Enable, Timeout, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
OculusXRTelemetry::Events::FAnchorsSetComponentStatusRequest Trace(static_cast<int>(GetTypeHash(RequestId)));
if (bAsyncStartSuccess)
{
SetComponentStatusBinding SetComponentStatusData;
SetComponentStatusData.RequestId = RequestId;
SetComponentStatusData.Binding = ResultCallback;
SetComponentStatusData.AnchorHandle = Anchor->GetHandle();
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->SetComponentStatusBindings.Add(RequestId, SetComponentStatusData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to set anchor component status."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, FOculusXRUInt64(), EOculusXRSpaceComponentType::Undefined, false);
Trace.SetResult(OculusXRTelemetry::EAction::Cancel).End();
}
return true;
}
bool FOculusXRAnchors::GetAnchorComponentStatus(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(Anchor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to get space component status."));
OutResult = EOculusXRAnchorResult::Failure;
return false;
}
if (!Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor provided to get space component status has invalid handle."));
OutResult = EOculusXRAnchorResult::Failure;
return false;
}
return GetComponentStatus(Anchor->GetHandle(), SpaceComponentType, OutEnabled, OutChangePending, OutResult);
}
bool FOculusXRAnchors::GetAnchorSupportedComponents(UOculusXRAnchorComponent* Anchor, TArray<EOculusXRSpaceComponentType>& OutSupportedComponents, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(Anchor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to get space component status."));
OutResult = EOculusXRAnchorResult::Failure;
return false;
}
if (!Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor provided to get space component status has invalid handle."));
OutResult = EOculusXRAnchorResult::Failure;
return false;
}
return GetSupportedComponents(Anchor->GetHandle(), OutSupportedComponents, OutResult);
}
bool FOculusXRAnchors::SetComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, const FOculusXRAnchorSetComponentStatusDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::SetSpaceComponentStatus(Space, SpaceComponentType, Enable, Timeout, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
if (bAsyncStartSuccess)
{
SetComponentStatusBinding SetComponentStatusData;
SetComponentStatusData.RequestId = RequestId;
SetComponentStatusData.Binding = ResultCallback;
SetComponentStatusData.AnchorHandle = Space;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->SetComponentStatusBindings.Add(RequestId, SetComponentStatusData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to set anchor component status."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, Space, SpaceComponentType, Enable);
}
return true;
}
bool FOculusXRAnchors::GetComponentStatus(uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceComponentStatus(AnchorHandle, SpaceComponentType, OutEnabled, OutChangePending);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::GetSupportedComponents(uint64 AnchorHandle, TArray<EOculusXRSpaceComponentType>& OutSupportedComponents, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSupportedAnchorComponents(AnchorHandle, OutSupportedComponents);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::SaveAnchor(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceStorageLocation StorageLocation, const FOculusXRAnchorSaveDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
if (!IsValid(Anchor))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to save anchor."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
if (!Anchor->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor provided to save anchor has invalid handle."));
OutResult = EOculusXRAnchorResult::Failure;
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
return false;
}
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::SaveAnchor(Anchor->GetHandle(), StorageLocation, EOculusXRSpaceStoragePersistenceMode::Indefinite, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
OculusXRTelemetry::Events::FAnchorsSaveRequest Trace(static_cast<int>(GetTypeHash(RequestId)));
if (bAsyncStartSuccess)
{
SaveAnchorBinding SaveAnchorData;
SaveAnchorData.RequestId = RequestId;
SaveAnchorData.Binding = ResultCallback;
SaveAnchorData.Location = StorageLocation;
SaveAnchorData.Anchor = Anchor;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->AnchorSaveBindings.Add(RequestId, SaveAnchorData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to save anchor."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, nullptr);
Trace.SetResult(OculusXRTelemetry::EAction::Cancel).End();
}
return bAsyncStartSuccess;
}
void AnchorComponentsToReferences(const TArray<UOculusXRAnchorComponent*>& Anchors, TArray<uint64>& Handles, TArray<TWeakObjectPtr<UOculusXRAnchorComponent>>& AnchorPtrs)
{
Handles.Empty();
AnchorPtrs.Empty();
for (auto& AnchorInstance : Anchors)
{
if (!IsValid(AnchorInstance))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Invalid anchor provided when attempting to process anchor list."));
continue;
}
if (!AnchorInstance->HasValidHandle())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Anchor provided to anchor list has invalid handle."));
continue;
}
Handles.Add(AnchorInstance->GetHandle().GetValue());
AnchorPtrs.Add(AnchorInstance);
}
}
bool FOculusXRAnchors::SaveAnchorList(const TArray<UOculusXRAnchorComponent*>& Anchors, EOculusXRSpaceStorageLocation StorageLocation, const FOculusXRAnchorSaveListDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
TArray<uint64> Handles;
TArray<TWeakObjectPtr<UOculusXRAnchorComponent>> SavedAnchors;
AnchorComponentsToReferences(Anchors, Handles, SavedAnchors);
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::SaveAnchorList(Handles, StorageLocation, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
if (bAsyncStartSuccess)
{
SaveAnchorListBinding SaveAnchorListData;
SaveAnchorListData.RequestId = RequestId;
SaveAnchorListData.Binding = ResultCallback;
SaveAnchorListData.Location = StorageLocation;
SaveAnchorListData.SavedAnchors = SavedAnchors;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->AnchorSaveListBindings.Add(RequestId, SaveAnchorListData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to save anchor list."));
ResultCallback.ExecuteIfBound(OutResult, TArray<UOculusXRAnchorComponent*>());
}
return bAsyncStartSuccess;
}
bool FOculusXRAnchors::QueryAnchors(const TArray<FOculusXRUUID>& AnchorUUIDs, EOculusXRSpaceStorageLocation Location, const FOculusXRAnchorQueryDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
FOculusXRSpaceQueryInfo QueryInfo;
QueryInfo.FilterType = EOculusXRSpaceQueryFilterType::FilterByIds;
QueryInfo.IDFilter = AnchorUUIDs;
QueryInfo.Location = Location;
QueryInfo.MaxQuerySpaces = AnchorUUIDs.Num();
return QueryAnchorsAdvanced(QueryInfo, ResultCallback, OutResult);
}
bool FOculusXRAnchors::QueryAnchorsAdvanced(const FOculusXRSpaceQueryInfo& QueryInfo, const FOculusXRAnchorQueryDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::QuerySpaces(QueryInfo, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
OculusXRTelemetry::Events::FAnchorsQueryRequest Trace(static_cast<int>(GetTypeHash(RequestId)));
if (bAsyncStartSuccess)
{
AnchorQueryBinding QueryResults;
QueryResults.RequestId = RequestId;
QueryResults.Binding = ResultCallback;
QueryResults.Location = QueryInfo.Location;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->AnchorQueryBindings.Add(RequestId, QueryResults);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to query anchors."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, TArray<FOculusXRSpaceQueryResult>());
Trace.SetResult(OculusXRTelemetry::EAction::Cancel).End();
}
return bAsyncStartSuccess;
}
bool FOculusXRAnchors::ShareAnchors(const TArray<UOculusXRAnchorComponent*>& Anchors, const TArray<uint64>& OculusUserIDs, const FOculusXRAnchorShareDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult)
{
TArray<uint64> Handles;
TArray<TWeakObjectPtr<UOculusXRAnchorComponent>> SharedAnchors;
AnchorComponentsToReferences(Anchors, Handles, SharedAnchors);
uint64 RequestId = 0;
OutResult = FOculusXRAnchorManager::ShareSpaces(Handles, OculusUserIDs, RequestId);
bool bAsyncStartSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
if (bAsyncStartSuccess)
{
ShareAnchorsBinding ShareAnchorsData;
ShareAnchorsData.RequestId = RequestId;
ShareAnchorsData.Binding = ResultCallback;
ShareAnchorsData.SharedAnchors = SharedAnchors;
ShareAnchorsData.OculusUserIds = OculusUserIDs;
FOculusXRAnchors* SDKInstance = GetInstance();
SDKInstance->ShareAnchorsBindings.Add(RequestId, ShareAnchorsData);
}
else
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to start async call to share anchor."));
ResultCallback.ExecuteIfBound(EOculusXRAnchorResult::Failure, TArray<UOculusXRAnchorComponent*>(), TArray<uint64>());
}
return bAsyncStartSuccess;
}
bool FOculusXRAnchors::GetSpaceContainerUUIDs(uint64 Space, TArray<FOculusXRUUID>& OutUUIDs, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceContainerUUIDs(Space, OutUUIDs);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::GetSpaceScenePlane(uint64 Space, FVector& OutPos, FVector& OutSize, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceScenePlane(Space, OutPos, OutSize);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::GetSpaceSceneVolume(uint64 Space, FVector& OutPos, FVector& OutSize, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceSceneVolume(Space, OutPos, OutSize);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::GetSpaceSemanticClassification(uint64 Space, TArray<FString>& OutSemanticClassifications, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceSemanticClassification(Space, OutSemanticClassifications);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
bool FOculusXRAnchors::GetSpaceBoundary2D(uint64 Space, TArray<FVector2f>& OutVertices, EOculusXRAnchorResult::Type& OutResult)
{
OutResult = FOculusXRAnchorManager::GetSpaceBoundary2D(Space, OutVertices);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(OutResult);
}
void FOculusXRAnchors::HandleSpatialAnchorCreateComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUInt64 Space, FOculusXRUUID UUID)
{
OculusXRTelemetry::Events::FAnchorsCreateResponse(static_cast<int>(GetTypeHash(RequestId)))
.SetResult(OVRP_SUCCESS(Result) ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail);
CreateAnchorBinding* AnchorDataPtr = CreateSpatialAnchorBindings.Find(RequestId.GetValue());
if (AnchorDataPtr == nullptr)
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("Couldn't find anchor data binding for create spatial anchor! Request: %llu"), RequestId.GetValue());
return;
}
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to create Spatial Anchor. Request: %llu -- Result: %d"), RequestId.GetValue(), Result);
AnchorDataPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), nullptr);
CreateSpatialAnchorBindings.Remove(RequestId.GetValue());
return;
}
if (!AnchorDataPtr->Actor.IsValid())
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Actor has been invalidated while creating actor. Request: %llu"), RequestId.GetValue());
// Clean up the orphaned space
EOculusXRAnchorResult::Type AnchorResult;
FOculusXRAnchors::DestroyAnchor(Space, AnchorResult);
AnchorDataPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), nullptr);
CreateSpatialAnchorBindings.Remove(RequestId.GetValue());
return;
}
AActor* TargetActor = AnchorDataPtr->Actor.Get();
UOculusXRSpatialAnchorComponent* SpatialAnchorComponent = TargetActor->FindComponentByClass<UOculusXRSpatialAnchorComponent>();
if (SpatialAnchorComponent == nullptr)
{
SpatialAnchorComponent = Cast<UOculusXRSpatialAnchorComponent>(TargetActor->AddComponentByClass(UOculusXRSpatialAnchorComponent::StaticClass(), false, FTransform::Identity, false));
}
SpatialAnchorComponent->SetHandle(Space);
SpatialAnchorComponent->SetUUID(UUID);
uint64 tempOut;
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Locatable, true, 0.0f, tempOut);
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Sharable, true, 0.0f, tempOut);
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Storable, true, 0.0f, tempOut);
AnchorDataPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SpatialAnchorComponent);
CreateSpatialAnchorBindings.Remove(RequestId.GetValue());
}
void FOculusXRAnchors::HandleAnchorEraseComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUUID UUID, EOculusXRSpaceStorageLocation Location)
{
OculusXRTelemetry::Events::FAnchorsEraseResponse(static_cast<int>(GetTypeHash(RequestId)))
.SetResult(OVRP_SUCCESS(Result) ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail);
EraseAnchorBinding* EraseDataPtr = EraseAnchorBindings.Find(RequestId.GetValue());
if (EraseDataPtr == nullptr)
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("Couldn't find binding for space erase! Request: %llu"), RequestId.GetValue());
return;
}
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to erase Spatial Anchor. Request: %llu -- Result: %d"), RequestId.GetValue(), Result);
EraseDataPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), UUID);
EraseAnchorBindings.Remove(RequestId.GetValue());
return;
}
if (EraseDataPtr->Anchor.IsValid())
{
// Since you can only erase local anchors, just unset local anchor storage
EraseDataPtr->Anchor->SetStoredLocation(EOculusXRSpaceStorageLocation::Local, false);
}
EraseDataPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), UUID);
EraseAnchorBindings.Remove(RequestId.GetValue());
}
void FOculusXRAnchors::HandleSetComponentStatusComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUInt64 Space, FOculusXRUUID UUID, EOculusXRSpaceComponentType ComponentType, bool Enabled)
{
OculusXRTelemetry::Events::FAnchorsSetComponentStatusResponse(static_cast<int>(GetTypeHash(RequestId)))
.SetResult(OVRP_SUCCESS(Result) ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail);
SetComponentStatusBinding* SetStatusBinding = SetComponentStatusBindings.Find(RequestId.GetValue());
if (SetStatusBinding == nullptr)
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Couldn't find binding for set component status! Request: %llu"), RequestId.GetValue());
return;
}
if (SetStatusBinding != nullptr)
{
SetStatusBinding->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SetStatusBinding->AnchorHandle, ComponentType, Enabled);
SetComponentStatusBindings.Remove(RequestId.GetValue());
return;
}
SetStatusBinding->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SetStatusBinding->AnchorHandle, ComponentType, Enabled);
SetComponentStatusBindings.Remove(RequestId.GetValue());
}
void FOculusXRAnchors::HandleAnchorSaveComplete(FOculusXRUInt64 RequestId, FOculusXRUInt64 Space, bool Success, int Result, FOculusXRUUID UUID)
{
OculusXRTelemetry::Events::FAnchorsSaveResponse(static_cast<int>(GetTypeHash(RequestId)))
.SetResult(OVRP_SUCCESS(Result) ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail);
SaveAnchorBinding* SaveAnchorData = AnchorSaveBindings.Find(RequestId.GetValue());
if (SaveAnchorData == nullptr)
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("Couldn't find binding for save anchor! Request: %llu"), RequestId.GetValue());
return;
}
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to save Spatial Anchor. Request: %llu -- Result: %d -- Space: %llu"), RequestId.GetValue(), Result, Space.GetValue());
SaveAnchorData->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SaveAnchorData->Anchor.Get());
AnchorSaveBindings.Remove(RequestId.GetValue());
return;
}
if (SaveAnchorData->Anchor.IsValid())
{
SaveAnchorData->Anchor->SetStoredLocation(SaveAnchorData->Location, true);
}
SaveAnchorData->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SaveAnchorData->Anchor.Get());
AnchorSaveBindings.Remove(RequestId.GetValue());
}
void FOculusXRAnchors::HandleAnchorSaveListComplete(FOculusXRUInt64 RequestId, int Result)
{
SaveAnchorListBinding* SaveListData = AnchorSaveListBindings.Find(RequestId.GetValue());
if (SaveListData == nullptr)
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("Couldn't find binding for save anchor list! Request: %llu"), RequestId.GetValue());
return;
}
// Get all anchors
TArray<UOculusXRAnchorComponent*> SavedAnchors;
for (auto& WeakAnchor : SaveListData->SavedAnchors)
{
if (WeakAnchor.IsValid())
{
SavedAnchors.Add(WeakAnchor.Get());
}
}
// Failed to save
if (OVRP_FAILURE(Result))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to save Spatial Anchors. Request: %llu -- Result: %d"), RequestId.GetValue(), Result);
SaveListData->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SavedAnchors);
AnchorSaveListBindings.Remove(RequestId.GetValue());
return;
}
// Set new storage location
for (auto& SavedAnchor : SavedAnchors)
{
SavedAnchor->SetStoredLocation(SaveListData->Location, true);
}
SaveListData->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SavedAnchors);
AnchorSaveListBindings.Remove(RequestId.GetValue());
}
void FOculusXRAnchors::HandleAnchorQueryResultsBegin(FOculusXRUInt64 RequestId)
{
// no op
}
void FOculusXRAnchors::HandleAnchorQueryResultElement(FOculusXRUInt64 RequestId, FOculusXRUInt64 Space, FOculusXRUUID UUID)
{
AnchorQueryBinding* ResultPtr = AnchorQueryBindings.Find(RequestId.GetValue());
if (ResultPtr)
{
uint64 tempOut;
TArray<EOculusXRSpaceComponentType> supportedTypes;
FOculusXRAnchorManager::GetSupportedAnchorComponents(Space, supportedTypes);
if (supportedTypes.Contains(EOculusXRSpaceComponentType::Locatable))
{
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Locatable, true, 0.0f, tempOut);
}
if (supportedTypes.Contains(EOculusXRSpaceComponentType::Sharable))
{
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Sharable, true, 0.0f, tempOut);
}
if (supportedTypes.Contains(EOculusXRSpaceComponentType::Storable))
{
FOculusXRAnchorManager::SetSpaceComponentStatus(Space, EOculusXRSpaceComponentType::Storable, true, 0.0f, tempOut);
}
ResultPtr->Results.Add(FOculusXRSpaceQueryResult(Space, UUID, ResultPtr->Location));
}
}
void FOculusXRAnchors::HandleAnchorQueryComplete(FOculusXRUInt64 RequestId, int Result)
{
OculusXRTelemetry::Events::FAnchorsQueryResponse(static_cast<int>(GetTypeHash(RequestId)))
.SetResult(OVRP_SUCCESS(Result) ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail);
AnchorQueryBinding* ResultPtr = AnchorQueryBindings.Find(RequestId.GetValue());
if (ResultPtr)
{
ResultPtr->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), ResultPtr->Results);
AnchorQueryBindings.Remove(RequestId.GetValue());
}
}
void FOculusXRAnchors::HandleAnchorSharingComplete(FOculusXRUInt64 RequestId, int Result)
{
ShareAnchorsBinding* ShareAnchorsData = ShareAnchorsBindings.Find(RequestId);
if (ShareAnchorsData == nullptr)
{
UE_LOG(LogOculusXRAnchors, Error, TEXT("Couldn't find binding for share anchors! Request: %llu"), RequestId.GetValue());
return;
}
TArray<UOculusXRAnchorComponent*> SharedAnchors;
for (auto& WeakAnchor : ShareAnchorsData->SharedAnchors)
{
SharedAnchors.Add(WeakAnchor.Get());
}
ShareAnchorsData->Binding.ExecuteIfBound(static_cast<EOculusXRAnchorResult::Type>(Result), SharedAnchors, ShareAnchorsData->OculusUserIds);
ShareAnchorsBindings.Remove(RequestId.GetValue());
}
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,55 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRAnchorsModule.h"
#if OCULUS_ANCHORS_SUPPORTED_PLATFORMS
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRAnchors.h"
#include "OculusXRAnchorManager.h"
#include "OculusXRRoomLayoutManager.h"
DEFINE_LOG_CATEGORY(LogOculusXRAnchors);
#define LOCTEXT_NAMESPACE "OculusXRAnchors"
//-------------------------------------------------------------------------------------------------
// FOculusXRAnchorsModule
//-------------------------------------------------------------------------------------------------
void FOculusXRAnchorsModule::StartupModule()
{
if (!GEngine)
{
return;
}
OculusXRHMD::FOculusXRHMD* HMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (!HMD)
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Unable to retrieve OculusXRHMD, cannot add event polling delegates."));
return;
}
HMD->AddEventPollingDelegate(OculusXRHMD::FOculusXRHMDEventPollingDelegate::CreateStatic(&OculusXRAnchors::FOculusXRAnchorManager::OnPollEvent));
HMD->AddEventPollingDelegate(OculusXRHMD::FOculusXRHMDEventPollingDelegate::CreateStatic(&OculusXRAnchors::FOculusXRRoomLayoutManager::OnPollEvent));
Anchors.Initialize();
}
void FOculusXRAnchorsModule::ShutdownModule()
{
Anchors.Teardown();
}
OculusXRAnchors::FOculusXRAnchors* FOculusXRAnchorsModule::GetOculusAnchors()
{
FOculusXRAnchorsModule& Module = FModuleManager::LoadModuleChecked<FOculusXRAnchorsModule>(TEXT("OculusXRAnchors"));
return &Module.Anchors;
}
#endif // OCULUS_ANCHORS_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRAnchorsModule, OculusXRAnchors)
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,41 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IOculusXRAnchorsModule.h"
#include "OculusXRAnchors.h"
#define LOCTEXT_NAMESPACE "OculusAnchors"
//-------------------------------------------------------------------------------------------------
// FOculusXRAnchorsModule
//-------------------------------------------------------------------------------------------------
#if OCULUS_ANCHORS_SUPPORTED_PLATFORMS
DECLARE_LOG_CATEGORY_EXTERN(LogOculusXRAnchors, Log, All);
class FOculusXRAnchorsModule : public IOculusXRAnchorsModule
{
public:
virtual ~FOculusXRAnchorsModule() = default;
// IModuleInterface interface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
static OculusXRAnchors::FOculusXRAnchors* GetOculusAnchors();
private:
OculusXRAnchors::FOculusXRAnchors Anchors;
};
#else // OCULUS_ANCHORS_SUPPORTED_PLATFORMS
class FOculusXRAnchorsModule : public FDefaultModuleImpl
{
};
#endif // OCULUS_ANCHORS_SUPPORTED_PLATFORMS
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,5 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRAnchorsModule.h"

View File

@@ -0,0 +1,148 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRRoomLayoutManager.h"
#include "OculusXRHMD.h"
#include "OculusXRAnchorDelegates.h"
#include "OculusXRAnchorsModule.h"
namespace OculusXRAnchors
{
void FOculusXRRoomLayoutManager::OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult)
{
ovrpEventDataBuffer& buf = *EventDataBuffer;
switch (buf.EventType)
{
case ovrpEventType_None:
break;
case ovrpEventType_SceneCaptureComplete:
{
ovrpEventSceneCaptureComplete sceneCaptureComplete;
unsigned char* bufData = buf.EventData;
memcpy(&sceneCaptureComplete.requestId, bufData, sizeof(sceneCaptureComplete.requestId));
bufData += sizeof(ovrpUInt64); //move forward
memcpy(&sceneCaptureComplete.result, bufData, sizeof(sceneCaptureComplete.result));
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.Broadcast(FOculusXRUInt64(sceneCaptureComplete.requestId), sceneCaptureComplete.result >= 0);
break;
}
default:
{
EventPollResult = false;
break;
}
}
EventPollResult = true;
}
/**
* @brief Requests the launch of Capture Flow
* @param OutRequestID The requestId returned by the system
* @return returns true if sucessfull
*/
bool FOculusXRRoomLayoutManager::RequestSceneCapture(uint64& OutRequestID)
{
OutRequestID = 0;
ovrpSceneCaptureRequest sceneCaptureRequest;
sceneCaptureRequest.request = nullptr;
sceneCaptureRequest.requestByteCount = 0;
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().RequestSceneCapture(&sceneCaptureRequest, &OutRequestID);
if (OVRP_FAILURE(Result))
{
return false;
}
return true;
}
/**
* @brief Gets the room layout for a specific space
* @param Space The space to get the room layout for
* @param MaxWallsCapacity Maximum number of walls to query
* @param OutCeilingUuid The ceiling entity's uuid
* @param OutFloorUuid The floor entity's uuid
* @param OutWallsUuid Array of uuids belonging to the walls in the room layout
* @return returns true if successful
*/
bool FOculusXRRoomLayoutManager::GetSpaceRoomLayout(const uint64 Space, const uint32 MaxWallsCapacity,
FOculusXRUUID& OutCeilingUuid, FOculusXRUUID& OutFloorUuid, TArray<FOculusXRUUID>& OutWallsUuid)
{
ovrpRoomLayout roomLayout;
roomLayout.wallUuidCapacityInput = 0;
roomLayout.wallUuidCountOutput = 0;
// First call to get output size
const ovrpResult firstCallResult = FOculusXRHMDModule::GetPluginWrapper().GetSpaceRoomLayout(&Space, &roomLayout);
if (OVRP_FAILURE(firstCallResult))
{
return false;
}
// Set the input size and pointer to the uuid array
TArray<ovrpUuid> uuids;
uuids.InsertZeroed(0, roomLayout.wallUuidCountOutput);
roomLayout.wallUuidCapacityInput = roomLayout.wallUuidCountOutput;
roomLayout.wallUuids = uuids.GetData();
const ovrpResult secondCallResult = FOculusXRHMDModule::GetPluginWrapper().GetSpaceRoomLayout(&Space, &roomLayout);
if (OVRP_FAILURE(secondCallResult))
{
return false;
}
OutCeilingUuid = FOculusXRUUID(roomLayout.ceilingUuid.data);
OutFloorUuid = FOculusXRUUID(roomLayout.floorUuid.data);
OutWallsUuid.Empty();
OutWallsUuid.InsertZeroed(0, uuids.Num());
for (int32 i = 0; i < uuids.Num(); ++i)
{
OutWallsUuid[i] = FOculusXRUUID(roomLayout.wallUuids[i].data);
}
return true;
}
bool FOculusXRRoomLayoutManager::GetSpaceTriangleMesh(uint64 Space, TArray<FVector>& Vertices, TArray<int32>& Triangles)
{
ovrpTriangleMesh OVRPMesh = { 0, 0, nullptr, 0, 0, nullptr };
ovrpResult CountResult = FOculusXRHMDModule::GetPluginWrapper().GetSpaceTriangleMesh(&Space, &OVRPMesh);
if (OVRP_FAILURE(CountResult))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to load TriangleMesh info - Space: %llu - Result: %d"), Space, CountResult);
return false;
}
OVRPMesh.indexCapacityInput = OVRPMesh.indexCountOutput;
OVRPMesh.vertexCapacityInput = OVRPMesh.vertexCountOutput;
TArray<ovrpVector3f> OVRPVertices;
OVRPVertices.SetNum(OVRPMesh.vertexCapacityInput);
OVRPMesh.vertices = OVRPVertices.GetData();
Triangles.SetNum(OVRPMesh.indexCapacityInput);
check(sizeof(TRemoveReference<decltype(Triangles)>::Type::ElementType) == sizeof(TRemovePointer<decltype(OVRPMesh.indices)>::Type));
OVRPMesh.indices = Triangles.GetData();
const ovrpResult MeshResult = FOculusXRHMDModule::GetPluginWrapper().GetSpaceTriangleMesh(&Space, &OVRPMesh);
if (OVRP_FAILURE(MeshResult))
{
UE_LOG(LogOculusXRAnchors, Warning, TEXT("Failed to load TriangleMesh data - Space: %llu - Result: %d"), Space, MeshResult);
return false;
}
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Loaded TriangleMesh data - Space: %llu - Vertices: %d - Faces: %d"),
Space, OVRPMesh.vertexCapacityInput, OVRPMesh.indexCapacityInput);
Vertices.Empty(OVRPVertices.Num());
Algo::Transform(OVRPVertices, Vertices, [](const auto& Vertex) { return OculusXRHMD::ToFVector(Vertex); });
return true;
return false;
}
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,21 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRAnchorComponent.h"
#include "OculusXRHMDPrivate.h"
namespace OculusXRAnchors
{
struct FOculusXRRoomLayoutManager
{
static bool RequestSceneCapture(uint64& OutRequestID);
static bool GetSpaceRoomLayout(const uint64 Space, const uint32 MaxWallsCapacity,
FOculusXRUUID& OutCeilingUuid, FOculusXRUUID& OutFloorUuid, TArray<FOculusXRUUID>& OutWallsUuid);
static bool GetSpaceTriangleMesh(uint64 Space, TArray<FVector>& Vertices, TArray<int32>& Triangles);
static void OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult);
};
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,82 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRRoomLayoutManagerComponent.h"
#include "OculusXRHMD.h"
#include "OculusXRRoomLayoutManager.h"
#include "OculusXRAnchorDelegates.h"
#include "OculusXRAnchorManager.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "ProceduralMeshComponent.h"
#include "OculusXRAnchorsModule.h"
UOculusXRRoomLayoutManagerComponent::UOculusXRRoomLayoutManagerComponent(const FObjectInitializer& ObjectInitializer)
{
bWantsInitializeComponent = true; // so that InitializeComponent() gets called
}
void UOculusXRRoomLayoutManagerComponent::OnRegister()
{
Super::OnRegister();
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.AddUObject(this, &UOculusXRRoomLayoutManagerComponent::OculusRoomLayoutSceneCaptureComplete_Handler);
}
void UOculusXRRoomLayoutManagerComponent::OnUnregister()
{
Super::OnUnregister();
FOculusXRAnchorEventDelegates::OculusSceneCaptureComplete.RemoveAll(this);
}
void UOculusXRRoomLayoutManagerComponent::InitializeComponent()
{
Super::InitializeComponent();
}
void UOculusXRRoomLayoutManagerComponent::UninitializeComponent()
{
Super::UninitializeComponent();
}
bool UOculusXRRoomLayoutManagerComponent::LaunchCaptureFlow()
{
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Launch capture flow -- UOculusXRRoomLayoutManagerComponent"));
uint64 OutRequest = 0;
const bool bSuccess = OculusXRAnchors::FOculusXRRoomLayoutManager::RequestSceneCapture(OutRequest);
if (bSuccess)
{
EntityRequestList.Add(OutRequest);
}
UE_LOG(LogOculusXRAnchors, Verbose, TEXT("Launch capture flow -- RequestSceneCapture -- %d"), bSuccess);
return bSuccess;
}
bool UOculusXRRoomLayoutManagerComponent::GetRoomLayout(FOculusXRUInt64 Space, FOculusXRRoomLayout& RoomLayoutOut, int32 MaxWallsCapacity)
{
return UOculusXRAnchorBPFunctionLibrary::GetRoomLayout(Space, RoomLayoutOut, MaxWallsCapacity);
}
bool UOculusXRRoomLayoutManagerComponent::LoadTriangleMesh(FOculusXRUInt64 Space, UProceduralMeshComponent* Mesh, bool CreateCollision) const
{
ensure(Mesh);
TArray<FVector> Vertices;
TArray<int32> Triangles;
bool Success = OculusXRAnchors::FOculusXRRoomLayoutManager::GetSpaceTriangleMesh(Space, Vertices, Triangles);
if (!Success)
{
return false;
}
// Mesh->bUseAsyncCooking = true;
TArray<FVector> EmptyNormals;
TArray<FVector2D> EmptyUV;
TArray<FColor> EmptyVertexColors;
TArray<FProcMeshTangent> EmptyTangents;
Mesh->CreateMeshSection(0, Vertices, Triangles, EmptyNormals, EmptyUV, EmptyVertexColors, EmptyTangents, CreateCollision);
return true;
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSpatialAnchorComponent.h"
DEFINE_LOG_CATEGORY(LogOculusSpatialAnchor);
UOculusXRSpatialAnchorComponent::UOculusXRSpatialAnchorComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
bool UOculusXRSpatialAnchorComponent::Create(const FTransform& NewAnchorTransform, AActor* OwningActor, const FOculusXRSpatialAnchorCreateDelegate& Callback)
{
EOculusXRAnchorResult::Type AnchorResult;
return OculusXRAnchors::FOculusXRAnchors::CreateSpatialAnchor(NewAnchorTransform, OwningActor, Callback, AnchorResult);
}
bool UOculusXRSpatialAnchorComponent::Erase(const FOculusXRAnchorEraseDelegate& Callback)
{
EOculusXRAnchorResult::Type AnchorResult;
return OculusXRAnchors::FOculusXRAnchors::EraseAnchor(this, Callback, AnchorResult);
}
bool UOculusXRSpatialAnchorComponent::Save(EOculusXRSpaceStorageLocation Location, const FOculusXRAnchorSaveDelegate& Callback)
{
EOculusXRAnchorResult::Type AnchorResult;
return OculusXRAnchors::FOculusXRAnchors::SaveAnchor(this, Location, Callback, AnchorResult);
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSpatialAnchorManager.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
namespace OculusXRAnchors
{
bool FOculusXRSpatialAnchorManager::CreateSpatialAnchor(const FTransform& InTransform, uint64& OutRequestId)
{
EOculusXRAnchorResult::Type Result = CreateAnchor(InTransform, OutRequestId, FTransform::Identity);
return UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(Result);
}
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,19 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRAnchorManager.h"
namespace OculusXRAnchors
{
struct FOculusXRSpatialAnchorManager : FOculusXRAnchorManager
{
FOculusXRSpatialAnchorManager()
: FOculusXRAnchorManager()
{
}
static bool CreateSpatialAnchor(const FTransform& InTransform, uint64& OutRequestId);
};
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,24 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRTelemetry.h"
namespace OculusXRTelemetry::Events
{
using FAnchorsCreate = TMarker<191967648>;
using FAnchorsCreateRequest = TScopedMarker<FAnchorsCreate, EScopeMode::Start>;
using FAnchorsCreateResponse = TScopedMarker<FAnchorsCreate, EScopeMode::End>;
using FAnchorsSetComponentStatus = TMarker<191962330>;
using FAnchorsSetComponentStatusRequest = TScopedMarker<FAnchorsSetComponentStatus, EScopeMode::Start>;
using FAnchorsSetComponentStatusResponse = TScopedMarker<FAnchorsSetComponentStatus, EScopeMode::End>;
using FAnchorsSave = TMarker<191961984>;
using FAnchorsSaveRequest = TScopedMarker<FAnchorsSave, EScopeMode::Start>;
using FAnchorsSaveResponse = TScopedMarker<FAnchorsSave, EScopeMode::End>;
using FAnchorsQuery = TMarker<191959258>;
using FAnchorsQueryRequest = TScopedMarker<FAnchorsQuery, EScopeMode::Start>;
using FAnchorsQueryResponse = TScopedMarker<FAnchorsQuery, EScopeMode::End>;
using FAnchorsErase = TMarker<191960591>;
using FAnchorsEraseRequest = TScopedMarker<FAnchorsErase, EScopeMode::Start>;
using FAnchorsEraseResponse = TScopedMarker<FAnchorsErase, EScopeMode::End>;
} // namespace OculusXRTelemetry::Events

View File

@@ -0,0 +1,37 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
#define OCULUS_ANCHORS_SUPPORTED_PLATFORMS (PLATFORM_WINDOWS && WINVER > 0x0502) || (PLATFORM_ANDROID_ARM || PLATFORM_ANDROID_ARM64)
/**
* The public interface to this module. In most cases, this interface is only public to sibling modules
* within this plugin.
*/
class IOculusXRAnchorsModule : 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 IOculusXRAnchorsModule& Get()
{
return FModuleManager::LoadModuleChecked<IOculusXRAnchorsModule>("OculusXRAnchors");
}
/**
* 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("OculusXRAnchors");
}
};

View File

@@ -0,0 +1,56 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "OculusXRAnchorTypes.h"
#include "OculusXRAnchorComponents.h"
#include "OculusXRAnchorBPFunctionLibrary.generated.h"
//Helper
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAnchorBPFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Spawn Oculus Anchor Actor", WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"), Category = "OculusXR|SpatialAnchor")
static AActor* SpawnActorWithAnchorHandle(UObject* WorldContextObject, FOculusXRUInt64 Handle, FOculusXRUUID UUID, EOculusXRSpaceStorageLocation AnchorLocation, UClass* ActorClass, AActor* Owner, APawn* Instigator, ESpawnActorCollisionHandlingMethod CollisionHandlingMethod);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Spawn Oculus Anchor Actor From Query", WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true"), Category = "OculusXR|SpatialAnchor")
static AActor* SpawnActorWithAnchorQueryResults(UObject* WorldContextObject, const FOculusXRSpaceQueryResult& QueryResult, UClass* ActorClass, AActor* Owner, APawn* Instigator, ESpawnActorCollisionHandlingMethod CollisionHandlingMethod);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static bool GetAnchorComponentStatus(AActor* TargetActor, EOculusXRSpaceComponentType ComponentType, bool& bIsEnabled);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static bool GetAnchorTransformByHandle(const FOculusXRUInt64& Handle, FTransform& OutTransform);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static bool TryGetAnchorTransformByHandle(const FOculusXRUInt64& Handle, FTransform& OutTransform, FOculusXRAnchorLocationFlags& OutLocationFlags);
UFUNCTION(BlueprintPure, meta = (DisplayName = "FOculusXRUInt64 To String", CompactNodeTitle = "->", BlueprintAutocast), Category = "OculusXR|SpatialAnchor")
static FString AnchorHandleToString(const FOculusXRUInt64 Value);
UFUNCTION(BlueprintPure, meta = (DisplayName = "FOculusXRUUID To String", CompactNodeTitle = "->", BlueprintAutocast), Category = "OculusXR|SpatialAnchor")
static FString AnchorUUIDToString(const FOculusXRUUID& Value);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static FOculusXRUUID StringToAnchorUUID(const FString& Value);
UFUNCTION(BlueprintPure, meta = (DisplayName = "FOculusXRUInt64 equal", CompactNodeTitle = "==", Keywords = "equal", BlueprintAutocast), Category = "OculusXR|SpatialAnchor")
static bool IsEqual_FOculusXRUInt64(const FOculusXRUInt64 Left, const FOculusXRUInt64 Right) { return Left == Right; };
UFUNCTION(BlueprintPure, meta = (DisplayName = "FOculusXRUUID equal", CompactNodeTitle = "==", Keywords = "equal", BlueprintAutocast), Category = "OculusXR|SpatialAnchor")
static bool IsEqual_FOculusXRUUID(const FOculusXRUUID& Left, const FOculusXRUUID& Right) { return Left.IsEqual(Right); };
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static bool IsAnchorResultSuccess(EOculusXRAnchorResult::Type result);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static const UOculusXRBaseAnchorComponent* GetAnchorComponent(const FOculusXRSpaceQueryResult& QueryResult, EOculusXRSpaceComponentType ComponentType, UObject* Outer);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
static bool GetRoomLayout(FOculusXRUInt64 Space, FOculusXRRoomLayout& RoomLayoutOut, int32 MaxWallsCapacity = 64);
};

View File

@@ -0,0 +1,56 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRAnchorTypes.h"
#include "Components/ActorComponent.h"
#include "OculusXRAnchorComponent.generated.h"
UCLASS(meta = (DisplayName = "Oculus Anchor Component"))
class OCULUSXRANCHORS_API UOculusXRAnchorComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOculusXRAnchorComponent(const FObjectInitializer& ObjectInitializer);
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintPure, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
FOculusXRUInt64 GetHandle() const;
UFUNCTION(BlueprintCallable, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
void SetHandle(FOculusXRUInt64 Handle);
UFUNCTION(BlueprintPure, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
bool HasValidHandle() const;
UFUNCTION(BlueprintPure, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
FOculusXRUUID GetUUID() const;
void SetUUID(FOculusXRUUID NewUUID);
UFUNCTION(BlueprintPure, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
bool IsStoredAtLocation(EOculusXRSpaceStorageLocation Location) const;
// Not exposed to BP because this is managed in code
void SetStoredLocation(EOculusXRSpaceStorageLocation Location, bool Stored);
UFUNCTION(BlueprintPure, Category = "OculusXR|Anchor", meta = (DefaultToSelf = Target))
bool IsSaved() const;
protected:
bool bUpdateHeadSpaceTransform;
private:
FOculusXRUInt64 AnchorHandle;
FOculusXRUUID AnchorUUID;
int32 StorageLocations;
UPROPERTY()
class APlayerCameraManager* PlayerCameraManager;
void UpdateAnchorTransform() const;
bool ToWorldSpacePose(FTransform CameraTransform, FTransform& OutTrackingSpaceTransform) const;
};

View File

@@ -0,0 +1,149 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "UObject/Class.h"
#include "OculusXRAnchorTypes.h"
#include "OculusXRAnchorComponents.generated.h"
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRBaseAnchorComponent : public UObject
{
GENERATED_BODY()
public:
template <typename T>
static T* FromSpace(uint64 space, UObject* Outer)
{
T* Component = NewObject<T>(Outer);
Component->Space = space;
return Component;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool IsComponentEnabled() const;
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
EOculusXRSpaceComponentType GetType() const;
uint64 GetSpace() const;
protected:
uint64 Space;
EOculusXRSpaceComponentType Type = EOculusXRSpaceComponentType::Undefined;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRLocatableAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRLocatableAnchorComponent()
{
Type = EOculusXRSpaceComponentType::Locatable;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetTransform(FTransform& outTransform) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRPlaneAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRPlaneAnchorComponent()
{
Type = EOculusXRSpaceComponentType::ScenePlane;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetPositionAndSize(FVector& outPosition, FVector& outSize) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRVolumeAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRVolumeAnchorComponent()
{
Type = EOculusXRSpaceComponentType::SceneVolume;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetPositionAndSize(FVector& outPosition, FVector& outSize) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRSemanticClassificationAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRSemanticClassificationAnchorComponent()
{
Type = EOculusXRSpaceComponentType::SemanticClassification;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetSemanticClassifications(TArray<FString>& outClassifications) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRRoomLayoutAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRRoomLayoutAnchorComponent()
{
Type = EOculusXRSpaceComponentType::RoomLayout;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetRoomLayout(FOculusXRUUID& outFloorUUID, FOculusXRUUID& outCeilingUUID, TArray<FOculusXRUUID>& outWallsUUIDs) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRSpaceContainerAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRSpaceContainerAnchorComponent()
{
Type = EOculusXRSpaceComponentType::SpaceContainer;
}
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor")
bool GetUUIDs(TArray<FOculusXRUUID>& outUUIDs) const;
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRSharableAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRSharableAnchorComponent()
{
Type = EOculusXRSpaceComponentType::Sharable;
}
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRStorableAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRStorableAnchorComponent()
{
Type = EOculusXRSpaceComponentType::Storable;
}
};
UCLASS(Blueprintable)
class OCULUSXRANCHORS_API UOculusXRTriangleMeshAnchorComponent : public UOculusXRBaseAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRTriangleMeshAnchorComponent()
{
Type = EOculusXRSpaceComponentType::TriangleMesh;
}
};

View File

@@ -0,0 +1,122 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreTypes.h"
#include "OculusXRAnchorTypes.h"
#include "Delegates/Delegate.h"
class FOculusXRAnchorEventDelegates
{
public:
/* ovrpEventType_SpatialAnchorCreateComplete
*
* SpatialAnchorCreateComplete
* Prefix:
* FOculusXRSpatialAnchorCreateComplete
* Suffix:
* FOculusXRSpatialAnchorCreateCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_FourParams(FOculusXRSpatialAnchorCreateCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /*result*/, FOculusXRUInt64 /*space*/, FOculusXRUUID /*uuid*/);
static OCULUSXRANCHORS_API FOculusXRSpatialAnchorCreateCompleteDelegate OculusSpatialAnchorCreateComplete;
/* ovrpEventType_SpaceSetComponentStatusComplete
*
* SpaceSetComponentStatusComplete
* Prefix:
* FOculusXRSpaceSetComponentStatusComplete
* Suffix:
* FOculusXRSpaceSetComponentStatusCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_SixParams(FOculusXRSpaceSetComponentStatusCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /*result*/, FOculusXRUInt64 /*space*/, FOculusXRUUID /*uuid*/, EOculusXRSpaceComponentType /*componenttype */, bool /*enabled*/);
static OCULUSXRANCHORS_API FOculusXRSpaceSetComponentStatusCompleteDelegate OculusSpaceSetComponentStatusComplete;
/* ovrpEventType_SpaceQueryResults
*
* SpaceQueryResults
* Prefix:
* FOculusXRSpaceQueryResults
* Suffix:
* FOculusXRSpaceQueryResultsDelegate
*/
DECLARE_MULTICAST_DELEGATE_OneParam(FOculusXRSpaceQueryResultsDelegate, FOculusXRUInt64 /*requestId*/);
static OCULUSXRANCHORS_API FOculusXRSpaceQueryResultsDelegate OculusSpaceQueryResults;
/* SpaceQueryResult (no ovrp event type)
*
* SpaceQueryResult
* Prefix:
* FOculusXRSpaceQueryResult
* Suffix:
* FOculusXRSpaceQueryResultDelegate
*/
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOculusXRSpaceQueryResultDelegate, FOculusXRUInt64 /*requestId*/, FOculusXRUInt64 /* space*/, FOculusXRUUID /*uuid*/);
static OCULUSXRANCHORS_API FOculusXRSpaceQueryResultDelegate OculusSpaceQueryResult;
/* ovrpEventType_SpaceQueryComplete
*
* SpaceQueryComplete
* Prefix:
* FOculusXRSpaceQueryComplete
* Suffix:
* FOculusXRSpaceQueryCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusXRSpaceQueryCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /*result*/);
static OCULUSXRANCHORS_API FOculusXRSpaceQueryCompleteDelegate OculusSpaceQueryComplete;
/* ovrpEventType_SpaceSaveComplete
*
* SpaceSaveComplete
* Prefix:
* FOculusXRSpaceSaveComplete
* Suffix:
* FOculusXRSpaceSaveCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_FiveParams(FOculusXRSpaceSaveCompleteDelegate, FOculusXRUInt64 /*requestId*/, FOculusXRUInt64 /* space*/, bool /* sucess*/, int /*result*/, FOculusXRUUID /*uuid*/);
static OCULUSXRANCHORS_API FOculusXRSpaceSaveCompleteDelegate OculusSpaceSaveComplete;
/* ovrpEventType_SpaceListSaveResult
*
* SpaceListSaveComplete
* Prefix:
* FOculusSpaceListSaveComplete
* Suffix:
* FOculusSpaceListSaveCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusXRSpaceListSaveCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /*result*/);
static OCULUSXRANCHORS_API FOculusXRSpaceListSaveCompleteDelegate OculusSpaceListSaveComplete;
/* ovrpEventType_SpaceEraseComplete
*
* SpaceEraseComplete
* Prefix:
* FOculusXRSpaceEraseComplete
* Suffix:
* FOculusXRSpaceEraseCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_FourParams(FOculusXRSpaceEraseCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /* result*/, FOculusXRUUID /*uuid*/, EOculusXRSpaceStorageLocation /*location*/);
static OCULUSXRANCHORS_API FOculusXRSpaceEraseCompleteDelegate OculusSpaceEraseComplete;
/* ovrpEventType_SpaceShareSpaceResult
*
* SpaceShareComplete
* Prefix:
* FOculusSpaceShareSpacesComplete
* Suffix:
* FOculusSpaceShareSpacesCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusXRSpaceShareCompleteDelegate, FOculusXRUInt64 /*requestId*/, int /*result*/);
static OCULUSXRANCHORS_API FOculusXRSpaceShareCompleteDelegate OculusSpaceShareComplete;
/* ovrpEventType_SceneCaptureComplete
*
* SceneCaptureComplete
* Prefix:
* FOculusXRSceneCaptureComplete
* Suffix:
* FOculusXRSceneCaptureCompleteDelegate
*/
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusXRSceneCaptureCompleteDelegate, FOculusXRUInt64 /*requestId*/, bool /*success*/);
static OCULUSXRANCHORS_API FOculusXRSceneCaptureCompleteDelegate OculusSceneCaptureComplete;
};

View File

@@ -0,0 +1,298 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Templates/SharedPointer.h"
#include "OculusXRAnchorTypes.h"
#include "OculusXRAnchorComponent.h"
#include "OculusXRAnchorComponents.h"
#include "OculusXRAnchorLatentActions.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXR_LatentAction_CreateSpatialAnchor_Success, UOculusXRAnchorComponent*, Anchor, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_CreateSpatialAnchor_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOculusXR_LatentAction_EraseAnchor_Success, AActor*, Actor, FOculusXRUUID, UUID, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_EraseAnchor_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXR_LatentAction_SaveAnchor_Success, UOculusXRAnchorComponent*, Anchor, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_SaveAnchor_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXR_LatentAction_SaveAnchorList_Success, const TArray<UOculusXRAnchorComponent*>&, Anchors, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_SaveAnchorList_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXR_LatentAction_QueryAnchors_Success, const TArray<FOculusXRSpaceQueryResult>&, QueryResults, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_QueryAnchors_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOculusXR_LatentAction_SetComponentStatus_Success, UOculusXRAnchorComponent*, Anchor, EOculusXRSpaceComponentType, ComponentType, bool, Enabled, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_SetComponentStatus_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXR_LatentAction_SetAnchorComponentStatus_Success, UOculusXRBaseAnchorComponent*, Component, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_SetAnchorComponentStatus_Failure, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOculusXR_LatentAction_ShareAnchors_Success, const TArray<UOculusXRAnchorComponent*>&, SharedAnchors, const TArray<FString>&, UserIds, EOculusXRAnchorResult::Type, Result);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusXR_LatentAction_ShareAnchors_Failure, EOculusXRAnchorResult::Type, Result);
//
// Create Anchor
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_CreateSpatialAnchor : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_CreateSpatialAnchor* OculusXRAsyncCreateSpatialAnchor(AActor* TargetActor, const FTransform& AnchorTransform);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_CreateSpatialAnchor_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_CreateSpatialAnchor_Failure Failure;
// Target actor
UPROPERTY(Transient)
AActor* TargetActor;
FTransform AnchorTransform;
private:
void HandleCreateComplete(EOculusXRAnchorResult::Type CreateResult, UOculusXRAnchorComponent* Anchor);
};
//
// Erase Anchor
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_EraseAnchor : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_EraseAnchor* OculusXRAsyncEraseAnchor(AActor* TargetActor);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_EraseAnchor_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_EraseAnchor_Failure Failure;
// Target actor
UPROPERTY(Transient)
AActor* TargetActor;
FOculusXRUInt64 DeleteRequestId;
private:
void HandleEraseAnchorComplete(EOculusXRAnchorResult::Type EraseResult, FOculusXRUUID UUID);
};
//
// Save Anchor
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_SaveAnchor : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_SaveAnchor* OculusXRAsyncSaveAnchor(AActor* TargetActor, EOculusXRSpaceStorageLocation StorageLocation);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SaveAnchor_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SaveAnchor_Failure Failure;
// Target actor
UPROPERTY(Transient)
AActor* TargetActor;
EOculusXRSpaceStorageLocation StorageLocation;
private:
void HandleSaveAnchorComplete(EOculusXRAnchorResult::Type SaveResult, UOculusXRAnchorComponent* Anchor);
};
//
// Save Anchor List
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_SaveAnchorList : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_SaveAnchorList* OculusXRAsyncSaveAnchorList(const TArray<AActor*>& TargetActors, EOculusXRSpaceStorageLocation StorageLocation);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SaveAnchorList_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SaveAnchorList_Failure Failure;
UPROPERTY(Transient)
TArray<UOculusXRAnchorComponent*> TargetAnchors;
EOculusXRSpaceStorageLocation StorageLocation;
private:
void HandleSaveAnchorListComplete(EOculusXRAnchorResult::Type SaveResult, const TArray<UOculusXRAnchorComponent*>& SavedSpaces);
};
//
// Query Anchors
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_QueryAnchors : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_QueryAnchors* OculusXRAsyncQueryAnchors(EOculusXRSpaceStorageLocation Location, const TArray<FOculusXRUUID>& UUIDs);
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_QueryAnchors* OculusXRAsyncQueryAnchorsAdvanced(const FOculusXRSpaceQueryInfo& QueryInfo);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_QueryAnchors_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_QueryAnchors_Failure Failure;
FOculusXRSpaceQueryInfo QueryInfo;
TArray<FOculusXRSpaceQueryResult> QueryResults;
private:
void HandleQueryAnchorsResults(EOculusXRAnchorResult::Type QueryResult, const TArray<FOculusXRSpaceQueryResult>& Results);
};
//
// Set Component Status
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_SetAnchorComponentStatus : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_SetAnchorComponentStatus* OculusXRAsyncSetAnchorComponentStatus(AActor* TargetActor, EOculusXRSpaceComponentType ComponentType, bool bEnabled);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SetComponentStatus_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SetComponentStatus_Failure Failure;
// Target actor
UPROPERTY(Transient)
AActor* TargetActor;
UPROPERTY(Transient)
UOculusXRAnchorComponent* TargetAnchorComponent;
EOculusXRSpaceComponentType ComponentType;
bool bEnabled;
private:
void HandleSetComponentStatusComplete(EOculusXRAnchorResult::Type SetStatusResult, uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool bResultEnabled);
};
//
// Set Anchor Component Status
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_SetComponentStatus : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_SetComponentStatus* OculusXRAsyncSetComponentStatus(UOculusXRBaseAnchorComponent* Component, bool bEnabled);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SetAnchorComponentStatus_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_SetAnchorComponentStatus_Failure Failure;
// Target actor
UPROPERTY(Transient)
UOculusXRBaseAnchorComponent* Component;
bool bEnabled;
private:
void HandleSetComponentStatusComplete(EOculusXRAnchorResult::Type SetStatusResult, uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool bResultEnabled);
};
//
// Share Anchors
//
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAsyncAction_ShareAnchors : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
virtual void Activate() override;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UOculusXRAsyncAction_ShareAnchors* OculusXRAsyncShareAnchors(const TArray<AActor*>& TargetActors, const TArray<FString>& ToShareWithIds);
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_ShareAnchors_Success Success;
UPROPERTY(BlueprintAssignable)
FOculusXR_LatentAction_ShareAnchors_Failure Failure;
// Target Spaces
UPROPERTY(Transient)
TArray<UOculusXRAnchorComponent*> TargetAnchors;
// Users to share with
TArray<uint64> ToShareWithIds;
FOculusXRUInt64 ShareSpacesRequestId;
private:
void HandleShareAnchorsComplete(EOculusXRAnchorResult::Type ShareResult, const TArray<UOculusXRAnchorComponent*>& TargetAnchors, const TArray<uint64>& OculusUserIds);
};
UCLASS()
class OCULUSXRANCHORS_API UOculusXRAnchorLaunchCaptureFlow : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOculusXRAnchorCaptureFlowFinished);
UFUNCTION(BlueprintCallable, Category = "OculusXR|SpatialAnchor", meta = (WorldContext = "WorldContext", BlueprintInternalUseOnly = "true"))
static UOculusXRAnchorLaunchCaptureFlow* LaunchCaptureFlowAsync(const UObject* WorldContext);
void Activate() override;
UPROPERTY(BlueprintAssignable)
FOculusXRAnchorCaptureFlowFinished Success;
UPROPERTY(BlueprintAssignable)
FOculusXRAnchorCaptureFlowFinished Failure;
private:
uint64 Request = 0;
UFUNCTION(CallInEditor)
void OnCaptureFinish(FOculusXRUInt64 RequestId, bool bSuccess);
};

View File

@@ -0,0 +1,293 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include <memory>
#include "OculusXRAnchorTypes.generated.h"
#define OCULUSXR_UUID_SIZE 16
typedef uint8 ovrpXRUuidArray[OCULUSXR_UUID_SIZE];
UENUM(BlueprintType)
namespace EOculusXRAnchorResult
{
enum Type
{
Success = 0,
Success_EventUnavailable = 1,
Success_Pending = 2,
/// Failure
Failure = -1000,
Failure_InvalidParameter = -1001,
Failure_NotInitialized = -1002,
Failure_InvalidOperation = -1003,
Failure_Unsupported = -1004,
Failure_NotYetImplemented = -1005,
Failure_OperationFailed = -1006,
Failure_InsufficientSize = -1007,
Failure_DataIsInvalid = -1008,
Failure_DeprecatedOperation = -1009,
Failure_ErrorLimitReached = -1010,
Failure_ErrorInitializationFailed = -1011,
/// Space error cases
Failure_SpaceCloudStorageDisabled = -2000,
Failure_SpaceMappingInsufficient = -2001,
Failure_SpaceLocalizationFailed = -2002,
Failure_SpaceNetworkTimeout = -2003,
Failure_SpaceNetworkRequestFailed = -2004,
};
} // namespace EOculusXRAnchorResult
UENUM(BlueprintType, meta = (Bitflags))
enum class EOculusLocationFlags : uint8
{
None = 0, // required for the metadata generation
OrientationValid = (1 << 0),
PositionValid = (1 << 1),
OrientationTracked = (1 << 2),
PositionTracked = (1 << 3)
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRAnchorLocationFlags
{
GENERATED_BODY()
public:
FOculusXRAnchorLocationFlags(uint32 InFlags = 0)
: Flags(InFlags) {}
bool OrientationValid() const
{
return Flags & static_cast<uint32>(EOculusLocationFlags::OrientationValid);
}
bool PositionValid() const
{
return Flags & static_cast<uint32>(EOculusLocationFlags::PositionValid);
}
bool OrientationTracked() const
{
return Flags & static_cast<uint32>(EOculusLocationFlags::OrientationTracked);
}
bool PositionTracked() const
{
return Flags & static_cast<uint32>(EOculusLocationFlags::PositionTracked);
}
bool IsValid() const
{
return OrientationValid() && PositionValid();
}
private:
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|SpatialAnchor", meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EOculusLocationFlags"))
int32 Flags;
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRUUID
{
GENERATED_BODY()
FOculusXRUUID();
FOculusXRUUID(const ovrpXRUuidArray& UuidArray);
bool operator==(const FOculusXRUUID& Other) const;
bool operator!=(const FOculusXRUUID& Other) const;
bool IsValidUUID() const;
bool IsEqual(const FOculusXRUUID& Other) const;
friend uint32 GetTypeHash(const FOculusXRUUID& Other);
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
OCULUSXRANCHORS_API friend FArchive& operator<<(FArchive& Ar, FOculusXRUUID& UUID);
bool Serialize(FArchive& Ar);
FString ToString() const;
uint8 UUIDBytes[OCULUSXR_UUID_SIZE];
};
template <>
struct TStructOpsTypeTraits<FOculusXRUUID> : public TStructOpsTypeTraitsBase2<FOculusXRUUID>
{
enum
{
WithIdenticalViaEquality = true,
WithNetSerializer = true,
WithSerializer = true
};
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRUInt64
{
GENERATED_BODY()
FOculusXRUInt64()
: FOculusXRUInt64(0) {}
FOculusXRUInt64(const uint64& Value) { this->Value = Value; }
operator uint64() const { return Value; }
bool operator==(const FOculusXRUInt64& Right) const;
bool operator!=(const FOculusXRUInt64& Right) const;
UPROPERTY()
uint64 Value;
bool IsEqual(const FOculusXRUInt64& Other) const
{
return Other.Value == Value;
}
friend uint32 GetTypeHash(const FOculusXRUInt64& Other)
{
return FCrc::MemCrc_DEPRECATED(&Other.Value, sizeof(Other.Value));
}
uint64 GetValue() const { return Value; };
void SetValue(const uint64 Val) { Value = Val; };
};
template <>
struct TStructOpsTypeTraits<FOculusXRUInt64> : public TStructOpsTypeTraitsBase2<FOculusXRUInt64>
{
enum
{
WithIdenticalViaEquality = true,
};
};
UENUM(BlueprintType)
enum class EOculusXRSpaceQueryFilterType : uint8
{
None = 0 UMETA(DisplayName = "No Filter"),
FilterByIds = 1 UMETA(DisplayName = "Filter queries by UUIDs"),
FilterByComponentType = 2 UMETA(DisplayName = "Filter queries by component type"),
};
// This is used as a bit-mask
UENUM(BlueprintType)
enum class EOculusXRSpaceStorageLocation : uint8
{
Invalid = 0 UMETA(DisplayName = "Invalid"),
Local = 1 << 0 UMETA(DisplayName = "Local"),
Cloud = 1 << 1 UMETA(DisplayName = "Cloud")
};
UENUM(BlueprintType)
enum class EOculusXRSpaceStoragePersistenceMode : uint8
{
Invalid = 0 UMETA(Hidden),
Indefinite = 1 UMETA(DisplayName = "Indefinite"),
};
UENUM(BlueprintType)
enum class EOculusXRSpaceComponentType : uint8
{
Locatable = 0 UMETA(DisplayName = "Locatable"),
Storable = 1 UMETA(DisplayName = "Storable"),
Sharable = 2 UMETA(DisplayName = "Sharable"),
ScenePlane = 3 UMETA(DisplayName = "ScenePlane"),
SceneVolume = 4 UMETA(DisplayName = "SceneVolume"),
SemanticClassification = 5 UMETA(DisplayName = "SemanticClassification"),
RoomLayout = 6 UMETA(DisplayName = "RoomLayout"),
SpaceContainer = 7 UMETA(DisplayName = "SpaceContainer"),
Undefined = 8 UMETA(DisplayName = "Not defined"),
TriangleMesh = 9 UMETA(DisplayName = "TriangleMesh"),
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRSpaceQueryInfo
{
GENERATED_BODY()
public:
FOculusXRSpaceQueryInfo()
: MaxQuerySpaces(1024), Timeout(0), Location(EOculusXRSpaceStorageLocation::Local), FilterType(EOculusXRSpaceQueryFilterType::None)
{
}
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
int MaxQuerySpaces;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
float Timeout;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
EOculusXRSpaceStorageLocation Location;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
EOculusXRSpaceQueryFilterType FilterType;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
TArray<FOculusXRUUID> IDFilter;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
TArray<EOculusXRSpaceComponentType> ComponentFilter;
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRSpaceQueryResult
{
GENERATED_BODY()
public:
FOculusXRSpaceQueryResult()
: Space(0), UUID(), Location(EOculusXRSpaceStorageLocation::Invalid) {}
FOculusXRSpaceQueryResult(FOculusXRUInt64 SpaceHandle, FOculusXRUUID ID, EOculusXRSpaceStorageLocation SpaceLocation)
: Space(SpaceHandle), UUID(ID), Location(SpaceLocation) {}
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
FOculusXRUInt64 Space;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
FOculusXRUUID UUID;
UPROPERTY(BlueprintReadWrite, Category = "OculusXR|SpatialAnchor")
EOculusXRSpaceStorageLocation Location;
};
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRSpaceQueryFilterValues
{
GENERATED_BODY()
public:
TArray<FOculusXRUUID> Uuids; // used if filtering by UUIDs
TArray<EOculusXRSpaceComponentType> ComponentTypes; // used if filtering by component types
};
// Represents a room layout within a specific space
USTRUCT(BlueprintType)
struct OCULUSXRANCHORS_API FOculusXRRoomLayout
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
FOculusXRUInt64 RoomAnchorHandle;
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
FOculusXRUUID RoomUuid;
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
FOculusXRUUID FloorUuid;
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
FOculusXRUUID CeilingUuid;
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
TArray<FOculusXRUUID> WallsUuid;
UPROPERTY(BlueprintReadOnly, Category = "OculusXR|Anchors")
TArray<FOculusXRUUID> RoomObjectUUIDs;
};

View File

@@ -0,0 +1,146 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRAnchorComponent.h"
#include "OculusXRAnchorTypes.h"
DECLARE_DELEGATE_TwoParams(FOculusXRSpatialAnchorCreateDelegate, EOculusXRAnchorResult::Type /*Result*/, UOculusXRAnchorComponent* /*Anchor*/);
DECLARE_DELEGATE_TwoParams(FOculusXRAnchorEraseDelegate, EOculusXRAnchorResult::Type /*Result*/, FOculusXRUUID /*AnchorUUID*/);
DECLARE_DELEGATE_FourParams(FOculusXRAnchorSetComponentStatusDelegate, EOculusXRAnchorResult::Type /*Result*/, uint64 /*AnchorHandle*/, EOculusXRSpaceComponentType /*ComponentType*/, bool /*Enabled*/);
DECLARE_DELEGATE_TwoParams(FOculusXRAnchorSaveDelegate, EOculusXRAnchorResult::Type /*Result*/, UOculusXRAnchorComponent* /*Anchor*/);
DECLARE_DELEGATE_TwoParams(FOculusXRAnchorSaveListDelegate, EOculusXRAnchorResult::Type /*Result*/, const TArray<UOculusXRAnchorComponent*>& /*SavedAnchors*/);
DECLARE_DELEGATE_TwoParams(FOculusXRAnchorQueryDelegate, EOculusXRAnchorResult::Type /*Result*/, const TArray<FOculusXRSpaceQueryResult>& /*Results*/);
DECLARE_DELEGATE_ThreeParams(FOculusXRAnchorShareDelegate, EOculusXRAnchorResult::Type /*Result*/, const TArray<UOculusXRAnchorComponent*>& /*Anchors*/, const TArray<uint64>& /*Users*/);
namespace OculusXRAnchors
{
struct OCULUSXRANCHORS_API FOculusXRAnchors
{
void Initialize();
void Teardown();
static FOculusXRAnchors* GetInstance();
static bool CreateSpatialAnchor(const FTransform& InTransform, AActor* TargetActor, const FOculusXRSpatialAnchorCreateDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool EraseAnchor(UOculusXRAnchorComponent* Anchor, const FOculusXRAnchorEraseDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool DestroyAnchor(uint64 AnchorHandle, EOculusXRAnchorResult::Type& OutResult);
static bool SetAnchorComponentStatus(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, const FOculusXRAnchorSetComponentStatusDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool GetAnchorComponentStatus(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending, EOculusXRAnchorResult::Type& OutResult);
static bool GetAnchorSupportedComponents(UOculusXRAnchorComponent* Anchor, TArray<EOculusXRSpaceComponentType>& OutSupportedComponents, EOculusXRAnchorResult::Type& OutResult);
static bool SetComponentStatus(uint64 Space, EOculusXRSpaceComponentType SpaceComponentType, bool Enable, float Timeout, const FOculusXRAnchorSetComponentStatusDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool GetComponentStatus(uint64 AnchorHandle, EOculusXRSpaceComponentType SpaceComponentType, bool& OutEnabled, bool& OutChangePending, EOculusXRAnchorResult::Type& OutResult);
static bool GetSupportedComponents(uint64 AnchorHandle, TArray<EOculusXRSpaceComponentType>& OutSupportedComponents, EOculusXRAnchorResult::Type& OutResult);
static bool SaveAnchor(UOculusXRAnchorComponent* Anchor, EOculusXRSpaceStorageLocation StorageLocation, const FOculusXRAnchorSaveDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool SaveAnchorList(const TArray<UOculusXRAnchorComponent*>& Anchors, EOculusXRSpaceStorageLocation StorageLocation, const FOculusXRAnchorSaveListDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool QueryAnchors(const TArray<FOculusXRUUID>& AnchorUUIDs, EOculusXRSpaceStorageLocation Location, const FOculusXRAnchorQueryDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool QueryAnchorsAdvanced(const FOculusXRSpaceQueryInfo& QueryInfo, const FOculusXRAnchorQueryDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool ShareAnchors(const TArray<UOculusXRAnchorComponent*>& Anchors, const TArray<uint64>& OculusUserIDs, const FOculusXRAnchorShareDelegate& ResultCallback, EOculusXRAnchorResult::Type& OutResult);
static bool GetSpaceContainerUUIDs(uint64 Space, TArray<FOculusXRUUID>& OutUUIDs, EOculusXRAnchorResult::Type& OutResult);
static bool GetSpaceScenePlane(uint64 Space, FVector& OutPos, FVector& OutSize, EOculusXRAnchorResult::Type& OutResult);
static bool GetSpaceSceneVolume(uint64 Space, FVector& OutPos, FVector& OutSize, EOculusXRAnchorResult::Type& OutResult);
static bool GetSpaceSemanticClassification(uint64 Space, TArray<FString>& OutSemanticClassifications, EOculusXRAnchorResult::Type& OutResult);
static bool GetSpaceBoundary2D(uint64 Space, TArray<FVector2f>& OutVertices, EOculusXRAnchorResult::Type& OutResult);
private:
void HandleSpatialAnchorCreateComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUInt64 Space, FOculusXRUUID UUID);
void HandleAnchorEraseComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUUID UUID, EOculusXRSpaceStorageLocation Location);
void HandleSetComponentStatusComplete(FOculusXRUInt64 RequestId, int Result, FOculusXRUInt64 Space, FOculusXRUUID UUID, EOculusXRSpaceComponentType ComponentType, bool Enabled);
void HandleAnchorSaveComplete(FOculusXRUInt64 RequestId, FOculusXRUInt64 Space, bool Success, int Result, FOculusXRUUID UUID);
void HandleAnchorSaveListComplete(FOculusXRUInt64 RequestId, int Result);
void HandleAnchorQueryResultsBegin(FOculusXRUInt64 RequestId);
void HandleAnchorQueryResultElement(FOculusXRUInt64 RequestId, FOculusXRUInt64 Space, FOculusXRUUID UUID);
void HandleAnchorQueryComplete(FOculusXRUInt64 RequestId, int Result);
void HandleAnchorSharingComplete(FOculusXRUInt64 RequestId, int Result);
struct EraseAnchorBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorEraseDelegate Binding;
TWeakObjectPtr<UOculusXRAnchorComponent> Anchor;
};
struct SetComponentStatusBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorSetComponentStatusDelegate Binding;
uint64 AnchorHandle;
};
struct CreateAnchorBinding
{
FOculusXRUInt64 RequestId;
FOculusXRSpatialAnchorCreateDelegate Binding;
TWeakObjectPtr<AActor> Actor;
};
struct SaveAnchorBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorSaveDelegate Binding;
EOculusXRSpaceStorageLocation Location;
TWeakObjectPtr<UOculusXRAnchorComponent> Anchor;
};
struct SaveAnchorListBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorSaveListDelegate Binding;
EOculusXRSpaceStorageLocation Location;
TArray<TWeakObjectPtr<UOculusXRAnchorComponent>> SavedAnchors;
};
struct AnchorQueryBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorQueryDelegate Binding;
EOculusXRSpaceStorageLocation Location;
TArray<FOculusXRSpaceQueryResult> Results;
};
struct ShareAnchorsBinding
{
FOculusXRUInt64 RequestId;
FOculusXRAnchorShareDelegate Binding;
TArray<TWeakObjectPtr<UOculusXRAnchorComponent>> SharedAnchors;
TArray<uint64> OculusUserIds;
};
// Delegate bindings
TMap<uint64, CreateAnchorBinding> CreateSpatialAnchorBindings;
TMap<uint64, EraseAnchorBinding> EraseAnchorBindings;
TMap<uint64, SetComponentStatusBinding> SetComponentStatusBindings;
TMap<uint64, SaveAnchorBinding> AnchorSaveBindings;
TMap<uint64, SaveAnchorListBinding> AnchorSaveListBindings;
TMap<uint64, AnchorQueryBinding> AnchorQueryBindings;
TMap<uint64, ShareAnchorsBinding> ShareAnchorsBindings;
// Delegate handles
FDelegateHandle DelegateHandleAnchorCreate;
FDelegateHandle DelegateHandleAnchorErase;
FDelegateHandle DelegateHandleSetComponentStatus;
FDelegateHandle DelegateHandleAnchorSave;
FDelegateHandle DelegateHandleAnchorSaveList;
FDelegateHandle DelegateHandleQueryResultsBegin;
FDelegateHandle DelegateHandleQueryResultElement;
FDelegateHandle DelegateHandleQueryComplete;
FDelegateHandle DelegateHandleAnchorShare;
};
} // namespace OculusXRAnchors

View File

@@ -0,0 +1,64 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRSpatialAnchorComponent.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRRoomLayoutManagerComponent.generated.h"
UCLASS(meta = (DisplayName = "OculusXR Room Layout Manager Component", BlueprintSpawnableComponent))
class OCULUSXRANCHORS_API UOculusXRRoomLayoutManagerComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOculusXRRoomLayoutManagerComponent(const FObjectInitializer& ObjectInitializer);
virtual void InitializeComponent() override;
virtual void UninitializeComponent() override;
virtual void OnRegister() override;
virtual void OnUnregister() override;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusXRRoomLayoutSceneCaptureCompleteDelegate,
FOculusXRUInt64, requestId,
bool, result);
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusXRRoomLayoutSceneCompleteNativeDelegate, FOculusXRUInt64 /*requestId*/, bool /*success*/);
FOculusXRRoomLayoutSceneCompleteNativeDelegate OculusXRRoomLayoutSceneCaptureCompleteNative;
UPROPERTY(BlueprintAssignable, Category = "OculusXR|Room Layout Manager")
FOculusXRRoomLayoutSceneCaptureCompleteDelegate OculusXRRoomLayoutSceneCaptureComplete;
// Requests to launch Capture Flow
UFUNCTION(BlueprintCallable, Category = "OculusXR|Room Layout Manager")
bool LaunchCaptureFlow();
// Gets room layout for a specific space
UFUNCTION(BlueprintCallable, Category = "OculusXR|Room Layout Manager")
bool GetRoomLayout(FOculusXRUInt64 Space, UPARAM(ref) FOculusXRRoomLayout& RoomLayoutOut, int32 MaxWallsCapacity = 64);
// Loads mesh data (vertices, indeces) associated with the space into UProceduralMeshComponent
UFUNCTION(BlueprintCallable, Category = "OculusXR|Room Layout Manager")
bool LoadTriangleMesh(FOculusXRUInt64 Space, class UProceduralMeshComponent* Mesh, bool CreateCollision) const;
protected:
UPROPERTY(Transient)
TSet<uint64> EntityRequestList;
UPROPERTY(Transient)
TMap<FOculusXRUInt64, FOculusXRRoomLayout> RoomLayouts;
private:
UFUNCTION()
void OculusRoomLayoutSceneCaptureComplete_Handler(FOculusXRUInt64 RequestId, bool bSuccess)
{
if (EntityRequestList.Find(RequestId.Value) != nullptr)
{
OculusXRRoomLayoutSceneCaptureComplete.Broadcast(RequestId, bSuccess);
OculusXRRoomLayoutSceneCaptureCompleteNative.Broadcast(RequestId, bSuccess);
EntityRequestList.Remove(RequestId.Value);
}
}
};

View File

@@ -0,0 +1,25 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRAnchorComponent.h"
#include "OculusXRAnchors.h"
#include "OculusXRSpatialAnchorComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusSpatialAnchor, Log, All);
UCLASS(meta = (DisplayName = "Oculus Spatial Anchor Component", BlueprintSpawnableComponent))
class OCULUSXRANCHORS_API UOculusXRSpatialAnchorComponent : public UOculusXRAnchorComponent
{
GENERATED_BODY()
public:
UOculusXRSpatialAnchorComponent(const FObjectInitializer& ObjectInitializer);
static bool Create(const FTransform& NewAnchorTransform, AActor* OwningActor, const FOculusXRSpatialAnchorCreateDelegate& Callback);
bool Erase(const FOculusXRAnchorEraseDelegate& Callback);
bool Save(EOculusXRSpaceStorageLocation Location, const FOculusXRAnchorSaveDelegate& Callback);
private:
};

View File

@@ -0,0 +1,55 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class OculusXREditor : ModuleRules
{
public OculusXREditor(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
PrivateDependencyModuleNames.AddRange(
new string[] {
"Projects",
"InputCore",
"UnrealEd",
"LevelEditor",
"CoreUObject",
"Engine",
"EngineSettings",
"AndroidRuntimeSettings",
"Slate",
"SlateCore",
"EditorStyle",
"Core",
"OculusXRHMD",
"OculusXRMovement",
"OculusXRPassthrough",
"OVRPluginXR",
"OculusXRProjectSetupTool",
"HTTP",
"DesktopPlatform",
"LauncherServices",
"GameProjectGeneration",
"SharedSettingsWidgets",
"RHI",
"SourceControl",
}
);
PrivateIncludePaths.AddRange(
new string[] {
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
"OculusXREditor/Private",
"OculusXRHMD/Private"
});
PrivateIncludePathModuleNames.AddRange(
new string[] {
"Settings",
"OculusXRProjectSetupTool"
}
);
}
}

View File

@@ -0,0 +1,325 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRBuildAnalytics.h"
#include "GameProjectGenerationModule.h"
#include "OculusXRHMDModule.h"
#include "Runtime/Core/Public/HAL/FileManager.h"
FOculusBuildAnalytics* FOculusBuildAnalytics::instance = 0;
FOculusBuildAnalytics* FOculusBuildAnalytics::GetInstance()
{
if (IOculusXRHMDModule::IsAvailable())
{
if (instance == nullptr)
{
instance = new FOculusBuildAnalytics();
}
}
return instance;
}
bool FOculusBuildAnalytics::IsOculusXRHMDAvailable()
{
return IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::Get().PreInit();
}
void FOculusBuildAnalytics::Shutdown()
{
}
FOculusBuildAnalytics::FOculusBuildAnalytics()
{
bool TelemetryEnabled = false;
if (!GConfig->GetBool(TEXT("/Script/OculusXREditor.OculusXREditorSettings"), TEXT("bEnableOculusBuildTelemetry"), TelemetryEnabled, GEditorIni))
{
GConfig->SetBool(TEXT("/Script/OculusXREditor.OculusXREditorSettings"), TEXT("bEnableOculusBuildTelemetry"), TelemetryEnabled, GEditorIni);
GConfig->Flush(0);
}
if (TelemetryEnabled)
{
RegisterLauncherCallback();
}
}
void FOculusBuildAnalytics::OnTelemetryToggled(bool Enabled)
{
if (Enabled)
{
RegisterLauncherCallback();
}
else
{
if (LauncherCallbackHandle.IsValid())
{
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<ILauncherServicesModule>("LauncherServices");
ProjectLauncherServicesModule.OnCreateLauncherDelegate.Remove(LauncherCallbackHandle);
}
}
}
void FOculusBuildAnalytics::RegisterLauncherCallback()
{
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<ILauncherServicesModule>("LauncherServices");
LauncherCallbackHandle = ProjectLauncherServicesModule.OnCreateLauncherDelegate.AddRaw(this, &FOculusBuildAnalytics::OnLauncherCreated);
}
void FOculusBuildAnalytics::OnLauncherCreated(ILauncherRef Launcher)
{
// Add callback for when launcher worker is started
Launcher->FLauncherWorkerStartedDelegate.AddRaw(this, &FOculusBuildAnalytics::OnLauncherWorkerStarted);
}
void FOculusBuildAnalytics::OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker, ILauncherProfileRef Profile)
{
TArray<FString> Platforms = Profile.Get().GetCookedPlatforms();
if (Platforms.Num() == 1)
{
if (Platforms[0].Equals("Android_ASTC") || Platforms[0].Contains("Windows"))
{
CurrentBuildStage = UNDEFINED_STAGE;
AndroidPackageTime = 0;
UATLaunched = false;
BuildCompleted = false;
CurrentBuildPlatform = Platforms[0];
TotalBuildTime = 0;
BuildStepCount = 0;
FOculusXRHMDModule::GetPluginWrapper().SetDeveloperMode(true);
OutputDirectory = Profile.Get().GetPackageDirectory();
// Assign callbacks for stages
LauncherWorker.Get()->OnStageCompleted().AddRaw(this, &FOculusBuildAnalytics::OnStageCompleted);
LauncherWorker.Get()->OnOutputReceived().AddRaw(this, &FOculusBuildAnalytics::OnBuildOutputReceived);
LauncherWorker.Get()->OnStageStarted().AddRaw(this, &FOculusBuildAnalytics::OnStageStarted);
LauncherWorker.Get()->OnCompleted().AddRaw(this, &FOculusBuildAnalytics::OnCompleted);
// Get information on what oculus platform we are building for and also the OS platform
FString OculusPlatform;
if (CurrentBuildPlatform.Equals("Android_ASTC"))
{
UEnum* OculusMobileDevices = StaticEnum<EOculusMobileDevice::Type>();
UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
TArray<TEnumAsByte<EOculusMobileDevice::Type>> TargetOculusDevices = Settings->PackageForOculusMobile;
TArray<FString> Devices;
if (TargetOculusDevices.Contains(EOculusMobileDevice::Quest2))
{
Devices.Add("quest2");
}
OculusPlatform = FString::Join(Devices, TEXT("_"));
}
else if (CurrentBuildPlatform.Contains("Windows"))
{
CurrentBuildPlatform = "Windows";
OculusPlatform = "rift";
}
// Count user asset files
UserAssetCount = 0;
TArray<FString> FileNames;
IFileManager::Get().FindFilesRecursive(FileNames, *FPaths::ProjectContentDir(), TEXT("*.*"), true, false, false);
UserAssetCount = FileNames.Num();
// Count user script files
FGameProjectGenerationModule& GameProjectModule = FModuleManager::LoadModuleChecked<FGameProjectGenerationModule>(TEXT("GameProjectGeneration"));
SourceFileCount = 0;
SourceFileDirectorySize = 0;
GameProjectModule.Get().GetProjectSourceDirectoryInfo(SourceFileCount, SourceFileDirectorySize);
// Generate build GUID
FGuid guid = FGuid::NewGuid();
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_guid", TCHAR_TO_ANSI(*guid.ToString()));
// Send build start event with corresponding metadata
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("asset_count", TCHAR_TO_ANSI(*FString::FromInt(UserAssetCount)));
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("script_count", TCHAR_TO_ANSI(*FString::FromInt(SourceFileCount)));
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("target_platform", TCHAR_TO_ANSI(*CurrentBuildPlatform));
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("target_oculus_platform", TCHAR_TO_ANSI(*OculusPlatform));
TArray<ILauncherTaskPtr> TaskList;
LauncherWorker->GetTasks(TaskList);
BuildStepCount = TaskList.Num();
}
}
}
void FOculusBuildAnalytics::OnCompleted(bool Succeeded, double TotalTime, int32 ErrorCode)
{
if (!BuildCompleted && Succeeded)
{
SendBuildCompleteEvent(TotalTime);
}
}
void FOculusBuildAnalytics::OnStageCompleted(const FString& StageName, double Time)
{
if (CurrentBuildStage != UNDEFINED_STAGE)
{
FString TaskName;
switch (CurrentBuildStage)
{
case COOK_IN_EDITOR_STAGE:
TaskName = "build_step_editor_cook";
break;
case LAUNCH_UAT_STAGE:
TaskName = "build_step_launch_uat";
break;
case COMPILE_STAGE:
TaskName = "build_step_compile";
break;
case COOK_STAGE:
TaskName = "build_step_cook";
break;
case DEPLOY_STAGE:
TaskName = "build_step_deploy";
break;
case PACKAGE_STAGE:
TaskName = "build_step_package";
break;
case RUN_STAGE:
return;
default:
TaskName = "build_step_undefined";
break;
}
if (AndroidPackageTime > 0)
{
Time -= AndroidPackageTime;
}
TotalBuildTime += Time;
FOculusXRHMDModule::GetPluginWrapper().SendEvent2(TCHAR_TO_ANSI(*TaskName), TCHAR_TO_ANSI(*FString::SanitizeFloat(Time)), "ovrbuild");
}
}
void FOculusBuildAnalytics::OnStageStarted(const FString& StageName)
{
if (StageName.Equals("Cooking in the editor"))
{
CurrentBuildStage = COOK_IN_EDITOR_STAGE;
}
else if (StageName.Equals("Build Task") && CurrentBuildStage == LAUNCH_UAT_STAGE)
{
CurrentBuildStage = COMPILE_STAGE;
}
else if (StageName.Equals("Build Task"))
{
CurrentBuildStage = LAUNCH_UAT_STAGE;
}
else if (StageName.Equals("Cook Task"))
{
CurrentBuildStage = COOK_STAGE;
}
else if (StageName.Equals("Package Task"))
{
CurrentBuildStage = PACKAGE_STAGE;
}
else if (StageName.Equals("Deploy Task"))
{
CurrentBuildStage = DEPLOY_STAGE;
}
else if (StageName.Equals("Run Task"))
{
CurrentBuildStage = RUN_STAGE;
SendBuildCompleteEvent(TotalBuildTime);
BuildCompleted = true;
}
else
{
CurrentBuildStage = UNDEFINED_STAGE;
}
}
void FOculusBuildAnalytics::OnBuildOutputReceived(const FString& Message)
{
if (CurrentBuildPlatform.Equals("Android_ASTC") && (CurrentBuildStage == DEPLOY_STAGE || CurrentBuildStage == PACKAGE_STAGE))
{
if (Message.Contains("BUILD SUCCESSFUL"))
{
FString Text, Time;
Message.Split("in", &Text, &Time);
if (!Time.IsEmpty())
{
FString SMinutes, SSeconds;
if (Time.Contains("m"))
{
Time.Split("m", &SMinutes, &SSeconds);
}
else
{
SSeconds = Time;
}
int Minutes = FCString::Atoi(*SMinutes);
int Seconds = FCString::Atoi(*SSeconds);
AndroidPackageTime = Minutes * 60 + Seconds;
FOculusXRHMDModule::GetPluginWrapper().SendEvent2("build_step_gradle_build", TCHAR_TO_ANSI(*FString::SanitizeFloat(AndroidPackageTime)), "ovrbuild");
}
}
}
}
void FOculusBuildAnalytics::SendBuildCompleteEvent(float TotalTime)
{
if (CurrentBuildPlatform.Equals("Android_ASTC"))
{
int64 APKTotalSize = 0;
TArray<FString> FoundAPKs;
OutputDirectory = FPaths::ProjectDir() + "Binaries/Android";
OutputDirectory = FPaths::ConvertRelativePathToFull(OutputDirectory);
IFileManager::Get().FindFiles(FoundAPKs, *FPaths::Combine(OutputDirectory, TEXT("*.apk")), true, false);
FDateTime LatestTime = FDateTime(0);
FString LatestAPK;
for (int i = 0; i < FoundAPKs.Num(); i++)
{
FDateTime APKCreationTime = IFileManager::Get().GetTimeStamp(*FPaths::Combine(OutputDirectory, FoundAPKs[i]));
if (APKCreationTime > LatestTime)
{
LatestTime = APKCreationTime;
LatestAPK = FoundAPKs[i];
}
}
TArray<FString> FoundOBBs;
LatestTime = FDateTime(0);
FString LatestOBB;
IFileManager::Get().FindFiles(FoundOBBs, *FPaths::Combine(OutputDirectory, TEXT("*.obb")), true, false);
for (int i = 0; i < FoundOBBs.Num(); i++)
{
FDateTime OBBCreationTime = IFileManager::Get().GetTimeStamp(*FPaths::Combine(OutputDirectory, FoundOBBs[i]));
if (OBBCreationTime > LatestTime)
{
LatestTime = OBBCreationTime;
LatestOBB = FoundOBBs[i];
}
}
if (!LatestAPK.IsEmpty())
{
APKTotalSize += IFileManager::Get().FileSize(*FPaths::Combine(OutputDirectory, LatestAPK));
}
if (!LatestOBB.IsEmpty())
{
APKTotalSize += IFileManager::Get().FileSize(*FPaths::Combine(OutputDirectory, LatestOBB));
}
if (APKTotalSize > 0)
{
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_output_size", TCHAR_TO_ANSI(*FString::FromInt(APKTotalSize)));
}
}
FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_step_count", TCHAR_TO_ANSI(*FString::FromInt(BuildStepCount)));
FOculusXRHMDModule::GetPluginWrapper().SendEvent2("build_complete", TCHAR_TO_ANSI(*FString::SanitizeFloat(TotalTime)), "ovrbuild");
}

View File

@@ -0,0 +1,62 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ILauncherServicesModule.h"
#include "ILauncher.h"
#include "Modules/ModuleManager.h"
#include "UObject/Class.h"
#include "AndroidRuntimeSettings.h"
#include "OculusXRPluginWrapper.h"
enum EBuildStage
{
UNDEFINED_STAGE,
COOK_IN_EDITOR_STAGE,
COOK_STAGE,
LAUNCH_UAT_STAGE,
COMPILE_STAGE,
PACKAGE_STAGE,
DEPLOY_STAGE,
RUN_STAGE,
};
class FOculusBuildAnalytics
{
public:
static FOculusBuildAnalytics* GetInstance();
static void Shutdown();
static bool IsOculusXRHMDAvailable();
void RegisterLauncherCallback();
void OnTelemetryToggled(bool Enabled);
void OnLauncherCreated(ILauncherRef Launcher);
void OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker, ILauncherProfileRef Profile);
void OnStageCompleted(const FString& StageName, double Time);
void OnStageStarted(const FString& StageName);
void OnBuildOutputReceived(const FString& Message);
void OnCompleted(bool Succeeded, double TotalTime, int32 ErrorCode);
void SendBuildCompleteEvent(float TotalTime);
private:
FOculusBuildAnalytics();
static FOculusBuildAnalytics* instance;
FDelegateHandle LauncherCallbackHandle;
float TotalBuildTime;
float AndroidPackageTime;
bool BuildCompleted;
bool UATLaunched;
int UserAssetCount;
int BuildStepCount;
int32 SourceFileCount;
int64 SourceFileDirectorySize;
EBuildStage CurrentBuildStage;
FString CurrentBuildPlatform;
FString OutputDirectory;
};

View File

@@ -0,0 +1,598 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXREditorModule.h"
#include "AssetToolsModule.h"
#include "OculusXRToolStyle.h"
#include "OculusXRToolCommands.h"
#include "OculusXRPlatformToolWidget.h"
#include "OculusXRAssetDirectory.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "IOculusXRProjectSetupModule.h"
#include "OculusXRHMDTypes.h"
#include "LevelEditor.h"
#include "Modules/ModuleManager.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Input/SButton.h"
#include "PropertyEditorModule.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "GeneralProjectSettings.h"
#include "IAssetTools.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ISettingsModule.h"
#include "OculusXRPassthroughColorLutAsset.h"
#include "OculusXRHMDModule.h"
#include "OculusXRPrivacyNotification.h"
#include "OculusXRSettingsToggle.h"
#include "OculusXRTelemetryPrivacySettings.h"
#include "OculusXRTelemetry.h"
#include "OculusXRTelemetryEditorEvents.h"
#include "SExternalImageReference.h"
#include "AndroidRuntimeSettings.h"
#include "SourceControlHelpers.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Editor/EditorPerformanceSettings.h"
#define LOCTEXT_NAMESPACE "OculusXREditor"
const FName FOculusXREditorModule::OculusPlatToolTabName = FName("OculusXRPlaformTool");
void FOculusXREditorModule::PostLoadCallback()
{
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
}
void FOculusXREditorModule::StartupModule()
{
bModuleValid = true;
RegisterSettings();
FOculusAssetDirectory::LoadForCook();
if (!IsRunningCommandlet())
{
FOculusToolStyle::Initialize();
FOculusToolStyle::ReloadTextures();
FOculusToolCommands::Register();
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
FOculusToolCommands::Get().OpenProjectSetupTool,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::PluginOpenSetupToolWindow),
FCanExecuteAction());
PluginCommands->MapAction(
FOculusToolCommands::Get().OpenPlatWindow,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::PluginOpenPlatWindow),
FCanExecuteAction());
PluginCommands->MapAction(
FOculusToolCommands::Get().ToggleDeploySo,
FExecuteAction::CreateLambda([=]() {
UOculusXRHMDRuntimeSettings* settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
settings->bDeploySoToDevice = !settings->bDeploySoToDevice;
settings->Modify(true);
settings->UpdateSinglePropertyInConfigFile(settings->GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bDeploySoToDevice)), settings->GetDefaultConfigFilename());
}),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([=]() {
return GetMutableDefault<UOculusXRHMDRuntimeSettings>()->bDeploySoToDevice;
}));
PluginCommands->MapAction(
FOculusToolCommands::Get().ToggleMetaXRSim,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::ToggleOpenXRRuntime),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([=]() {
return FOculusXRHMDModule::IsSimulatorActivated();
}));
PluginCommands->MapAction(
FOculusToolCommands::Get().LaunchGameRoom,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::LaunchSESGameRoom),
FCanExecuteAction());
PluginCommands->MapAction(
FOculusToolCommands::Get().LaunchLivingRoom,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::LaunchSESLivingRoom),
FCanExecuteAction());
PluginCommands->MapAction(
FOculusToolCommands::Get().LaunchBedroom,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::LaunchSESBedroom),
FCanExecuteAction());
PluginCommands->MapAction(
FOculusToolCommands::Get().StopServer,
FExecuteAction::CreateRaw(this, &FOculusXREditorModule::StopSESServer),
FCanExecuteAction());
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
// Adds an option to launch the tool to Window->Developer Tools.
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
MenuExtender->AddMenuExtension("Miscellaneous", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FOculusXREditorModule::AddMenuExtension));
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
// We add the Oculus menu on the toolbar
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
ToolbarExtender->AddToolBarExtension("Play", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FOculusXREditorModule::AddToolbarExtension));
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(OculusPlatToolTabName, FOnSpawnTab::CreateRaw(this, &FOculusXREditorModule::OnSpawnPlatToolTab)).SetDisplayName(LOCTEXT("FOculusPlatfToolTabTitle", "Meta XR Platform Tool")).SetMenuType(ETabSpawnerMenuType::Hidden);
// Register asset types
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
AssetTools.RegisterAssetTypeActions(MakeShareable(new FAssetTypeActions_OculusXRPassthroughColorLut));
OculusXRTelemetry::PropagateTelemetryConsent();
// If needed, open a notification here.
OculusXRTelemetry::SpawnNotification();
const UGeneralProjectSettings& ProjectSettings = *GetDefault<UGeneralProjectSettings>();
const FString ProjectIdString = ProjectSettings.ProjectID.ToString();
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FEditorStart> StartEvent;
const auto& Annotated = StartEvent.AddAnnotation("project_hash", StringCast<ANSICHAR>(*ProjectIdString).Get());
UEditorPerformanceSettings* EditorPerformanceSettings = GetMutableDefault<UEditorPerformanceSettings>();
if (EditorPerformanceSettings->bOverrideMaxViewportRenderingResolution)
{
UE_LOG(LogTemp, Warning, TEXT("Existing value for UEditorPerformanceSettings::MaxViewportRenderingResolution will be overriden."));
}
UE_LOG(LogTemp, Log, TEXT("MetaXR ignores max viewport resolution in editor to support full HMD resolutions."));
EditorPerformanceSettings->bOverrideMaxViewportRenderingResolution = true;
EditorPerformanceSettings->MaxViewportRenderingResolution = 0;
FPropertyChangedEvent DisabledMaxResolutionEvent(EditorPerformanceSettings->GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UEditorPerformanceSettings, MaxViewportRenderingResolution)), EPropertyChangeType::ValueSet);
EditorPerformanceSettings->PostEditChangeProperty(DisabledMaxResolutionEvent);
}
}
void FOculusXREditorModule::ShutdownModule()
{
if (!bModuleValid)
{
return;
}
if (!IsRunningCommandlet())
{
FOculusToolStyle::Shutdown();
FOculusToolCommands::Unregister();
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(OculusPlatToolTabName);
}
FOculusAssetDirectory::ReleaseAll();
if (UObjectInitialized())
{
UnregisterSettings();
}
}
TSharedRef<SDockTab> FOculusXREditorModule::OnSpawnPlatToolTab(const FSpawnTabArgs& SpawnTabArgs)
{
/* clang-format off */
auto myTab = SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
SNew(SOculusPlatformToolWidget)
];
/* clang-format on */
return myTab;
}
void FOculusXREditorModule::RegisterSettings()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->RegisterSettings("Project", "Plugins", "OculusXR",
LOCTEXT("RuntimeSettingsName", "Meta XR"),
LOCTEXT("RuntimeSettingsDescription", "Configure the Meta XR plugin"),
GetMutableDefault<UOculusXRHMDRuntimeSettings>());
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomClassLayout(UOculusXRHMDRuntimeSettings::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FOculusXRHMDSettingsDetailsCustomization::MakeInstance));
SettingsModule->RegisterSettings("Editor", "Privacy", "OculusXR",
LOCTEXT("PrivacyTelemetrySettingsName", "MetaXR Usage Data"),
LOCTEXT("PrivacyTelemetrySettingsDescription", "Configure the way MetaXR usage information is handled."),
GetMutableDefault<UOculusXRTelemetryPrivacySettings>());
PropertyModule.RegisterCustomClassLayout(UOculusXRTelemetryPrivacySettings::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FOculusXRSettingsToggle::MakeInstance));
}
}
void FOculusXREditorModule::UnregisterSettings()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "Plugins", "OculusXR");
SettingsModule->UnregisterSettings("Editor", "Privacy", "OculusXR");
}
}
FReply FOculusXREditorModule::PluginClickFn(bool text)
{
PluginOpenSetupToolWindow();
return FReply::Handled();
}
void FOculusXREditorModule::PluginOpenSetupToolWindow()
{
IOculusXRProjectSetupToolModule::Get().ShowProjectSetupTool("Meta Menu");
}
void FOculusXREditorModule::PluginOpenPlatWindow()
{
FGlobalTabmanager::Get()->TryInvokeTab(OculusPlatToolTabName);
}
void FOculusXREditorModule::ToggleOpenXRRuntime()
{
FOculusXRHMDModule::ToggleOpenXRRuntime();
}
void FOculusXREditorModule::LaunchSESGameRoom()
{
FOculusXRHMDModule::LaunchEnvironment("GameRoom");
}
void FOculusXREditorModule::LaunchSESLivingRoom()
{
FOculusXRHMDModule::LaunchEnvironment("LivingRoom");
}
void FOculusXREditorModule::LaunchSESBedroom()
{
FOculusXRHMDModule::LaunchEnvironment("Bedroom");
}
void FOculusXREditorModule::StopSESServer()
{
FOculusXRHMDModule::StopServer();
}
void FOculusXREditorModule::AddMenuExtension(FMenuBuilder& Builder)
{
bool v = false;
GConfig->GetBool(TEXT("/Script/OculusXREditor.OculusXREditorSettings"), TEXT("bAddMenuOption"), v, GEditorIni);
if (v)
{
Builder.AddMenuEntry(FOculusToolCommands::Get().OpenProjectSetupTool);
}
}
void FOculusXREditorModule::AddToolbarExtension(FToolBarBuilder& Builder)
{
Builder.SetLabelVisibility(EVisibility::All);
Builder.AddComboButton(
FUIAction(),
FOnGetContent::CreateRaw(this, &FOculusXREditorModule::CreateToolbarEntryMenu, PluginCommands),
LOCTEXT("OculusToolsToolBarCombo", "Meta XR Tools"),
LOCTEXT("OculusToolsToolBarComboTooltip", "Meta XR tools"),
TAttribute<FSlateIcon>::CreateLambda([]() {
return FSlateIcon(FOculusToolStyle::GetStyleSetName(), "OculusTool.MenuButton");
}),
false);
Builder.AddComboButton(
FUIAction(),
FOnGetContent::CreateRaw(this, &FOculusXREditorModule::CreateXrSimToolbarEntryMenu, PluginCommands),
LOCTEXT("MetaXRSimulatorCombo", "Meta XR Simulator"),
LOCTEXT("MetaXRSimulatorComboTooltip", "Meta XR Simulator"),
TAttribute<FSlateIcon>::CreateLambda([]() {
return FSlateIcon(FOculusToolStyle::GetStyleSetName(), "OculusTool.MenuButton");
}),
false);
}
// Add the entries to the OculusXR Tools toolbar menu button
TSharedRef<SWidget> FOculusXREditorModule::CreateToolbarEntryMenu(TSharedPtr<class FUICommandList> Commands)
{
FMenuBuilder MenuBuilder(true, Commands);
MenuBuilder.BeginSection("OculusXRBuilds", LOCTEXT("OculusXRBuilds", "Builds"));
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().ToggleDeploySo);
MenuBuilder.EndSection();
MenuBuilder.BeginSection("OculusXRTools", LOCTEXT("OculusXRTools", "Tools"));
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().OpenProjectSetupTool);
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().OpenPlatWindow);
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
TSharedRef<SWidget> FOculusXREditorModule::CreateXrSimToolbarEntryMenu(TSharedPtr<class FUICommandList> Commands)
{
FMenuBuilder MenuBuilder(true, Commands);
MenuBuilder.BeginSection("MetaXRSimulator", LOCTEXT("MetaXRSimulator", "Toggle"));
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().ToggleMetaXRSim);
MenuBuilder.EndSection();
MenuBuilder.BeginSection("SES", LOCTEXT("SES", "SES"));
MenuBuilder.AddSubMenu(
LOCTEXT("Synthetic Environment Server", "Synthetic Environment Server"),
LOCTEXT("Synthetic Environment Server", "Synthetic Environment Server"),
FNewMenuDelegate::CreateRaw(this, &FOculusXREditorModule::CreateSESSubMenus));
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
void FOculusXREditorModule::CreateSESSubMenus(FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection("Synthetic Environment Server", LOCTEXT("Synthetic Environment Server", "Synthetic Environment Server"));
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().LaunchGameRoom);
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().LaunchLivingRoom);
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().LaunchBedroom);
MenuBuilder.AddMenuEntry(FOculusToolCommands::Get().StopServer);
MenuBuilder.EndSection();
}
FOculusXRHMDSettingsDetailsCustomization::FOculusXRHMDSettingsDetailsCustomization()
: EngineAndroidPath(FPaths::EngineDir() + TEXT("Build/Android/Java"))
, GameAndroidPath(FPaths::ProjectDir() + TEXT("Build/Android"))
, LaunchImageLandscape(FPlatformIconInfo(TEXT("res/drawable/splashscreen_landscape.png"), LOCTEXT("SystemSplashImage", "System Splash Image"), FText::GetEmpty(), 640, 360, FPlatformIconInfo::Required))
, VRSplashPath(FPaths::ProjectDir() + TEXT("Build/Android/assets/vr_splash.png"))
{
}
TSharedRef<IDetailCustomization> FOculusXRHMDSettingsDetailsCustomization::MakeInstance()
{
return MakeShareable(new FOculusXRHMDSettingsDetailsCustomization);
}
FReply FOculusXRHMDSettingsDetailsCustomization::PluginClickPerfFn(bool text)
{
IOculusXRProjectSetupToolModule::Get().ShowProjectSetupTool("Settings");
return FReply::Handled();
}
FReply FOculusXRHMDSettingsDetailsCustomization::PluginClickPlatFn(bool text)
{
FGlobalTabmanager::Get()->TryInvokeTab(FOculusXREditorModule::OculusPlatToolTabName);
return FReply::Handled();
}
FReply FOculusXRHMDSettingsDetailsCustomization::DisableEngineSplash(bool text)
{
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
Settings->bAutoEnabled = false;
Settings->SplashDescs.Empty();
Settings->TryUpdateDefaultConfigFile();
return FReply::Handled();
}
FReply FOculusXRHMDSettingsDetailsCustomization::AddSplashImage(bool text)
{
const FString AutomaticImagePath = EngineAndroidPath / LaunchImageLandscape.IconPath;
FText FailReason;
if (!SourceControlHelpers::CopyFileUnderSourceControl(VRSplashPath, AutomaticImagePath, LOCTEXT("ImageDescription", "image"), FailReason))
{
FNotificationInfo Info(FailReason);
Info.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(Info);
return FReply::Unhandled();
}
return FReply::Handled();
}
void FOculusXRHMDSettingsDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
SavedLayoutBuilder = &DetailLayout;
// Labeled "General OculusXR" instead of "General" to enable searchability. The button "Launch Oculus Utilities Window" doesn't show up if you search for "Oculus"
IDetailCategoryBuilder& CategoryBuilder = DetailLayout.EditCategory("General Meta XR", FText::GetEmpty(), ECategoryPriority::Important);
/* clang-format off */
CategoryBuilder.AddCustomRow(LOCTEXT("General", "General"))
.WholeRowContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight().Padding(2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("LaunchTool", "Launch Meta XR Project Setup Tool"))
.OnClicked(this, &FOculusXRHMDSettingsDetailsCustomization::PluginClickPerfFn, true)
]
+ SHorizontalBox::Slot().FillWidth(8)
]
+ SVerticalBox::Slot().AutoHeight().Padding(2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("LaunchPlatTool", "Launch Meta XR Platform Window"))
.OnClicked(this, &FOculusXRHMDSettingsDetailsCustomization::PluginClickPlatFn, true)
]
+ SHorizontalBox::Slot().FillWidth(8)
]
];
IDetailCategoryBuilder& CTXPTCategoryBuilder = DetailLayout.EditCategory("System SplashScreen", FText::GetEmpty(), ECategoryPriority::Important);
static const FName WarningColorStyle("Colors.AccentYellow");
CTXPTCategoryBuilder.AddCustomRow(LOCTEXT("CTXPTWarning", "Contextual Passthrough Warning"))
.Visibility(TAttribute<EVisibility>(this, &FOculusXRHMDSettingsDetailsCustomization::GetContextualPassthroughWarningVisibility))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot().FillHeight(1.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.f).VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.AutoWrapText(true)
.Justification(ETextJustify::Center)
.Text(LOCTEXT("CTXPT_EngineSplashWarning", "Engine Splash Screen is enabled, this will result in an inconsistent experience."))
.ColorAndOpacity(FAppStyle::Get().GetSlateColor(WarningColorStyle))
]
+ SHorizontalBox::Slot().FillWidth(1.f).HAlign(EHorizontalAlignment::HAlign_Left)
[
SNew(SButton)
.VAlign(EVerticalAlignment::VAlign_Center)
.Text(LOCTEXT("DisableEngineSplashScreen", "Disable Engine Splash Screen"))
.OnClicked(this, &FOculusXRHMDSettingsDetailsCustomization::DisableEngineSplash, true)
]
]
];
// Duplicate "Show Launch Image" and "Launch Landscape" properties from Android Settings
CTXPTCategoryBuilder.AddCustomRow(LOCTEXT("ShowSystemSplashImageRow", "Show System Splash Image Row"))
.NameContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(FMargin(0, 1, 0, 1))
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(LOCTEXT("ShowSystemSplashImage", "Show System Splash Image"))
.Font(DetailLayout.GetDetailFont())
.ToolTipText(LOCTEXT("ShowSystemSplashImageToolTip", "Same as \"Show Launch Image\" setting in the \"Platform > Android > Launch Images\" section. If set, the image will be presented by the Operating System at launch time"))
]
]
.ValueContent()
.MaxDesiredWidth(400.0f)
.MinDesiredWidth(100.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
.IsChecked(TAttribute<ECheckBoxState>(this, &FOculusXRHMDSettingsDetailsCustomization::GetShowLaunchImageCheckBoxState))
.OnCheckStateChanged(this, &FOculusXRHMDSettingsDetailsCustomization::OnShowLaunchImageCheckStateChanged)
]
];
const FString AutomaticImagePath = EngineAndroidPath / LaunchImageLandscape.IconPath;
const FString TargetImagePath = GameAndroidPath / LaunchImageLandscape.IconPath;
const FVector2D LaunchImageMaxSize(150.0f, 150.0f);
CTXPTCategoryBuilder.AddCustomRow(LaunchImageLandscape.IconName)
.IsEnabled(TAttribute<bool>(this, &FOculusXRHMDSettingsDetailsCustomization::IsLaunchImageEnabled))
.NameContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(FMargin(0, 1, 0, 1))
.FillWidth(1.0f)
[
SNew(STextBlock)
.Text(LaunchImageLandscape.IconName)
.Font(DetailLayout.GetDetailFont())
.ToolTipText(LOCTEXT("SystemSplashImageToolTip", "Same as \"Launch Landscape\" setting in the \"Platform > Android > Launch Images\" section. This is the image that will be presented by the Operating System at launch time"))
]
]
.ValueContent()
.MaxDesiredWidth(400.0f)
.MinDesiredWidth(100.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.VAlign(VAlign_Center)
[
SNew(SExternalImageReference, AutomaticImagePath, TargetImagePath)
.FileDescription(LaunchImageLandscape.IconDescription)
.MaxDisplaySize(LaunchImageMaxSize)
.OnPostExternalImageCopy(FOnPostExternalImageCopy::CreateSP(this, &FOculusXRHMDSettingsDetailsCustomization::OnLaunchImageChanged))
]
];
CTXPTCategoryBuilder.AddCustomRow(LOCTEXT("SystemSplashImageWarning", "System Splash Image warning"))
.Visibility(TAttribute<EVisibility>(this, &FOculusXRHMDSettingsDetailsCustomization::GetSystemSplashImageWarningVisibility))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot().FillHeight(1.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.f).VAlign(EVerticalAlignment::VAlign_Center)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.AutoWrapText(true)
.Justification(ETextJustify::Center)
.Text(LOCTEXT("SystemSplashWarningText", "Splash Image is currently missing from project. Click button to add it."))
.ColorAndOpacity(FAppStyle::Get().GetSlateColor(WarningColorStyle))
]
+ SHorizontalBox::Slot().FillWidth(1.f).HAlign(EHorizontalAlignment::HAlign_Left)
[
SNew(SButton)
.VAlign(EVerticalAlignment::VAlign_Center)
.Text(LOCTEXT("DisableEngineSplashScreen", "Add Splash Image file to project"))
.OnClicked(this, &FOculusXRHMDSettingsDetailsCustomization::AddSplashImage, true)
]
]
];
/* clang-format on */
}
EVisibility FOculusXRHMDSettingsDetailsCustomization::GetContextualPassthroughWarningVisibility() const
{
UOculusXRHMDRuntimeSettings* OculusSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
return OculusSettings->SystemSplashBackground == ESystemSplashBackgroundType::Contextual && (OculusSettings->bAutoEnabled || !OculusSettings->SplashDescs.IsEmpty()) ? EVisibility::Visible : EVisibility::Collapsed;
}
ECheckBoxState FOculusXRHMDSettingsDetailsCustomization::GetShowLaunchImageCheckBoxState() const
{
UAndroidRuntimeSettings* AndroidSettings = GetMutableDefault<UAndroidRuntimeSettings>();
return AndroidSettings->bShowLaunchImage ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
bool FOculusXRHMDSettingsDetailsCustomization::IsLaunchImageEnabled() const
{
UAndroidRuntimeSettings* AndroidSettings = GetMutableDefault<UAndroidRuntimeSettings>();
return AndroidSettings->bShowLaunchImage;
}
void FOculusXRHMDSettingsDetailsCustomization::OnShowLaunchImageCheckStateChanged(const ECheckBoxState NewState)
{
UAndroidRuntimeSettings* AndroidSettings = GetMutableDefault<UAndroidRuntimeSettings>();
AndroidSettings->bShowLaunchImage = NewState == ECheckBoxState::Checked;
AndroidSettings->TryUpdateDefaultConfigFile();
}
bool FOculusXRHMDSettingsDetailsCustomization::OnLaunchImageChanged(const FString& InChosenImage)
{
// This will refresh the launch image located in android settings as well
SavedLayoutBuilder->ForceRefreshDetails();
FText FailReason;
if (!SourceControlHelpers::CopyFileUnderSourceControl(VRSplashPath, InChosenImage, LOCTEXT("ImageDescription", "image"), FailReason))
{
FNotificationInfo Info(FailReason);
Info.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(Info);
return false;
}
return true;
}
EVisibility FOculusXRHMDSettingsDetailsCustomization::GetSystemSplashImageWarningVisibility() const
{
IFileManager& FileManager = IFileManager::Get();
return !FileManager.FileExists(*VRSplashPath) ? EVisibility::Visible : EVisibility::Collapsed;
}
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_MODULE(FOculusXREditorModule, OculusXREditor);
//////////////////////////////////////////////////////////////////////////
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,99 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "IOculusXREditorModule.h"
#include "Modules/ModuleInterface.h"
#include "IDetailCustomization.h"
#include "PlatformIconInfo.h"
#include "Input/Reply.h"
#include "Layout/Visibility.h"
class FToolBarBuilder;
class FMenuBuilder;
#define OCULUS_EDITOR_MODULE_NAME "OculusXREditor"
enum class ECheckBoxState : uint8;
class FOculusXREditorModule : public IOculusXREditorModule
{
public:
FOculusXREditorModule()
: bModuleValid(false){};
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual void PostLoadCallback() override;
void RegisterSettings();
void UnregisterSettings();
void PluginOpenSetupToolWindow();
FReply PluginClickFn(bool text);
void PluginOpenPlatWindow();
void ToggleOpenXRRuntime();
void CreateSESSubMenus(FMenuBuilder& MenuBuilder);
void LaunchSESGameRoom();
void LaunchSESLivingRoom();
void LaunchSESBedroom();
void StopSESServer();
public:
static const FName OculusPlatToolTabName;
private:
void AddToolbarExtension(FToolBarBuilder& Builder);
TSharedRef<SWidget> CreateToolbarEntryMenu(TSharedPtr<class FUICommandList> Commands);
TSharedRef<SWidget> CreateXrSimToolbarEntryMenu(TSharedPtr<class FUICommandList> Commands);
void AddMenuExtension(FMenuBuilder& Builder);
TSharedRef<class SDockTab> OnSpawnPlatToolTab(const class FSpawnTabArgs& SpawnTabArgs);
private:
TSharedPtr<class FUICommandList> PluginCommands;
bool bModuleValid;
};
class IDetailLayoutBuilder;
class FOculusXRHMDSettingsDetailsCustomization : public IDetailCustomization
{
private:
FOculusXRHMDSettingsDetailsCustomization();
FPlatformIconInfo LaunchImageLandscape;
const FString EngineAndroidPath;
const FString GameAndroidPath;
const FString VRSplashPath;
IDetailLayoutBuilder* SavedLayoutBuilder;
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
// IDetailCustomization interface
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override;
// End of IDetailCustomization interface
EVisibility GetContextualPassthroughWarningVisibility() const;
ECheckBoxState GetShowLaunchImageCheckBoxState() const;
bool IsLaunchImageEnabled() const;
void OnShowLaunchImageCheckStateChanged(const ECheckBoxState NewState);
bool OnLaunchImageChanged(const FString& InChosenImage);
EVisibility GetSystemSplashImageWarningVisibility() const;
FReply PluginClickPerfFn(bool text);
FReply PluginClickPlatFn(bool text);
FReply DisableEngineSplash(bool text);
FReply AddSplashImage(bool text);
};

View File

@@ -0,0 +1,9 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXREditorSettings.h"
UOculusXREditorSettings::UOculusXREditorSettings()
: PerfToolTargetPlatform(EOculusXRPlatform::PC)
{
}

View File

@@ -0,0 +1,346 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRMovementAssetsFactories.h"
#include "AssetToolsModule.h"
#include "IAssetTools.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "OculusXRLiveLinkRetargetFaceAsset.h"
#include "OculusXRLiveLinkRetargetBodyAsset.h"
#define LOCTEXT_NAMESPACE "OculusXRMovementAssetsFactories"
UOculusXRMetahumanRetargetAssetFactory::UOculusXRMetahumanRetargetAssetFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UBlueprint::StaticClass();
bCreateNew = true;
bEditAfterNew = true;
}
bool UOculusXRMetahumanRetargetAssetFactory::ConfigureProperties()
{
return true;
}
uint32 UOculusXRMetahumanRetargetAssetFactory::GetMenuCategories() const
{
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
return AssetTools.RegisterAdvancedAssetCategory("LiveLink", LOCTEXT("AssetCategoryName", "Live Link"));
}
UObject* UOculusXRMetahumanRetargetAssetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext)
{
UBlueprint* RetargetBlueprint = FKismetEditorUtilities::CreateBlueprint(ParentClass, InParent, Name, BlueprintType, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), CallingContext);
if (const TSubclassOf<UObject> GeneratedClass = RetargetBlueprint->GeneratedClass)
{
SetDefaults(GeneratedClass);
}
return RetargetBlueprint;
}
UOculusXRMetahumanFaceRetargetAssetFactory::UOculusXRMetahumanFaceRetargetAssetFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ParentClass = UOculusXRLiveLinkRetargetFaceAsset::StaticClass();
}
FText UOculusXRMetahumanFaceRetargetAssetFactory::GetDisplayName() const
{
return LOCTEXT("DisplayNameMetahumanFace", "Face retarget asset for OculusXRMovement and Metahuman");
}
void UOculusXRMetahumanFaceRetargetAssetFactory::SetDefaults(const TSubclassOf<UObject> GeneratedClass) const
{
if (UOculusXRLiveLinkRetargetFaceAsset* Retargeting = GeneratedClass->GetDefaultObject<UOculusXRLiveLinkRetargetFaceAsset>())
{
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::BrowLowererL, { "CTRL_expressions_BrowDownL", "CTRL_expressions_BrowLateralL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::BrowLowererR, { "CTRL_expressions_BrowDownR", "CTRL_expressions_BrowLateralR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekPuffL, { "CTRL_expressions_mouthCheekBlowL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekPuffR, { "CTRL_expressions_mouthCheekBlowR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekRaiserL, { "CTRL_expressions_eyeCheekRaiseL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekRaiserR, { "CTRL_expressions_eyeCheekRaiseR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekSuckL, { "CTRL_expressions_mouthCheekSuckL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::CheekSuckR, { "CTRL_expressions_mouthCheekSuckR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::ChinRaiserB, { "CTRL_expressions_jawChinRaiseDL", "CTRL_expressions_jawChinRaiseDR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::ChinRaiserT, { "CTRL_expressions_jawChinRaiseUL", "CTRL_expressions_jawChinRaiseUR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::DimplerL, { "CTRL_expressions_mouthDimpleL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::DimplerR, { "CTRL_expressions_mouthDimpleR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesClosedL, { "CTRL_expressions_eyeBlinkL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesClosedR, { "CTRL_expressions_eyeBlinkR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookDownL, { "CTRL_expressions_eyeLookDownL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookDownR, { "CTRL_expressions_eyeLookDownR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookLeftL, { "CTRL_expressions_eyeLookLeftL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookLeftR, { "CTRL_expressions_eyeLookLeftR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookRightL, { "CTRL_expressions_eyeLookRightL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookRightR, { "CTRL_expressions_eyeLookRightR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookUpL, { "CTRL_expressions_eyeLookUpL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::EyesLookUpR, { "CTRL_expressions_eyeLookUpR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::InnerBrowRaiserL, { "CTRL_expressions_browRaiseInL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::InnerBrowRaiserR, { "CTRL_expressions_browRaiseInR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::JawDrop, { "CTRL_expressions_jawOpen" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::JawSidewaysLeft, { "CTRL_expressions_jawLeft" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::JawSidewaysRight, { "CTRL_expressions_jawRight" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::JawThrust, { "CTRL_expressions_jawFwd", "CTRL_expressions_jawBack" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LidTightenerL, { "CTRL_expressions_eyeSquintInnerL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LidTightenerR, { "CTRL_expressions_eyeSquintInnerR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipCornerDepressorL, { "CTRL_expressions_mouthCornerDepressL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipCornerDepressorR, { "CTRL_expressions_mouthCornerDepressR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipCornerPullerL, { "CTRL_expressions_mouthCornerPullL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipCornerPullerR, { "CTRL_expressions_mouthCornerPullR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipFunnelerLB, { "CTRL_expressions_mouthFunnelDL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipFunnelerLT, { "CTRL_expressions_mouthFunnelUL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipFunnelerRB, { "CTRL_expressions_mouthFunnelDR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipFunnelerRT, { "CTRL_expressions_mouthFunnelUR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipPressorL, { "CTRL_expressions_mouthLipsPressL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipPressorR, { "CTRL_expressions_mouthLipsPressR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipPuckerL, { "CTRL_expressions_mouthLipsPurseDL", "CTRL_expressions_mouthLipsPurseUL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipPuckerR, { "CTRL_expressions_mouthLipsPurseDR", "CTRL_expressions_mouthLipsPurseUR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipStretcherL, { "CTRL_expressions_mouthStretchL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipStretcherR, { "CTRL_expressions_mouthStretchR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipSuckLB, { "CTRL_expressions_mouthLowerLipBiteL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipSuckLT, { "CTRL_expressions_mouthUpperLipBiteL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipSuckRB, { "CTRL_expressions_mouthLowerLipBiteR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipSuckRT, { "CTRL_expressions_mouthUpperLipBiteR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipTightenerL, { "CTRL_expressions_mouthLipsTightenDL", "CTRL_expressions_mouthLipsTightenUL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipTightenerR, { "CTRL_expressions_mouthLipsTightenDR", "CTRL_expressions_mouthLipsTightenUR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LipsToward, { "CTRL_expressions_jawChinRaiseDL", "CTRL_expressions_jawChinRaiseDR", "CTRL_expressions_jawChinRaiseUL", "CTRL_expressions_jawChinRaiseUR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LowerLipDepressorL, { "CTRL_expressions_mouthLowerLipDepressL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::LowerLipDepressorR, { "CTRL_expressions_mouthLowerLipDepressR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::MouthLeft, { "CTRL_expressions_mouthLeft" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::MouthRight, { "CTRL_expressions_mouthRight" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::NoseWrinklerL, { "CTRL_expressions_noseWrinkleL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::NoseWrinklerR, { "CTRL_expressions_noseWrinkleR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::OuterBrowRaiserL, { "CTRL_expressions_browRaiseOuterL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::OuterBrowRaiserR, { "CTRL_expressions_browRaiseOuterR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::UpperLidRaiserL, { "CTRL_expressions_eyeUpperLidUpL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::UpperLidRaiserR, { "CTRL_expressions_eyeUpperLidUpR" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::UpperLipRaiserL, { "CTRL_expressions_mouthUpperLipRaiseL" });
Retargeting->CurveRemapping.Emplace(EOculusXRFaceExpression::UpperLipRaiserR, { "CTRL_expressions_mouthUpperLipRaiseR" });
}
}
UOculusXRMetahumanBodyRetargetAssetFactory::UOculusXRMetahumanBodyRetargetAssetFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ParentClass = UOculusXRLiveLinkRetargetBodyAsset::StaticClass();
}
FText UOculusXRMetahumanBodyRetargetAssetFactory::GetDisplayName() const
{
return LOCTEXT("DisplayNameMetahumanBody", "Body retarget asset for OculusXRMovement and Metahuman");
}
void UOculusXRMetahumanBodyRetargetAssetFactory::SetDefaults(const TSubclassOf<UObject> GeneratedClass) const
{
if (UOculusXRLiveLinkRetargetBodyAsset* Retargeting = GeneratedClass->GetDefaultObject<UOculusXRLiveLinkRetargetBodyAsset>())
{
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRoot, "root");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyHips, "pelvis");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodySpineLower, "spine_01");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodySpineMiddle, "spine_02");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodySpineUpper, "spine_04");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyChest, "spine_05");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyNeck, "neck_02");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyHead, "head");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftShoulder, "clavicle_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftScapula, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftArmUpper, "upperarm_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftArmLower, "lowerarm_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandWristTwist, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightShoulder, "clavicle_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightScapula, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightArmUpper, "upperarm_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightArmLower, "lowerarm_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandWristTwist, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandPalm, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandWrist, "hand_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandThumbMetacarpal, "thumb_01_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandThumbProximal, "thumb_02_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandThumbDistal, "thumb_03_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandThumbTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandIndexMetacarpal, "index_metacarpal_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandIndexProximal, "index_01_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandIndexIntermediate, "index_02_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandIndexDistal, "index_03_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandIndexTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandMiddleMetacarpal, "middle_metacarpal_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandMiddleProximal, "middle_01_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandMiddleIntermediate, "middle_02_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandMiddleDistal, "middle_03_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandMiddleTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandRingMetacarpal, "ring_metacarpal_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandRingProximal, "ring_01_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandRingIntermediate, "ring_02_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandRingDistal, "ring_03_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandRingTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandLittleMetacarpal, "pinky_metacarpal_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandLittleProximal, "pinky_01_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandLittleIntermediate, "pinky_02_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandLittleDistal, "pinky_03_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftHandLittleTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandPalm, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandWrist, "hand_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandThumbMetacarpal, "thumb_01_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandThumbProximal, "thumb_02_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandThumbDistal, "thumb_03_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandThumbTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandIndexMetacarpal, "index_metacarpal_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandIndexProximal, "index_01_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandIndexIntermediate, "index_02_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandIndexDistal, "index_03_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandIndexTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandMiddleMetacarpal, "middle_metacarpal_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandMiddleProximal, "middle_01_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandMiddleIntermediate, "middle_02_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandMiddleDistal, "middle_03_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandMiddleTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandRingMetacarpal, "ring_metacarpal_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandRingProximal, "ring_01_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandRingIntermediate, "ring_02_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandRingDistal, "ring_03_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandRingTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandLittleMetacarpal, "pinky_metacarpal_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandLittleProximal, "pinky_01_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandLittleIntermediate, "pinky_02_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandLittleDistal, "pinky_03_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightHandLittleTip, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftUpperLeg, "thigh_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftLowerLeg, "calf_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftFootAnkleTwist, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftFootAnkle, "foot_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftFootSubtalar, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftFootTransverse, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyLeftFootBall, "ball_l");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightUpperLeg, "thigh_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightLowerLeg, "calf_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightFootAnkleTwist, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightFootAnkle, "foot_r");
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightFootSubtalar, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightFootTransverse, NAME_None);
Retargeting->BoneRemapping.Emplace(EOculusXRBoneID::BodyRightFootBall, "ball_r");
{
FOculusXRBoneCorrection FromOculusToUnreal;
FromOculusToUnreal.RotationOffset.Roll = -90;
FromOculusToUnreal.RotationOffset.Yaw = -90;
Retargeting->GlobalCorrection = FromOculusToUnreal;
}
{
FOculusXRBoneCorrectionSet Root;
Root.Bones.Add(EOculusXRBoneID::BodyRoot);
Root.BoneCorrection.RotationOffset.Roll = 90;
Retargeting->LocalCorrections.Add(Root);
}
{
FOculusXRBoneCorrectionSet Hips;
Hips.Bones.Add(EOculusXRBoneID::BodyHips);
Hips.BoneCorrection.RotationOffset.Yaw = 5;
Retargeting->LocalCorrections.Add(Hips);
}
{
FOculusXRBoneCorrectionSet SpineLowerPart;
SpineLowerPart.Bones.Add(EOculusXRBoneID::BodySpineLower);
SpineLowerPart.Bones.Add(EOculusXRBoneID::BodySpineMiddle);
SpineLowerPart.BoneCorrection.RotationOffset.Yaw = 10;
Retargeting->LocalCorrections.Add(SpineLowerPart);
}
{
FOculusXRBoneCorrectionSet SpineUpperPart;
SpineUpperPart.Bones.Add(EOculusXRBoneID::BodySpineUpper);
SpineUpperPart.BoneCorrection.PositionOffset.Y = 3;
SpineUpperPart.BoneCorrection.RotationOffset.Yaw = -5;
Retargeting->LocalCorrections.Add(SpineUpperPart);
}
{
FOculusXRBoneCorrectionSet Chest;
Chest.Bones.Add(EOculusXRBoneID::BodyChest);
Chest.BoneCorrection.PositionOffset.Y = 3;
Chest.BoneCorrection.RotationOffset.Yaw = -5;
Retargeting->LocalCorrections.Add(Chest);
}
{
FOculusXRBoneCorrectionSet Head;
Head.Bones.Add(EOculusXRBoneID::BodyHead);
Head.Bones.Add(EOculusXRBoneID::BodyNeck);
Head.BoneCorrection.PositionOffset.Y = -3;
Head.BoneCorrection.RotationOffset.Yaw = 5;
Retargeting->LocalCorrections.Add(Head);
}
{
FOculusXRBoneCorrectionSet LeftShoulder;
LeftShoulder.Bones.Add(EOculusXRBoneID::BodyLeftShoulder);
LeftShoulder.BoneCorrection.RotationOffset.Pitch = -5;
LeftShoulder.BoneCorrection.RotationOffset.Yaw = 30;
LeftShoulder.BoneCorrection.PositionOffset.X = 5;
LeftShoulder.BoneCorrection.PositionOffset.Y = -6;
Retargeting->LocalCorrections.Add(LeftShoulder);
}
{
FOculusXRBoneCorrectionSet RightShoulder;
RightShoulder.Bones.Add(EOculusXRBoneID::BodyRightShoulder);
RightShoulder.BoneCorrection.RotationOffset.Pitch = -5;
RightShoulder.BoneCorrection.RotationOffset.Yaw = 30;
RightShoulder.BoneCorrection.PositionOffset.X = -6;
RightShoulder.BoneCorrection.PositionOffset.Y = 5;
Retargeting->LocalCorrections.Add(RightShoulder);
}
{
FOculusXRBoneCorrectionSet Hands;
for (uint8 BoneId = static_cast<uint8>(EOculusXRBoneID::BodyLeftHandPalm); BoneId <= static_cast<uint8>(EOculusXRBoneID::BodyRightHandLittleTip); ++BoneId)
{
Hands.Bones.Emplace(static_cast<EOculusXRBoneID>(BoneId));
}
Hands.BoneCorrection.RotationOffset.Roll = 180;
Retargeting->LocalCorrections.Add(Hands);
}
{
FOculusXRBoneCorrectionSet Legs;
Legs.Bones.Add(EOculusXRBoneID::BodyLeftUpperLeg);
Legs.Bones.Add(EOculusXRBoneID::BodyLeftLowerLeg);
Legs.Bones.Add(EOculusXRBoneID::BodyRightUpperLeg);
Legs.Bones.Add(EOculusXRBoneID::BodyRightLowerLeg);
Legs.BoneCorrection.RotationOffset.Yaw = 180;
Retargeting->LocalCorrections.Add(Legs);
}
{
FOculusXRBoneCorrectionSet FootAnkles;
FootAnkles.Bones.Add(EOculusXRBoneID::BodyLeftFootAnkle);
FootAnkles.Bones.Add(EOculusXRBoneID::BodyRightFootAnkle);
FootAnkles.BoneCorrection.RotationOffset.Roll = 180;
FootAnkles.BoneCorrection.RotationOffset.Pitch = 175;
FootAnkles.BoneCorrection.RotationOffset.Yaw = -80;
Retargeting->LocalCorrections.Add(FootAnkles);
}
{
FOculusXRBoneCorrectionSet FootBalls;
FootBalls.Bones.Add(EOculusXRBoneID::BodyLeftFootBall);
FootBalls.BoneCorrection.RotationOffset.Yaw = 200;
FootBalls.BoneCorrection.PositionOffset.Y = -5;
FootBalls.BoneCorrection.PositionOffset.Z = 1;
Retargeting->LocalCorrections.Add(FootBalls);
}
{
FOculusXRBoneCorrectionSet FootBalls;
FootBalls.Bones.Add(EOculusXRBoneID::BodyRightFootBall);
FootBalls.BoneCorrection.RotationOffset.Yaw = 200;
FootBalls.BoneCorrection.PositionOffset.Y = 5;
FootBalls.BoneCorrection.PositionOffset.Z = -1;
Retargeting->LocalCorrections.Add(FootBalls);
}
Retargeting->RetargetingMode = EOculusXRRetargetingMode::RotationsPlusHips;
Retargeting->ForwardMesh = EOculusXRAxis::Y;
}
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,49 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "Factories/BlueprintFactory.h"
#include "OculusXRMovementAssetsFactories.generated.h"
UCLASS(Abstract, hidecategories = Object, MinimalAPI)
class UOculusXRMetahumanRetargetAssetFactory : public UBlueprintFactory
{
GENERATED_BODY()
public:
UOculusXRMetahumanRetargetAssetFactory(const FObjectInitializer& ObjectInitializer);
virtual bool ConfigureProperties() override;
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override;
virtual uint32 GetMenuCategories() const override;
protected:
virtual void SetDefaults(const TSubclassOf<UObject> GeneratedClass) const PURE_VIRTUAL(UOculusXRMetahumanRetargetAssetFactory::SetDefaults, );
};
UCLASS(hidecategories = Object, MinimalAPI)
class UOculusXRMetahumanFaceRetargetAssetFactory : public UOculusXRMetahumanRetargetAssetFactory
{
GENERATED_BODY()
public:
UOculusXRMetahumanFaceRetargetAssetFactory(const FObjectInitializer& ObjectInitializer);
virtual FText GetDisplayName() const override;
protected:
virtual void SetDefaults(const TSubclassOf<UObject> GeneratedClass) const override;
};
UCLASS(hidecategories = Object, MinimalAPI)
class UOculusXRMetahumanBodyRetargetAssetFactory : public UOculusXRMetahumanRetargetAssetFactory
{
GENERATED_BODY()
public:
UOculusXRMetahumanBodyRetargetAssetFactory(const FObjectInitializer& ObjectInitializer);
virtual FText GetDisplayName() const override;
protected:
virtual void SetDefaults(const TSubclassOf<UObject> GeneratedClass) const override;
};

View File

@@ -0,0 +1,59 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRPassthroughColorLutAsset.h"
#include "AssetTypeCategories.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "OculusXRPassthroughColorLut.h"
#define LOCTEXT_NAMESPACE "AssetTypeActions"
FText FAssetTypeActions_OculusXRPassthroughColorLut::GetName() const
{
return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_PassthroughColorLUT", "PassthroughColorLUT");
}
FColor FAssetTypeActions_OculusXRPassthroughColorLut::GetTypeColor() const
{
return FColor(100, 100, 100);
}
const TArray<FText>& FAssetTypeActions_OculusXRPassthroughColorLut::GetSubMenus() const
{
static const TArray<FText> SubMenus{
LOCTEXT("AssetOculusXRPassthroughColorLutSubMenu", "OculusXR")
};
return SubMenus;
}
UClass* FAssetTypeActions_OculusXRPassthroughColorLut::GetSupportedClass() const
{
return UOculusXRPassthroughColorLut::StaticClass();
}
uint32 FAssetTypeActions_OculusXRPassthroughColorLut::GetCategories()
{
return EAssetTypeCategories::Misc;
}
UOculusXRPassthroughColorLutFactory::UOculusXRPassthroughColorLutFactory(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UOculusXRPassthroughColorLut::StaticClass();
bCreateNew = true;
bEditAfterNew = true;
}
UObject* UOculusXRPassthroughColorLutFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context,
FFeedbackContext* Warn)
{
return NewObject<UOculusXRPassthroughColorLut>(InParent, Name, Flags);
}
uint32 UOculusXRPassthroughColorLutFactory::GetMenuCategories() const
{
return EAssetTypeCategories::Misc;
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,33 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "AssetTypeActions_Base.h"
#include "OculusXRPassthroughColorLutAsset.generated.h"
class FAssetTypeActions_OculusXRPassthroughColorLut : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override;
virtual FColor GetTypeColor() const override;
virtual const TArray<FText>& GetSubMenus() const override;
virtual UClass* GetSupportedClass() const override;
virtual uint32 GetCategories() override;
};
UCLASS(hidecategories = Object, MinimalAPI)
class UOculusXRPassthroughColorLutFactory : public UFactory
{
GENERATED_BODY()
public:
UOculusXRPassthroughColorLutFactory(const FObjectInitializer& ObjectInitializer);
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context,
FFeedbackContext* Warn) override;
virtual uint32 GetMenuCategories() const override;
};

View File

@@ -0,0 +1,25 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRPlatformToolSettings.h"
UOculusXRPlatformToolSettings::UOculusXRPlatformToolSettings()
: OculusTargetPlatform(EOculusXRPlatformTarget::Rift)
{
uint8 NumPlatforms = (uint8)EOculusXRPlatformTarget::Length;
OculusApplicationID.Init("", NumPlatforms);
OculusApplicationToken.Init("", NumPlatforms);
OculusReleaseChannel.Init("Alpha", NumPlatforms);
OculusReleaseNote.Init("", NumPlatforms);
OculusLaunchFilePath.Init("", NumPlatforms);
OculusSymbolDirPath.Init("", NumPlatforms);
OculusLanguagePacksPath.Init("", NumPlatforms);
OculusExpansionFilesPath.Init("", NumPlatforms);
OculusAssetConfigs.Init(FOculusXRAssetConfigArray(), NumPlatforms);
UploadDebugSymbols = true;
for (int i = 0; i < NumPlatforms; i++)
{
OculusAssetConfigs[i].ConfigArray = TArray<FOculusXRAssetConfig>();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,222 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRPlatformToolSettings.h"
#include "Widgets/SWidget.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SMultiLineEditableTextBox.h"
#include "Widgets/Input/SComboBox.h"
#include "Widgets/Input/STextComboBox.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SCheckBox.h"
#include "Engine/PostProcessVolume.h"
#include "Framework/Text/SlateHyperlinkRun.h"
#include "HttpModule.h"
#include "HttpManager.h"
#include "Interfaces/IHttpResponse.h"
#include "Async/AsyncWork.h"
#include "HAL/Event.h"
#include "HAL/ThreadSafeBool.h"
#include "OculusXRPluginWrapper.h"
#include "Brushes/SlateDynamicImageBrush.h"
class SOculusPlatformToolWidget;
// Function Delegates
DECLARE_DELEGATE_OneParam(FEnableUploadButtonDel, bool);
DECLARE_DELEGATE_OneParam(FUpdateLogTextDel, FString);
DECLARE_DELEGATE_OneParam(FSetProcessDel, FProcHandle);
DECLARE_DELEGATE_RetVal_TwoParams(bool, FFieldValidatorDel, FString, FString&);
class SOculusPlatformToolWidget : public SCompoundWidget
{
public:
typedef void (SOculusPlatformToolWidget::*PTextComboBoxDel)(TSharedPtr<FString>, ESelectInfo::Type);
typedef void (SOculusPlatformToolWidget::*PTextComittedDel)(const FText&, ETextCommit::Type);
typedef FReply (SOculusPlatformToolWidget::*PButtonClickedDel)();
typedef bool (SOculusPlatformToolWidget::*PFieldValidatorDel)(FString, FString&);
typedef void (SOculusPlatformToolWidget::*PCheckBoxChangedDel)(ECheckBoxState);
SLATE_BEGIN_ARGS(SOculusPlatformToolWidget)
{
}
SLATE_END_ARGS();
SOculusPlatformToolWidget();
void Construct(const FArguments& InArgs);
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
static FString LogText;
private:
TSharedPtr<SMultiLineEditableTextBox> ToolConsoleLog;
TSharedPtr<SVerticalBox> GeneralSettingsBox;
TSharedPtr<SHorizontalBox> ButtonToolbar;
TSharedPtr<SVerticalBox> OptionalSettings;
TSharedPtr<SVerticalBox> ExpansionFilesSettings;
TSharedPtr<FSlateDynamicImageBrush> ODHIconDynamicImageBrush;
UEnum* PlatformEnum;
UEnum* GamepadEmulationEnum;
UEnum* AssetTypeEnum;
UOculusXRPlatformToolSettings* PlatformSettings;
TArray<TSharedPtr<FString>> OculusPlatforms;
TArray<TSharedPtr<FString>> RiftGamepadEmulation;
TArray<TSharedPtr<FString>> AssetType;
bool Options2DCollapsed;
bool OptionsRedistPackagesCollapsed;
bool ActiveUploadButton;
bool RequestUploadButtonActive;
FProcHandle PlatformProcess;
FThreadSafeBool LogTextUpdated;
FEnableUploadButtonDel EnableUploadButtonDel;
FUpdateLogTextDel UpdateLogTextDel;
FSetProcessDel SetProcessDel;
// Callbacks
FReply OnStartPlatformUpload();
FReply OnSelectRiftBuildDirectory();
FReply OnClearRiftBuildDirectory();
FReply OnSelectLaunchFilePath();
FReply OnClearLaunchFilePath();
FReply OnSelectSymbolDirPath();
FReply OnClearSymbolDirPath();
FReply OnSelect2DLaunchPath();
FReply OnClear2DLaunchPath();
FReply OnCancelUpload();
FReply OnSelectLanguagePacksPath();
FReply OnClearLanguagePacksPath();
FReply OnSelectExpansionFilesPath();
FReply OnClearExpansionFilesPath();
FString GenerateSymbolPath();
void OnPlatformSettingChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo);
void OnApplicationIDChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnApplicationTokenChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnReleaseChannelChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnReleaseNoteChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnRiftBuildVersionChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnRiftLaunchParamsChanged(const FText& InText, ETextCommit::Type InCommitType);
void OnRiftFirewallChanged(ECheckBoxState CheckState);
void OnRedistPackageStateChanged(ECheckBoxState CheckState, FOculusXRRedistPackage* Package);
void OnRiftGamepadEmulationChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo);
void On2DLaunchParamsChanged(const FText& InText, ETextCommit::Type InCommitType);
void On2DOptionsExpanded(bool bExpanded);
void OnRedistPackagesExpanded(bool bExpanded);
void OnAssetConfigRequiredChanged(ECheckBoxState CheckState, int i);
void OnAssetConfigTypeChanged(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo, int i);
void OnAssetConfigSKUChanged(const FText& InText, ETextCommit::Type InCommitType, int i);
void OnUploadDebugSymbolsChanged(ECheckBoxState CheckState);
void OnDebugSymbolsOnlyChanged(ECheckBoxState CheckState);
void OnBuildIDChanged(const FText& InText, ETextCommit::Type InCommitType);
// UI Constructors
void BuildGeneralSettingsBox(TSharedPtr<SVerticalBox> box);
void BuildTextComboBoxField(TSharedPtr<SVerticalBox> box, FText name, TArray<TSharedPtr<FString>>* options, TSharedPtr<FString> current, PTextComboBoxDel deleg, int32 indentAmount = 0);
void BuildTextField(TSharedPtr<SVerticalBox> box, FText name, FText text, FText tooltip, PTextComittedDel deleg, bool isPassword = false, int32 indentAmount = 0);
void BuildFileDirectoryField(TSharedPtr<SVerticalBox> box, FText name, FText path, FText tooltip, PButtonClickedDel deleg, PButtonClickedDel clearDeleg, int32 indentAmount = 0);
void BuildCheckBoxField(TSharedPtr<SVerticalBox> box, FText name, bool check, FText tooltip, PCheckBoxChangedDel deleg, int32 indentAmount = 0);
void BuildButtonToolbar(TSharedPtr<SHorizontalBox> box);
void BuildRiftOptionalFields(TSharedPtr<SVerticalBox> area);
void BuildRedistPackagesBox(TSharedPtr<SVerticalBox> box);
void BuildExpansionFileBox(TSharedPtr<SVerticalBox> box);
void BuildAssetConfigBox(TSharedPtr<SVerticalBox> box, FOculusXRAssetConfig config, int index);
// Text Field Validators
void ValidateTextField(PFieldValidatorDel del, FString text, FString name, bool& success);
bool GenericFieldValidator(FString text, FString& error);
bool IDFieldValidator(FString text, FString& error);
bool DirectoryFieldValidator(FString text, FString& error);
bool FileFieldValidator(FString text, FString& error);
bool LaunchParamValidator(FString text, FString& error);
bool ConstructArguments(FString& args);
bool ConstructDebugSymbolArguments(FString& args);
void EnableUploadButton(bool enabled);
void LoadConfigSettings();
void UpdateLogText(FString text);
void SetPlatformProcess(FProcHandle proc);
void LoadRedistPackages();
};
class FPlatformDownloadTask : public FNonAbandonableTask
{
friend class FAsyncTask<FPlatformDownloadTask>;
private:
FUpdateLogTextDel UpdateLogText;
FString ToolConsoleLog;
FEvent* downloadCompleteEvent;
FEvent* SaveCompleteEvent;
TArray<uint8> httpData;
public:
FPlatformDownloadTask(FUpdateLogTextDel textDel, FEvent* saveEvent);
void OnDownloadRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded);
void OnRequestDownloadProgress(FHttpRequestPtr HttpRequest, int32 BytesSend, int32 InBytesReceived);
protected:
void DoWork();
void UpdateProgressLog(int progress);
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FPlatformDownloadTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
class FPlatformUploadTask : public FNonAbandonableTask
{
friend class FAsyncTask<FPlatformUploadTask>;
public:
FPlatformUploadTask(FString args, FEnableUploadButtonDel del, FUpdateLogTextDel textDel, FSetProcessDel procDel);
private:
void* ReadPipe;
void* WritePipe;
FSetProcessDel SetProcess;
FUpdateLogTextDel UpdateLogText;
FEnableUploadButtonDel EnableUploadButton;
FString LaunchArgs;
protected:
void DoWork();
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FPlatformUploadTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
class FPlatformLoadRedistPackagesTask : public FNonAbandonableTask
{
friend class FAsyncTask<FPlatformLoadRedistPackagesTask>;
public:
FPlatformLoadRedistPackagesTask(FUpdateLogTextDel textDel);
private:
void* ReadPipe;
void* WritePipe;
FUpdateLogTextDel UpdateLogText;
protected:
void DoWork();
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FPlatformLoadRedistPackagesTask, STATGROUP_ThreadPoolAsyncTasks);
}
};

View File

@@ -0,0 +1,82 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRPrivacyNotification.h"
#include "GeneralProjectSettings.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "OculusXRHMDModule.h"
#include "OculusXRToolStyle.h"
#include "OculusXRTelemetryEvents.h"
#include "OculusXRTelemetryPrivacySettings.h"
#define LOCTEXT_NAMESPACE "OculusXRTelemetryPrivacySettings"
void OculusXRTelemetry::SpawnNotification()
{
const auto EditorPrivacySettings = GetDefault<UOculusXRTelemetryPrivacySettings>();
if ((!EditorPrivacySettings) || (EditorPrivacySettings->bHasNotified))
{
return;
}
FNotificationInfo Info(LOCTEXT("PrivacyTelemetrySettingsName", "MetaXR Usage Data"));
Info.Image = FOculusToolStyle::Get().GetBrush("OculusTool.MenuButton");
Info.ExpireDuration = 10.0f;
Info.bFireAndForget = false;
Info.Hyperlink = FSimpleDelegate::CreateLambda([EditorPrivacySettings]() {
const FString DocsURL = EditorPrivacySettings->GetAdditionalInfoUrl();
FPlatformProcess::LaunchURL(*DocsURL, nullptr, nullptr);
});
Info.HyperlinkText = EditorPrivacySettings->GetAdditionalInfoUrlLabel();
Info.SubText = EditorPrivacySettings->GetTrueStateDescription();
Info.bUseLargeFont = true;
TPromise<TSharedPtr<SNotificationItem>> BtnNotificationPromise;
const auto Clicked = [NotificationFuture = BtnNotificationPromise.GetFuture().Share()](bool bConsent) {
const TSharedPtr<SNotificationItem> Notification = NotificationFuture.Get();
Notification->SetCompletionState(bConsent ? SNotificationItem::CS_Success : SNotificationItem::CS_Fail);
Notification->Fadeout();
const auto EditorPrivacySettings = GetMutableDefault<UOculusXRTelemetryPrivacySettings>();
EditorPrivacySettings->Modify();
EditorPrivacySettings->bIsEnabled = bConsent;
EditorPrivacySettings->bHasNotified = true;
EditorPrivacySettings->SaveConfig();
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
Events::FEditorConsent().End(bConsent ? EAction::Success : EAction::Fail);
OculusXRTelemetry::PropagateTelemetryConsent();
}
};
Info.ButtonDetails.Add(
FNotificationButtonInfo(
EditorPrivacySettings->GetFalseStateLabel(),
EditorPrivacySettings->GetFalseStateTooltip(),
FSimpleDelegate::CreateLambda(Clicked, false),
SNotificationItem::CS_Pending));
Info.ButtonDetails.Add(
FNotificationButtonInfo(
EditorPrivacySettings->GetTrueStateLabel(),
EditorPrivacySettings->GetTrueStateTooltip(),
FSimpleDelegate::CreateLambda(Clicked, true),
SNotificationItem::CS_Pending));
const TSharedPtr<SNotificationItem> PrivacyNotification = FSlateNotificationManager::Get().AddNotification(Info);
if (PrivacyNotification.IsValid())
{
PrivacyNotification->SetCompletionState(SNotificationItem::CS_Pending);
BtnNotificationPromise.SetValue(PrivacyNotification);
}
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
const UGeneralProjectSettings& ProjectSettings = *GetDefault<UGeneralProjectSettings>();
const FString ProjectIdString = ProjectSettings.ProjectID.ToString();
NotEnd = Events::FEditorConsent().Start() //
.AddAnnotation(Events::ConsentOriginKey, "Notification") //
.AddAnnotation("project_hash", StringCast<ANSICHAR>(*ProjectIdString).Get());
}
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,7 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
namespace OculusXRTelemetry
{
void SpawnNotification();
}

View File

@@ -0,0 +1,202 @@

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSettingsToggle.h"
#include "Containers/Array.h"
#include "Delegates/Delegate.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "Engine/ImportantToggleSettingInterface.h"
#include "Fonts/SlateFontInfo.h"
#include "HAL/PlatformProcess.h"
#include "IDetailPropertyRow.h"
#include "Layout/Children.h"
#include "Layout/Margin.h"
#include "Misc/Attribute.h"
#include "PropertyHandle.h"
#include "SlotBase.h"
#include "Styling/AppStyle.h"
#include "Styling/SlateColor.h"
#include "Styling/SlateTypes.h"
#include "Templates/Casts.h"
#include "Types/SlateEnums.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Text/STextBlock.h"
#define LOCTEXT_NAMESPACE "OculusXRSettingsToggle"
class SImportantToggleButton : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SImportantToggleButton)
: _Text()
{
}
SLATE_STYLE_ARGUMENT(FCheckBoxStyle, CheckBoxStyle)
SLATE_ARGUMENT(FText, Text)
SLATE_ARGUMENT(FText, ToolTipText)
SLATE_ATTRIBUTE(bool, IsSet)
SLATE_EVENT(FSimpleDelegate, OnToggled)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs)
{
OnToggled = InArgs._OnToggled;
IsSetAttribute = InArgs._IsSet;
FSlateFontInfo LargeDetailsFont = IDetailLayoutBuilder::GetDetailFontBold();
LargeDetailsFont.Size += 4;
ChildSlot
[SNew(SCheckBox)
.Style(InArgs._CheckBoxStyle)
.IsChecked(this, &SImportantToggleButton::GetCheckedState)
.OnCheckStateChanged(this, &SImportantToggleButton::OnClick)
.ToolTipText(InArgs._ToolTipText)
.Padding(FMargin(16.0f, 12.0f))
.ForegroundColor(FSlateColor::UseForeground())
.IsFocusable(true)
[SNew(STextBlock)
.Text(InArgs._Text)
.Font(LargeDetailsFont)]];
}
private:
void OnClick(ECheckBoxState State)
{
OnToggled.ExecuteIfBound();
}
ECheckBoxState GetCheckedState() const
{
return IsSetAttribute.Get() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
private:
TAttribute<bool> IsSetAttribute;
FSimpleDelegate OnToggled;
};
TSharedRef<IDetailCustomization> FOculusXRSettingsToggle::MakeInstance()
{
return MakeShareable(new FOculusXRSettingsToggle);
}
void FOculusXRSettingsToggle::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray<TWeakObjectPtr<UObject>> Objects;
DetailBuilder.GetObjectsBeingCustomized(Objects);
if (Objects.Num() == 1)
{
ToggleSettingObject = Objects[0];
IImportantToggleSettingInterface* ToggleSettingInterface = Cast<IImportantToggleSettingInterface>(ToggleSettingObject.Get());
if (ToggleSettingInterface != nullptr)
{
FName CategoryName;
FName PropertyName;
ToggleSettingInterface->GetToggleCategoryAndPropertyNames(CategoryName, PropertyName);
IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(CategoryName);
TogglePropertyHandle = DetailBuilder.GetProperty(PropertyName);
FSlateFontInfo StateDescriptionFont = IDetailLayoutBuilder::GetDetailFont();
StateDescriptionFont.Size += 4;
// Customize collision section
Category.InitiallyCollapsed(false)
.AddProperty(TogglePropertyHandle)
.ShouldAutoExpand(true)
.CustomWidget()
[SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 12.0f, 0.0f, 0.0f)
[SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
[SNew(SImportantToggleButton)
.CheckBoxStyle(FAppStyle::Get(), "Property.ToggleButton.Start")
.Text(ToggleSettingInterface->GetFalseStateLabel())
.ToolTipText(ToggleSettingInterface->GetFalseStateTooltip())
.IsSet(this, &FOculusXRSettingsToggle::IsToggleValue, false)
.OnToggled(this, &FOculusXRSettingsToggle::OnToggledTo, false)]
+ SHorizontalBox::Slot()
.AutoWidth()
[SNew(SImportantToggleButton)
.CheckBoxStyle(FAppStyle::Get(), "Property.ToggleButton.End")
.Text(ToggleSettingInterface->GetTrueStateLabel())
.ToolTipText(ToggleSettingInterface->GetTrueStateTooltip())
.IsSet(this, &FOculusXRSettingsToggle::IsToggleValue, true)
.OnToggled(this, &FOculusXRSettingsToggle::OnToggledTo, true)]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.Padding(0.0f, 12.0f)
[SNew(SVerticalBox)
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
[SNew(SHyperlink)
.Text(ToggleSettingInterface->GetAdditionalInfoUrlLabel())
.OnNavigate(this, &FOculusXRSettingsToggle::OnNavigateHyperlink, ToggleSettingInterface->GetAdditionalInfoUrl())]]]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 12.0f)
[SNew(STextBlock)
.AutoWrapText(true)
.Text(this, &FOculusXRSettingsToggle::GetDescriptionText)
.Font(StateDescriptionFont)]];
}
}
}
bool FOculusXRSettingsToggle::IsToggleValue(bool bValue) const
{
bool bPropertyValue = false;
TogglePropertyHandle->GetValue(bPropertyValue);
return bPropertyValue == bValue;
}
void FOculusXRSettingsToggle::OnToggledTo(bool bSetTo)
{
TogglePropertyHandle->SetValue(bSetTo);
}
void FOculusXRSettingsToggle::OnNavigateHyperlink(FString Url)
{
FPlatformProcess::LaunchURL(*Url, nullptr, nullptr);
}
FText FOculusXRSettingsToggle::GetDescriptionText() const
{
IImportantToggleSettingInterface* ToogleSettingInterface = Cast<IImportantToggleSettingInterface>(ToggleSettingObject.Get());
if (ToogleSettingInterface != nullptr)
{
bool bPropertyValue = false;
TogglePropertyHandle->GetValue(bPropertyValue);
if (bPropertyValue)
{
return ToogleSettingInterface->GetTrueStateDescription();
}
else
{
return ToogleSettingInterface->GetFalseStateDescription();
}
}
return FText::GetEmpty();
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,27 @@

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
class IPropertyHandle;
class OCULUSXREDITOR_API FOculusXRSettingsToggle : public IDetailCustomization
{
public:
static TSharedRef<IDetailCustomization> MakeInstance();
// IDetailCustomization interface
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
// End of IDetailCustomization interface
private:
bool IsToggleValue(bool bValue) const;
void OnToggledTo(bool bSetTo);
void OnNavigateHyperlink(FString Url);
FText GetDescriptionText() const;
TSharedPtr<IPropertyHandle> TogglePropertyHandle;
TWeakObjectPtr<UObject> ToggleSettingObject;
};

View File

@@ -0,0 +1,10 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRTelemetry.h"
namespace OculusXRTelemetry::Events
{
using FEditorStart = TMarker<191956532>;
} // namespace OculusXRTelemetry::Events

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRToolCommands.h"
#include "../../OculusXRProjectSetupTool/Private/OculusXRProjectSetupToolModule.h"
#include "Framework/Docking/TabManager.h"
#define LOCTEXT_NAMESPACE "FOculusXREditorModule"
void FOculusToolCommands::RegisterCommands()
{
UI_COMMAND(OpenProjectSetupTool, "Meta XR Project Setup Tool", "Show Meta XR Project Setup Tool", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(ToggleDeploySo, "Deploy compiled .so directly to device", "Faster deploy when we only have code changes by deploying compiled .so directly to device", EUserInterfaceActionType::ToggleButton, FInputChord());
UI_COMMAND(OpenPlatWindow, "Meta XR Platform Window", "Show Meta XR Platform Window", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(ToggleMetaXRSim, "Meta XR Simulator", "Activate/Deactivate Meta XR Simulator", EUserInterfaceActionType::ToggleButton, FInputChord());
UI_COMMAND(LaunchGameRoom, "Launch GameRoom", "Launch GameRoom", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(LaunchLivingRoom, "Launch Living Room", "Launch Living Room", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(LaunchBedroom, "Launch Bedroom", "Launch Bedroom", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(StopServer, "Stop Server", "Stop Server", EUserInterfaceActionType::Button, FInputChord());
}
void FOculusToolCommands::ShowOculusTool()
{
IOculusXRProjectSetupToolModule::Get().ShowProjectSetupTool("Console");
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,44 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Framework/Commands/Commands.h"
#include "OculusXRToolStyle.h"
#include "OculusXREditorModule.h"
#include "HAL/IConsoleManager.h"
class FOculusToolCommands : public TCommands<FOculusToolCommands>
{
public:
FOculusToolCommands()
: TCommands<FOculusToolCommands>(
TEXT("OculusTool"), NSLOCTEXT("Contexts", "OculusXREditor", "OculusXREditor Plugin"), NAME_None,
FOculusToolStyle::GetStyleSetName())
, ShowOculusToolCommand(
TEXT("vr.oculus.ShowToolWindow"),
*NSLOCTEXT("OculusRift", "CCommandText_ShowToolWindow",
"Show the Oculus Editor Tool window (editor only).")
.ToString(),
FConsoleCommandDelegate::CreateRaw(this, &FOculusToolCommands::ShowOculusTool))
{
}
// TCommands<> interface
virtual void RegisterCommands() override;
TSharedPtr<FUICommandInfo> OpenProjectSetupTool;
TSharedPtr<FUICommandInfo> ToggleDeploySo;
TSharedPtr<FUICommandInfo> OpenPlatWindow;
TSharedPtr<FUICommandInfo> ToggleMetaXRSim;
TSharedPtr<FUICommandInfo> LaunchGameRoom;
TSharedPtr<FUICommandInfo> LaunchLivingRoom;
TSharedPtr<FUICommandInfo> LaunchBedroom;
TSharedPtr<FUICommandInfo> StopServer;
private:
void ShowOculusTool();
FAutoConsoleCommand ShowOculusToolCommand;
};

View File

@@ -0,0 +1,72 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRToolStyle.h"
#include "Styling/SlateStyleRegistry.h"
#include "Framework/Application/SlateApplication.h"
#include "Slate/SlateGameResources.h"
#include "Interfaces/IPluginManager.h"
TSharedPtr<FSlateStyleSet> FOculusToolStyle::StyleInstance = nullptr;
void FOculusToolStyle::Initialize()
{
if (!StyleInstance.IsValid())
{
StyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
}
}
void FOculusToolStyle::Shutdown()
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
ensure(StyleInstance.IsUnique());
StyleInstance.Reset();
}
FName FOculusToolStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("OculusToolStyle"));
return StyleSetName;
}
#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(Style->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__)
#define BOX_BRUSH(RelativePath, ...) FSlateBoxBrush(Style->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__)
#define BORDER_BRUSH(RelativePath, ...) FSlateBorderBrush(Style->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__)
#define TTF_FONT(RelativePath, ...) FSlateFontInfo(Style->RootToContentDir(RelativePath, TEXT(".ttf")), __VA_ARGS__)
#define OTF_FONT(RelativePath, ...) FSlateFontInfo(Style->RootToContentDir(RelativePath, TEXT(".otf")), __VA_ARGS__)
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon40x40(40.0f, 40.0f);
TSharedRef<FSlateStyleSet> FOculusToolStyle::Create()
{
TSharedRef<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("OculusToolStyle"));
Style->SetContentRoot(IPluginManager::Get().FindPlugin("OculusXR")->GetBaseDir() / TEXT("Resources"));
Style->Set("OculusTool.MenuButton", new IMAGE_BRUSH(TEXT("ButtonIcon_80x"), Icon40x40));
Style->Set("OculusTool.OpenPluginWindow", new IMAGE_BRUSH(TEXT("ButtonIcon_80x"), Icon40x40));
return Style;
}
#undef IMAGE_BRUSH
#undef BOX_BRUSH
#undef BORDER_BRUSH
#undef TTF_FONT
#undef OTF_FONT
void FOculusToolStyle::ReloadTextures()
{
if (FSlateApplication::IsInitialized())
{
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
}
const ISlateStyle& FOculusToolStyle::Get()
{
return *StyleInstance;
}

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Styling/SlateStyle.h"
/** */
class FOculusToolStyle
{
public:
static void Initialize();
static void Shutdown();
/** reloads textures used by slate renderer */
static void ReloadTextures();
/** @return The Slate style set for the Shooter game */
static const ISlateStyle& Get();
static FName GetStyleSetName();
private:
static TSharedRef<class FSlateStyleSet> Create();
private:
static TSharedPtr<class FSlateStyleSet> StyleInstance;
};

View File

@@ -0,0 +1,19 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"
class FToolBarBuilder;
class FMenuBuilder;
#define OCULUS_EDITOR_MODULE_NAME "OculusXREditor"
//////////////////////////////////////////////////////////////////////////
// IOculusXREditorModule
class IOculusXREditorModule : public IModuleInterface
{
};

View File

@@ -0,0 +1,37 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "OculusXREditorSettings.generated.h"
UENUM()
enum class EOculusXRPlatform : uint8
{
PC UMETA(DisplayName = "PC"),
Mobile UMETA(DisplayName = "Mobile"),
Length UMETA(DisplayName = "Invalid")
};
/**
*
*/
UCLASS(config = Editor)
class OCULUSXREDITOR_API UOculusXREditorSettings : public UObject
{
GENERATED_BODY()
public:
UOculusXREditorSettings();
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TMap<FName, bool> PerfToolIgnoreList;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
EOculusXRPlatform PerfToolTargetPlatform;
UPROPERTY(globalconfig, EditAnywhere, Category = MetaXR)
bool bAddMenuOption;
};

View File

@@ -0,0 +1,272 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "OculusXRPlatformToolSettings.generated.h"
UENUM()
enum class EOculusXRPlatformTarget : uint8
{
Rift UMETA(DisplayName = "Rift"),
Quest UMETA(DisplayName = "Quest"),
Length UMETA(DisplayName = "Invalid")
};
UENUM()
enum class EOculusXRGamepadEmulation : uint8
{
Off UMETA(DisplayName = "Off"),
Twinstick UMETA(DisplayName = "Twinstick"),
RightDPad UMETA(DisplayName = "Right D Pad"),
LeftDPad UMETA(DisplayName = "Left D Pad"),
Length UMETA(DisplayName = "Invalid")
};
UENUM()
enum class EOculusXRAssetType : uint8
{
Default UMETA(DisplayName = "Default"),
Store UMETA(DisplayName = "Store"),
Language_Pack UMETA(DisplayName = "Language Pack"),
Length UMETA(DisplayName = "Invlaid"),
};
USTRUCT()
struct FOculusXRRedistPackage
{
GENERATED_USTRUCT_BODY()
UPROPERTY(config, EditAnywhere, Category = MetaXR)
bool Included = false;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString Name;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString Id;
};
USTRUCT()
struct FOculusXRAssetConfig
{
GENERATED_USTRUCT_BODY()
UPROPERTY(config, EditAnywhere, Category = MetaXR)
EOculusXRAssetType AssetType = EOculusXRAssetType::Default;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
bool Required = false;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString Name;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString Sku;
};
USTRUCT()
struct FOculusXRAssetConfigArray
{
GENERATED_USTRUCT_BODY()
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FOculusXRAssetConfig> ConfigArray;
};
/**
*
*/
UCLASS(config = Editor)
class OCULUSXREDITOR_API UOculusXRPlatformToolSettings : public UObject
{
GENERATED_BODY()
public:
UOculusXRPlatformToolSettings();
uint8 GetTargetPlatform()
{
return (uint8)OculusTargetPlatform;
}
void SetTargetPlatform(uint8 i)
{
OculusTargetPlatform = (EOculusXRPlatformTarget)i;
}
FString GetApplicationID()
{
return (uint8)OculusTargetPlatform < OculusApplicationID.Num() ? OculusApplicationID[(uint8)OculusTargetPlatform] : "";
}
void SetApplicationID(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusApplicationID[(uint8)OculusTargetPlatform] = s;
}
}
FString GetApplicationToken()
{
return (uint8)OculusTargetPlatform < OculusApplicationToken.Num() ? OculusApplicationToken[(uint8)OculusTargetPlatform] : "";
}
void SetApplicationToken(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusApplicationToken[(uint8)OculusTargetPlatform] = s;
}
}
FString GetReleaseChannel()
{
return (uint8)OculusTargetPlatform < OculusReleaseChannel.Num() ? OculusReleaseChannel[(uint8)OculusTargetPlatform] : "Alpha";
}
void SetReleaseChannel(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusReleaseChannel[(uint8)OculusTargetPlatform] = s;
}
}
FString GetReleaseNote()
{
return (uint8)OculusTargetPlatform < OculusReleaseNote.Num() ? OculusReleaseNote[(uint8)OculusTargetPlatform] : "";
}
void SetReleaseNote(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusReleaseNote[(uint8)OculusTargetPlatform] = s;
}
}
FString GetLaunchFilePath()
{
return (uint8)OculusTargetPlatform < OculusLaunchFilePath.Num() ? OculusLaunchFilePath[(uint8)OculusTargetPlatform] : "";
}
void SetLaunchFilePath(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusLaunchFilePath[(uint8)OculusTargetPlatform] = s;
}
}
EOculusXRGamepadEmulation GetRiftGamepadEmulation()
{
return OculusRiftGamepadEmulation;
}
void SetRiftGamepadEmulation(uint8 i)
{
OculusRiftGamepadEmulation = (EOculusXRGamepadEmulation)i;
}
FString GetLanguagePacksPath()
{
return (uint8)OculusTargetPlatform < OculusLanguagePacksPath.Num() ? OculusLanguagePacksPath[(uint8)OculusTargetPlatform] : "";
}
void SetLanguagePacksPath(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusLanguagePacksPath[(uint8)OculusTargetPlatform] = s;
}
}
FString GetExpansionFilesPath()
{
return (uint8)OculusTargetPlatform < OculusExpansionFilesPath.Num() ? OculusExpansionFilesPath[(uint8)OculusTargetPlatform] : "";
}
void SetExpansionFilesPath(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusExpansionFilesPath[(uint8)OculusTargetPlatform] = s;
}
}
FString GetSymbolDirPath()
{
return (uint8)OculusTargetPlatform < OculusSymbolDirPath.Num() ? OculusSymbolDirPath[(uint8)OculusTargetPlatform] : "";
}
void SetSymbolDirPath(FString s)
{
if (OculusTargetPlatform < EOculusXRPlatformTarget::Length)
{
OculusSymbolDirPath[(uint8)OculusTargetPlatform] = s;
}
}
TArray<FOculusXRAssetConfig>* GetAssetConfigs()
{
return (uint8)OculusTargetPlatform < OculusAssetConfigs.Num() ? &OculusAssetConfigs[(uint8)OculusTargetPlatform].ConfigArray : nullptr;
}
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString OculusRiftBuildDirectory;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString OculusRiftBuildVersion;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString OculusRiftLaunchParams;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
bool OculusRiftFireWallException;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString OculusRift2DLaunchPath;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString OculusRift2DLaunchParams;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FOculusXRRedistPackage> OculusRedistPackages;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
bool UploadDebugSymbols;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
bool DebugSymbolsOnly;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
FString BuildID;
private:
UPROPERTY(config, EditAnywhere, Category = MetaXR)
EOculusXRPlatformTarget OculusTargetPlatform;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusApplicationID;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusApplicationToken;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusReleaseChannel;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusReleaseNote;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusLaunchFilePath;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
EOculusXRGamepadEmulation OculusRiftGamepadEmulation;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusLanguagePacksPath;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusExpansionFilesPath;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FString> OculusSymbolDirPath;
UPROPERTY(config, EditAnywhere, Category = MetaXR)
TArray<FOculusXRAssetConfigArray> OculusAssetConfigs;
};

View File

@@ -0,0 +1,42 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
namespace UnrealBuildTool.Rules
{
public class OculusXREyeTracker : ModuleRules
{
public OculusXREyeTracker(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
if (Target.Platform == UnrealTargetPlatform.Win64 ||
Target.Platform == UnrealTargetPlatform.Android)
{
PrivateIncludePaths.AddRange(
new string[] {
"OculusXRHMD/Private",
});
PublicDependencyModuleNames.AddRange(
new string[]
{
"InputDevice",
"EyeTracker",
"OVRPluginXR",
"OculusXRHMD",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
}
);
}
}
}
}

View File

@@ -0,0 +1,214 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "IEyeTrackerModule.h"
#include "EyeTrackerTypes.h"
#include "IEyeTracker.h"
#include "Modules/ModuleManager.h"
#include "GameFramework/WorldSettings.h"
#include "Engine/World.h"
#include "IXRTrackingSystem.h"
#include "Engine/Engine.h"
#include "OculusXRHMDModule.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRTelemetryEyeTrackerEvents.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
class FOculusXREyeTracker : public IEyeTracker
{
public:
FOculusXREyeTracker()
{
GetUnitScaleFactorFromSettings(GWorld, WorldToMeters);
if (GEngine != nullptr)
{
OculusXRHMD = GEngine->XRSystem.Get();
}
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FMovementSDKEyeTrackerCreated>();
}
virtual ~FOculusXREyeTracker()
{
if (bIsTrackerStarted)
{
ensureMsgf(OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StopEyeTracking()), TEXT("Cannot stop eye tracker."));
}
}
private:
// IEyeTracker
virtual void SetEyeTrackedPlayer(APlayerController*) override
{
unimplemented();
}
virtual bool GetEyeTrackerGazeData(FEyeTrackerGazeData& OutGazeData) const override
{
return ReactOnEyeTrackerState([this, &OutGazeData](const ovrpEyeGazesState& OVREyeGazesState, const FTransform& TrackingToWorld) {
OutGazeData.FixationPoint = GetFixationPoint(OVREyeGazesState);
OutGazeData.ConfidenceValue = MergeConfidence(OVREyeGazesState);
OutGazeData.GazeDirection = TrackingToWorld.TransformVector(MergeOrientation(OVREyeGazesState).GetForwardVector());
OutGazeData.GazeOrigin = TrackingToWorld.TransformPosition(MergePosition(OVREyeGazesState) * WorldToMeters);
});
}
virtual bool GetEyeTrackerStereoGazeData(FEyeTrackerStereoGazeData& OutGazeData) const override
{
return ReactOnEyeTrackerState([this, &OutGazeData](const ovrpEyeGazesState& OVREyeGazesState, const FTransform& TrackingToWorld) {
OutGazeData.FixationPoint = GetFixationPoint(OVREyeGazesState);
OutGazeData.ConfidenceValue = MergeConfidence(OVREyeGazesState);
const auto& LeftEyePose = OVREyeGazesState.EyeGazes[ovrpEye_Left].Pose;
const auto& RightEyePose = OVREyeGazesState.EyeGazes[ovrpEye_Right].Pose;
OutGazeData.LeftEyeDirection = TrackingToWorld.TransformVector(OculusXRHMD::ToFQuat(LeftEyePose.Orientation).GetForwardVector());
OutGazeData.RightEyeDirection = TrackingToWorld.TransformVector(OculusXRHMD::ToFQuat(RightEyePose.Orientation).GetForwardVector());
OutGazeData.LeftEyeOrigin = TrackingToWorld.TransformPosition(OculusXRHMD::ToFVector(LeftEyePose.Position) * WorldToMeters);
OutGazeData.RightEyeOrigin = TrackingToWorld.TransformPosition(OculusXRHMD::ToFVector(RightEyePose.Position) * WorldToMeters);
});
}
virtual EEyeTrackerStatus GetEyeTrackerStatus() const override
{
ovrpBool IsSupported = ovrpBool_False;
ovrpBool IsEnabled = ovrpBool_False;
const ovrpResult TrackingSupportedResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeTrackingSupported(&IsSupported);
const ovrpResult TrackingEnabledResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeTrackingEnabled(&IsEnabled);
if (OVRP_SUCCESS(TrackingSupportedResult) && OVRP_SUCCESS(TrackingEnabledResult))
{
if ((IsSupported == ovrpBool_True) && (IsEnabled == ovrpBool_True))
{
return EEyeTrackerStatus::Tracking;
}
if (IsSupported == ovrpBool_True)
{
return EEyeTrackerStatus::NotTracking;
}
}
return EEyeTrackerStatus::NotConnected;
}
virtual bool IsStereoGazeDataAvailable() const override
{
return true;
}
private:
// FOculusXREyeTracker
template <typename ReactOnState>
bool ReactOnEyeTrackerState(ReactOnState&& React) const
{
if (!bIsTrackerStarted)
{
bIsTrackerStarted = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().StartEyeTracking());
}
if (bIsTrackerStarted)
{
ovrpEyeGazesState OVREyeGazesState;
const ovrpResult OVREyeGazesStateResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeGazesState(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, &OVREyeGazesState);
checkf(OVREyeGazesStateResult != ovrpFailure_NotYetImplemented, TEXT("Eye tracking is not implemented on this platform."));
if (OVRP_SUCCESS(OVREyeGazesStateResult) && IsStateValidForBothEyes(OVREyeGazesState))
{
FTransform TrackingToWorld = OculusXRHMD ? OculusXRHMD->GetTrackingToWorldTransform() : FTransform::Identity;
React(OVREyeGazesState, TrackingToWorld);
return true;
}
}
return false;
}
static float IsStateValidForBothEyes(const ovrpEyeGazesState& OVREyeGazesState)
{
return OVREyeGazesState.EyeGazes[ovrpEye_Left].IsValid && OVREyeGazesState.EyeGazes[ovrpEye_Right].IsValid;
}
static float MergeConfidence(const ovrpEyeGazesState& OVREyeGazesState)
{
const auto& LeftEyeConfidence = OVREyeGazesState.EyeGazes[ovrpEye_Left].Confidence;
const auto& RightEyeConfidence = OVREyeGazesState.EyeGazes[ovrpEye_Right].Confidence;
return FGenericPlatformMath::Min(LeftEyeConfidence, RightEyeConfidence);
}
/// Warn: The result of MergedOrientation is not normalized.
static FQuat MergeOrientation(const ovrpEyeGazesState& OVREyeGazesState)
{
const auto& LeftEyeOrientation = OculusXRHMD::ToFQuat(OVREyeGazesState.EyeGazes[ovrpEye_Left].Pose.Orientation);
const auto& RightEyeOrientation = OculusXRHMD::ToFQuat(OVREyeGazesState.EyeGazes[ovrpEye_Right].Pose.Orientation);
return FQuat::FastLerp(LeftEyeOrientation, RightEyeOrientation, 0.5f);
}
static FVector MergePosition(const ovrpEyeGazesState& OVREyeGazesState)
{
const auto& LeftEyePosition = OculusXRHMD::ToFVector(OVREyeGazesState.EyeGazes[ovrpEye_Left].Pose.Position);
const auto& RightEyePosition = OculusXRHMD::ToFVector(OVREyeGazesState.EyeGazes[ovrpEye_Right].Pose.Position);
return (LeftEyePosition + RightEyePosition) / 2.f;
}
static FVector GetFixationPoint(const ovrpEyeGazesState& OVREyeGazesState)
{
return FVector::ZeroVector; // Not supported
}
float WorldToMeters = 100.f;
IXRTrackingSystem* OculusXRHMD = nullptr;
mutable bool bIsTrackerStarted = false;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
class FOculusXREyeTrackerModule : public IEyeTrackerModule
{
public:
static inline FOculusXREyeTrackerModule& Get()
{
return FModuleManager::LoadModuleChecked<FOculusXREyeTrackerModule>("OculusXREyeTracker");
}
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("OculusXREyeTracker");
}
virtual FString GetModuleKeyName() const override
{
return TEXT("OculusXREyeTracker");
}
virtual bool IsEyeTrackerConnected() const override
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
ovrpBool IsSupported = ovrpBool_False;
const ovrpResult trackingSupportedResult = FOculusXRHMDModule::GetPluginWrapper().GetEyeTrackingSupported(&IsSupported);
if (OVRP_SUCCESS(trackingSupportedResult))
{
return (IsSupported == ovrpBool_True);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
virtual TSharedPtr<class IEyeTracker, ESPMode::ThreadSafe> CreateEyeTracker() override
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
return MakeShared<OculusXRHMD::FOculusXREyeTracker>();
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return TSharedPtr<class IEyeTracker, ESPMode::ThreadSafe>();
}
};
IMPLEMENT_MODULE(FOculusXREyeTrackerModule, OculusXREyeTracker)

View File

@@ -0,0 +1,10 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRTelemetry.h"
namespace OculusXRTelemetry::Events
{
using FMovementSDKEyeTrackerCreated = TMarker<191957973>;
}

View File

@@ -0,0 +1,549 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Oculus mobile plugin additions-->
<root xmlns:android="http://schemas.android.com/apk/res/android">
<!-- init section is always evaluated once per architecture -->
<init>
<log text="Oculus mobile init"/>
<setBool result="bSupported" value="false"/>
<isArch arch="armeabi-v7a">
<setBool result="bSupported" value="true"/>
</isArch>
<isArch arch="arm64-v8a">
<setBool result="bSupported" value="true"/>
</isArch>
<!-- remove Oculus Signature Files by default -->
<setBool result="bRemoveOSIG" value="true"/>
<!-- determine the XrApi libraries that need to be loaded -->
<setStringFromProperty result="XrApi" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="XrApi" default="OVRPluginOpenXR"/>
<setBoolIsEqual result="bOVRPluginOpenXR" arg1="$S(XrApi)" arg2="OVRPluginOpenXR"/>
<setBoolIsEqual result="bNativeOpenXR" arg1="$S(XrApi)" arg2="NativeOpenXR"/>
<setBoolFromProperty result="bFocusAware" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bFocusAware" default="true"/>
<!-- get package for Meta Quest from AndroidRuntimeSettings -->
<setBoolFromProperty result="bPackageForMetaQuest" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bPackageForMetaQuest" default="false"/>
<!-- Backcompat for deprecated oculus device target setting -->
<setBoolFromPropertyContains result="bPackageForQuest1" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="Quest"/>
<setBoolFromPropertyContains result="bPackageForQuest2" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="Quest2"/>
<setBoolFromPropertyContains result="bPackageForQuestPro" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="QuestPro"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuest1)"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuest2)"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuestPro)"/>
<!-- get supported oculus devices from OculusXRHMDRuntimeSettings -->
<setBoolFromPropertyContains result="bSupportMetaQuest" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest"/>
<setBoolFromPropertyContains result="bSupportMetaQuest2" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest2"/>
<setBoolFromPropertyContains result="bSupportMetaQuestPro" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="QuestPro"/>
<setBoolFromPropertyContains result="bSupportMetaQuest3" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest3"/>
<setBoolFromProperty result="bShowLaunchImage" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bShowLaunchImage" default="false"/>
<setBoolFromProperty result="bRequiresSystemKeyboard" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bRequiresSystemKeyboard" default="false"/>
<setStringFromProperty result="HandTrackingSupport" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingSupport" default="ControllersOnly"/>
<setStringFromProperty result="HandTrackingFrequency" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingFrequency" default="LOW"/>
<setStringFromProperty result="HandTrackingVersion" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingVersion" default="Default"/>
<setBoolIsEqual result="bHandTrackingVersionDefault" arg1="$S(HandTrackingVersion)" arg2="Default"/>
<setBoolIsEqual result="bHandTrackingVersionV1" arg1="$S(HandTrackingVersion)" arg2="V1"/>
<setBoolIsEqual result="bHandTrackingVersionV2" arg1="$S(HandTrackingVersion)" arg2="V2"/>
<setStringFromProperty result="ColorSpace" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="ColorSpace" default="Rec_709"/>
<setBoolIsEqual result="bColorSpaceUnmanaged" arg1="$S(ColorSpace)" arg2="Unmanaged"/>
<setBoolIsEqual result="bColorSpaceRec2020" arg1="$S(ColorSpace)" arg2="Rec_2020"/>
<setBoolIsEqual result="bColorSpaceRec709" arg1="$S(ColorSpace)" arg2="Rec_709"/>
<setBoolIsEqual result="bColorSpaceRiftCV1" arg1="$S(ColorSpace)" arg2="Rift_CV1"/>
<setBoolIsEqual result="bColorSpaceRiftS" arg1="$S(ColorSpace)" arg2="Rift_S"/>
<setBoolIsEqual result="bColorSpaceQuest" arg1="$S(ColorSpace)" arg2="Quest"/>
<setBoolIsEqual result="bColorSpaceP3" arg1="$S(ColorSpace)" arg2="P3"/>
<setBoolIsEqual result="bColorSpaceAdobeRGB" arg1="$S(ColorSpace)" arg2="Adobe_RGB"/>
<setBoolFromProperty result="bInsightPassthroughEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bInsightPassthroughEnabled" default="false"/>
<setBoolFromProperty result="bAnchorSupportEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bAnchorSupportEnabled" default="false"/>
<setBoolFromProperty result="bAnchorSharingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bAnchorSharingEnabled" default="false"/>
<setBoolFromProperty result="bSceneSupportEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSceneSupportEnabled" default="false"/>
<setBoolFromProperty result="bSupportEyeTrackedFoveatedRendering" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSupportEyeTrackedFoveatedRendering" default="false"/>
<setStringFromProperty result="ProcessorFavor" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="ProcessorFavor" default="FavorEqually"/>
<setBoolIsEqual result="bProcessorFavorEqually" arg1="$S(ProcessorFavor)" arg2="FavorEqually"/>
<setStringFromProperty result="SystemSplashBackground" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SystemSplashBackground" default="Black"/>
<setBoolIsEqual result="bSystemSplashImageContextualPassthrough" arg1="$S(SystemSplashBackground)" arg2="Contextual"/>
<setBoolIsEqual result="bSystemSplashImageBlack" arg1="$S(SystemSplashBackground)" arg2="Black"/>
<!-- check for experimental feature support from config -->
<setBoolFromProperty result="bSupportExperimentalFeatures" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSupportExperimentalFeatures" default="false"/>
<setBoolFromProperty result="bBodyTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bBodyTrackingEnabled" default="false"/>
<setBoolFromProperty result="bEyeTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bEyeTrackingEnabled" default="false"/>
<setBoolFromProperty result="bFaceTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bFaceTrackingEnabled" default="false"/>
<setBoolFromProperty result="bTileTurnOffEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bTileTurnOffEnabled" default="false"/>
<!-- get packaging for Oculus Mobile from ini and reset it if architecture not supported -->
<if condition="bPackageForMetaQuest">
<true>
<if condition="bSupported">
<true>
<if condition="Distribution">
<true>
<setBoolFromProperty result="bRemoveOSIG" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bRemoveOSIG" default="false"/>
<if condition="bRemoveOSIG">
<true>
<log text="Oculus mobile entitlement checks are enabled"/>
</true>
</if>
</true>
<false>
<!-- if not using entitlement checks need to keep the osig files -->
<setBool result="bRemoveOSIG" value="false"/>
</false>
</if>
</true>
<false>
<setBool result="bPackageForMetaQuest" value="false"/>
<log text="Oculus mobile not supported for this architecture, disabled."/>
</false>
</if>
</true>
</if>
<if condition="bRemoveOSIG">
<true>
<log text="Oculus Signature Files (osig) will be removed from APK"/>
</true>
</if>
<!-- package for Oculus and for distribution -->
<setBool result="bOculusDistribution" value="false"/>
<if condition="bPackageForMetaQuest">
<true>
<isDistribution>
<setBool result="bOculusDistribution" value="true"/>
<log text="Building with Oculus mobile for distribution"/>
</isDistribution>
</true>
</if>
<!-- entitlements check if package Oculus for distribution and removing OSIGs -->
<setBoolAnd result="bEntitlementCheck" arg1="$B(bRemoveOSIG)" arg2="$B(bOculusDistribution)"/>
</init>
<!-- optional updates applied to AndroidManifest.xml -->
<androidManifestUpdates>
<if condition="bOculusDistribution">
<true>
<!-- distribution builds can install internal or SD card -->
<addAttribute tag="manifest" name="android:installLocation" value="auto"/>
<!-- update the GameActivity activity -->
<loopElements tag="activity">
<setStringFromAttribute result="activityName" tag="$" name="android:name"/>
<setBoolIsEqual result="bGameActivity" arg1="$S(activityName)" arg2="com.epicgames.unreal.GameActivity"/>
<if condition="bGameActivity">
<true>
<!-- do not want application to show in recents -->
<addAttribute tag="$" name="android:excludeFromRecents" value="true"/>
<!-- distribution builds should not be launched from home screen so remove LAUNCHER -->
<loopElements tag="category">
<setStringFromAttribute result="categoryName" tag="$" name="android:name"/>
<setBoolIsEqual result="bLauncher" arg1="$S(categoryName)" arg2="android.intent.category.LAUNCHER"/>
<if condition="bLauncher">
<true>
<removeElement tag="$"/>
</true>
</if>
</loopElements>
<!-- add INFO intent category instead -->
<setElement result="intentInfo" value="category"/>
<addAttribute tag="$intentInfo" name="android:name" value="android.intent.category.INFO"/>
<addElement tag="intent-filter" name="intentInfo"/>
</true>
</if>
</loopElements>
</true>
</if>
<setBool result="bOculus6Dof" value="$B(bPackageForMetaQuest)"/>
<if condition="bPackageForMetaQuest">
<true>
<if condition="bOculusDistribution">
<false>
<!-- Add Extlib Flag -->
<setElement result="extlib" value="meta-data"/>
<addAttribute tag="$extlib" name="android:name" value="com.oculus.extlib"/>
<addAttribute tag="$extlib" name="android:value" value="true"/>
<addElement tag="application" name="extlib"/>
</false>
</if>
</true>
</if>
<!-- Add Quest Specific Flags -->
<if condition="bOculus6Dof">
<true>
<addFeature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true"/>
<!-- Add Hand Tracking Flag -->
<setBoolIsEqual result="bHandsOnly" arg1="$S(HandTrackingSupport)" arg2="HandsOnly"/>
<setBoolIsEqual result="bControllersAndHands" arg1="$S(HandTrackingSupport)" arg2="ControllersAndHands"/>
<setBoolOr result="bEnableHandTracking" arg1="$B(bHandsOnly)" arg2="$B(bControllersAndHands)"/>
<if condition="bEnableHandTracking">
<true>
<addPermission android:name="com.oculus.permission.HAND_TRACKING"/>
<addFeature android:name="oculus.software.handtracking" android:required="$B(bHandsOnly)"/>
</true>
</if>
<!-- Add Passthrough flag-->
<if condition="bInsightPassthroughEnabled">
<true>
<addFeature android:name="com.oculus.feature.PASSTHROUGH" android:required="true"/>
</true>
</if>
<!-- Add Eye Tracking flags-->
<setBool result="bEyeTrackingFeature" value="false"/>
<setBoolOr result="bEyeTrackingFeature" arg1="$B(bEyeTrackingFeature)" arg2="$B(bSupportEyeTrackedFoveatedRendering)"/>
<setBoolOr result="bEyeTrackingFeature" arg1="$B(bEyeTrackingFeature)" arg2="$B(bEyeTrackingEnabled)"/>
<!-- Check for other features that require eye tracking here -->
<if condition="bEyeTrackingFeature">
<true>
<log text="Adding eye tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.EYE_TRACKING"/>
<addFeature android:name="oculus.software.eye_tracking" android:required="false"/>
</true>
</if>
<!-- Add Body Tracking flags-->
<if condition="bBodyTrackingEnabled">
<true>
<log text="Adding body tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.BODY_TRACKING"/>
<addFeature android:name="com.oculus.software.body_tracking" android:required="true"/>
</true>
</if>
<!-- Add Face Tracking flags-->
<if condition="bFaceTrackingEnabled">
<true>
<log text="Adding face tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.FACE_TRACKING"/>
<addPermission android:name="android.permission.RECORD_AUDIO"/>
<addFeature android:name="oculus.software.face_tracking" android:required="true"/>
</true>
</if>
<!-- Add Experimental Features flag-->
<if condition="bSupportExperimentalFeatures">
<true>
<log text="Adding experimental feature tag to manifest"/>
<addFeature android:name="com.oculus.experimental.enabled" android:required="true"/>
</true>
</if>
<!-- Add Flags for Spatial Anchors-->
<if condition="bAnchorSupportEnabled">
<true>
<addPermission android:name="com.oculus.permission.USE_ANCHOR_API"/>
</true>
</if>
<!-- Add Flag for Shared Anchors -->
<if condition="bAnchorSharingEnabled">
<true>
<addPermission android:name="com.oculus.permission.IMPORT_EXPORT_IOT_MAP_DATA"/>
</true>
</if>
<!-- Add Flags for Scene-->
<if condition="bSceneSupportEnabled">
<true>
<addPermission android:name="com.oculus.permission.USE_SCENE"/>
<addPermission android:name="com.oculus.permission.SCENE_OBJECT_TRACKING"/>
<addPermission android:name="com.oculus.permission.PLANE_TRACKING"/>
</true>
</if>
</true>
</if>
<!-- Add Activity Specific Flags -->
<loopElements tag="activity">
<setStringFromAttribute result="activityName" tag="$" name="android:name"/>
<setBoolIsEqual result="bGameActivity" arg1="$S(activityName)" arg2="com.epicgames.unreal.GameActivity"/>
<if condition="bGameActivity">
<true>
<!-- Add VR Intent Filter, Permissions, and Features -->
<if condition="bPackageForMetaQuest">
<true>
<addFeature android:name="android.hardware.usb.host"/>
</true>
</if>
<!-- Quest Specific Activity Tags -->
<if condition="bOculus6Dof">
<true>
<!-- Add System Keyboard Flag -->
<if condition="bFocusAware">
<true>
<if condition="bRequiresSystemKeyboard">
<true>
<addFeature android:name="oculus.software.overlay_keyboard" android:required="false"/>
</true>
</if>
</true>
</if>
</true>
</if>
</true>
</if>
</loopElements>
<!-- Add Application Specific Flags -->
<loopElements tag="application">
<!-- Add SupportedDevices Tag -->
<setString result="devicesString" value=""/>
<if condition="bSupportMetaQuest">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="quest|"/>
</true>
</if>
<if condition="bSupportMetaQuest2">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="quest2|"/>
</true>
</if>
<if condition="bSupportMetaQuestPro">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="cambria|"/>
</true>
</if>
<if condition="bSupportMetaQuest3">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="eureka|"/>
</true>
</if>
<setIntLength result="devicesStringLength" source="$S(devicesString)"/>
<setBoolIsGreater result="bDevicesSupported" arg1="$I(devicesStringLength)" arg2="0"/>
<if condition="bDevicesSupported">
<true>
<loopElements tag="meta-data">
<setStringFromAttribute result="nameString" tag="$" name="android:name"/>
<setBoolIsEqual result="bIsSupportedDevices" arg1="$S(nameString)" arg2="com.oculus.supportedDevices"/>
<if condition="bIsSupportedDevices">
<true>
<setStringFromAttribute result="existingDevicesString" tag="$" name="android:value"/>
<log text="Found existing Meta Quest supported devices tag: $S(existingDevicesString)"/>
<setStringAdd result="devicesString" arg1="$S(existingDevicesString)" arg2="|$S(devicesString)"/>
<setIntLength result="devicesStringLength" source="$S(devicesString)"/>
<removeElement tag="$"/>
</true>
</if>
</loopElements>
<setElement result="supportedDevices" value="meta-data"/>
<addAttribute tag="$supportedDevices" name="android:name" value="com.oculus.supportedDevices"/>
<setIntSubtract result="devicesStringLength" arg1="$I(devicesStringLength)" arg2="1"/>
<setStringSubstring result="devicesString" source="$S(devicesString)" start="0" length="$I(devicesStringLength)"/>
<log text="Adding Meta Quest supported devices tag: $S(devicesString)"/>
<addAttribute tag="$supportedDevices" name="android:value" value="$S(devicesString)"/>
<addElement tag="application" name="supportedDevices"/>
</true>
</if>
<!-- Add Hand Tracking Frequency -->
<if condition="bEnableHandTracking">
<true>
<setElement result="handTrackingFrequency" value="meta-data"/>
<addAttribute tag="$handTrackingFrequency" name="android:name" value="com.oculus.handtracking.frequency"/>
<addAttribute tag="$handTrackingFrequency" name="android:value" value="$S(HandTrackingFrequency)"/>
<addElement tag="application" name="handTrackingFrequency"/>
<if condition="bHandTrackingVersionDefault">
<false>
<if condition="bHandTrackingVersionV1">
<true>
<setElement result="handTrackingVersion" value="meta-data"/>
<addAttribute tag="$handTrackingVersion" name="android:name" value="com.oculus.handtracking.version"/>
<addAttribute tag="$handTrackingVersion" name="android:value" value="V1.0"/>
<addElement tag="application" name="handTrackingVersion"/>
</true>
</if>
<if condition="bHandTrackingVersionV2">
<true>
<setElement result="handTrackingVersion" value="meta-data"/>
<addAttribute tag="$handTrackingVersion" name="android:name" value="com.oculus.handtracking.version"/>
<addAttribute tag="$handTrackingVersion" name="android:value" value="V2.0"/>
<addElement tag="application" name="handTrackingVersion"/>
</true>
</if>
</false>
</if>
</true>
</if>
<!-- Add System Splash Image Background -->
<setString result="splashBackground" value=""/>
<if condition="bSystemSplashImageContextualPassthrough">
<true>
<setString result="splashBackground" value="passthrough-contextual"/>
</true>
</if>
<if condition="bSystemSplashImageBlack">
<true>
<setString result="splashBackground" value="black"/>
</true>
</if>
<setIntLength result="splashBackgroundLength" source="$S(splashBackground)"/>
<setBoolIsGreater result="bHasSplashBackground" arg1="$I(splashBackgroundLength)" arg2="0"/>
<if condition="bHasSplashBackground">
<true>
<setElement result="contextualPassthrough" value="meta-data"/>
<addAttribute tag="$contextualPassthrough" name="android:name" value="com.oculus.ossplash.background"/>
<addAttribute tag="$contextualPassthrough" name="android:value" value="$S(splashBackground)"/>
<addElement tag="application" name="contextualPassthrough"/>
</true>
</if>
<!-- Add Oculus Splash Screen -->
<if condition="bShowLaunchImage">
<true>
<setElement result="showOculusSplash" value="meta-data"/>
<addAttribute tag="$showOculusSplash" name="android:name" value="com.oculus.ossplash"/>
<addAttribute tag="$showOculusSplash" name="android:value" value="true"/>
<addElement tag="application" name="showOculusSplash"/>
<!-- Add Oculus Splash Screen colorspace setting -->
<setElement result="oculusSplashColorspace" value="meta-data"/>
<addAttribute tag="$oculusSplashColorspace" name="android:name" value="com.oculus.ossplash.colorspace"/>
<if condition="bColorSpaceUnmanaged">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Unmanaged"/>
</true>
</if>
<if condition="bColorSpaceRec2020">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="Rec.2020"/>
</true>
</if>
<if condition="bColorSpaceRec709">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="Rec.709"/>
</true>
</if>
<if condition="bColorSpaceRiftCV1">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!RiftCV1"/>
</true>
</if>
<if condition="bColorSpaceRiftS">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!RiftS"/>
</true>
</if>
<if condition="bColorSpaceQuest">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Quest"/>
</true>
</if>
<if condition="bColorSpaceP3">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!P3"/>
</true>
</if>
<if condition="bColorSpaceAdobeRGB">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Adobe"/>
</true>
</if>
<addElement tag="application" name="oculusSplashColorspace"/>
</true>
</if>
<!-- Add tradeCpuForGpuAmount Amount -->
<if condition="bProcessorFavorEqually">
<false>
<setBoolIsEqual result="bFavorGPU" arg1="$S(ProcessorFavor)" arg2="FavorGPU"/>
<setBoolIsEqual result="bFavorCPU" arg1="$S(ProcessorFavor)" arg2="FavorCPU"/>
<setElement result="tradeCpuForGpuAmount" value="meta-data"/>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:name" value="com.oculus.trade_cpu_for_gpu_amount"/>
<if condition="bFavorGPU">
<true>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:value" value="1"/>
</true>
</if>
<if condition="bFavorCPU">
<true>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:value" value="-1"/>
</true>
</if>
<addElement tag="application" name="tradeCpuForGpuAmount"/>
</false>
</if>
<!-- Add Tile Turn Off -->
<setElement result="tileTurnOffEnabled" value="meta-data"/>
<addAttribute tag="$tileTurnOffEnabled" name="android:name" value="com.oculus.foveation.tile_turn_off"/>
<addAttribute tag="$tileTurnOffEnabled" name="android:value" value="$B(bTileTurnOffEnabled)"/>
<addElement tag="application" name="tileTurnOffEnabled"/>
</loopElements>
</androidManifestUpdates>
<!-- optional additions to proguard -->
<proguardAdditions>
<insert>
-keep class com.oculus.** {
*;
}
-keep class android.app.** {
*;
}
</insert>
</proguardAdditions>
<!-- optional files or directories to copy to Intermediate/Android/APK -->
<resourceCopies>
<isArch arch="arm64-v8a">
<if condition="bOVRPluginOpenXR">
<true>
<log text="Copying libopenxr_loader.so"/>
<copyFile src="$S(EngineDir)/Source/ThirdParty/Oculus/OculusOpenXRLoader/OculusOpenXRLoader/Lib/arm64-v8a/libopenxr_loader.so" dst="$S(BuildDir)/libs/arm64-v8a/libopenxr_loader.so"/>
<log text="Copying OpenXR libOVRPlugin.so"/>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/Lib/arm64-v8a/OpenXR/libOVRPlugin.so" dst="$S(BuildDir)/libs/arm64-v8a/libOVRPlugin.so"/>
</true>
</if>
<if condition="bNativeOpenXR">
<true>
<log text="Copying libopenxr_loader.so"/>
<copyFile src="$S(EngineDir)/Source/ThirdParty/Oculus/OculusOpenXRLoader/OculusOpenXRLoader/Lib/arm64-v8a/libopenxr_loader.so" dst="$S(BuildDir)/libs/arm64-v8a/libopenxr_loader.so"/>
</true>
</if>
</isArch>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/ExtLibs/SystemUtils.jar" dst="$S(BuildDir)/libs/SystemUtils.jar"/>
<if condition="bEntitlementCheck">
<true>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/ExtLibs/vrplatlib.jar" dst="$S(BuildDir)/libs/vrplatlib.jar"/>
</true>
</if>
<if condition="bRemoveOSIG">
<true>
<deleteFiles filespec="assets/oculussig_*"/>
</true>
</if>
</resourceCopies>
<!-- optional libraries to load in GameActivity.java before libUnreal.so -->
<soLoadLibrary>
<!-- need this if plugin enabled and supported architecture, even if not packaged for Oculus mobile -->
<if condition="bSupported">
<true>
<if condition="bOVRPluginOpenXR">
<true>
<loadLibrary name="openxr_loader" failmsg="openxr_loader library not loaded and may be required for Oculus VR."/>
<loadLibrary name="OVRPlugin" failmsg="OVRPlugin library not loaded and may be required for Oculus VR."/>
</true>
</if>
<if condition="bNativeOpenXR">
<true>
<loadLibrary name="openxr_loader" failmsg="openxr_loader library not loaded and may be required for Oculus VR."/>
</true>
</if>
</true>
</if>
</soLoadLibrary>
</root>

View File

@@ -0,0 +1,139 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OculusXRHMD : ModuleRules
{
public OculusXRHMD(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
PrivateIncludePaths.AddRange(
new string[] {
Path.Combine(EngineDir, "Source/Runtime/Renderer/Private"),
Path.Combine(EngineDir, "Source/Runtime/Renderer/Private"),
Path.Combine(EngineDir, "Source/Runtime/OpenGLDrv/Private"),
Path.Combine(EngineDir, "Source/Runtime/Engine/Classes/Components"),
Path.Combine(EngineDir, "Source/Runtime/Engine/Classes/Kismet"),
});
PublicIncludePathModuleNames.AddRange(
new string[] {
"Launch",
"ProceduralMeshComponent",
"AndroidPermission"
});
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"RHI",
"RHICore",
"RenderCore",
"Renderer",
"Slate",
"SlateCore",
"ImageWrapper",
"MediaAssets",
"Analytics",
"OpenGLDrv",
"VulkanRHI",
"OVRPluginXR",
"OculusOpenXRLoader",
"ProceduralMeshComponent",
"Projects",
});
PublicDependencyModuleNames.AddRange(
new string[]
{
"HeadMountedDisplay",
});
if (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 3))
{
PublicDependencyModuleNames.AddRange(
new string[]
{
"XRBase",
});
}
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.Add("UnrealEd");
}
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenGL");
if (Target.Platform == UnrealTargetPlatform.Win64)
{
// D3D
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"D3D11RHI",
"D3D12RHI",
});
PrivateIncludePaths.AddRange(
new string[]
{
"OculusXRMR/Public",
});
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX11");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX12");
AddEngineThirdPartyPrivateStaticDependencies(Target, "NVAPI");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX11Audio");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DirectSound");
AddEngineThirdPartyPrivateStaticDependencies(Target, "NVAftermath");
AddEngineThirdPartyPrivateStaticDependencies(Target, "IntelMetricsDiscovery");
AddEngineThirdPartyPrivateStaticDependencies(Target, "IntelExtensionsFramework");
}
// Vulkan
{
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
}
// OVRPlugin
if (Target.Platform == UnrealTargetPlatform.Win64)
{
RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/" + Target.Platform.ToString() + "/OpenXR/OVRPlugin.dll");
}
}
else if (Target.Platform == UnrealTargetPlatform.Android)
{
// We are not currently supporting Mixed Reality on Android, but we need to include IOculusXRMRModule.h for OCULUS_MR_SUPPORTED_PLATFORMS definition
PrivateIncludePaths.AddRange(
new string[]
{
"OculusXRMR/Public"
});
// Vulkan
{
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
}
// AndroidPlugin
{
string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "OculusMobile_APL.xml"));
}
}
}
}
}

View File

@@ -0,0 +1,65 @@
#include "Common.ush"
void MainVertexShader(
float4 InPosition : ATTRIBUTE0,
float2 InUV : ATTRIBUTE1,
out float2 OutUV : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
OutPosition = InPosition;
OutUV = InUV;
}
Texture2D<uint> TextureParameter;
#define Zoom 2
#define Pan float2(0.5, 0)
#define Aspect 1
#define Iterations int(128*PSVariables.IterationsMultiplier)
#define JuliaSeed float2(0.39, 0.2)
#define ColorScale float3(4, 5, 6)
float ComputeValue(float2 v, float2 offset)
{
float vxsquare = 0;
float vysquare = 0;
int iteration = 0;
int lastIteration = Iterations;
do
{
vxsquare = v.x * v.x;
vysquare = v.y * v.y;
v = float2(vxsquare - vysquare, v.x * v.y * 2) + offset;
iteration++;
if ((lastIteration == Iterations) && (vxsquare + vysquare) > 4.0)
{
lastIteration = iteration + 1;
}
} while (iteration < lastIteration);
return (float(iteration) - (log(log(sqrt(vxsquare + vysquare))) / log(2.0))) / float(Iterations);
}
float4 Mandelbrot_Func(float2 texCoord : TEXCOORD0) : COLOR0
{
float2 v = (texCoord - 0.5) * Zoom * float2(1, Aspect) - Pan;
float val = ComputeValue(v, v);
return float4(sin(val * ColorScale.x), sin(val * ColorScale.y), sin(val * ColorScale.z), 1);
}
void MainPixelShader(
in float2 uv : TEXCOORD0,
out float4 OutColor : SV_Target0
)
{
OutColor = Mandelbrot_Func(uv);
}

View File

@@ -0,0 +1,294 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRAssetManager.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMDModule.h"
#include "Engine/StaticMesh.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/SoftObjectPath.h"
#include "Engine/SkeletalMesh.h"
#include "Components/SkeletalMeshComponent.h"
#include "OculusXRAssetDirectory.h"
#include "UObject/GCObject.h"
/* FOculusAssetDirectory
*****************************************************************************/
enum EOculusAsset
{
LeftTouchRiftS,
RightTouchRiftS,
LeftTouchQuest2,
RightTouchQuest2,
LeftTouchQuestPro,
RightTouchQuestPro,
LeftTouchQuest3,
RightTouchQuest3,
OculusAssetTotal
};
FSoftObjectPath FOculusAssetDirectory::AssetListing[OculusAssetTotal] = {
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuestRiftSController.LeftTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuestRiftSController.RightTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuest2.LeftTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuest2.RightTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPro.LeftMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPro.RightMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPlus.LeftMetaQuestTouchPlus")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPlus.RightMetaQuestTouchPlus")),
};
#if WITH_EDITORONLY_DATA
class FOculusAssetRepo : public FGCObject, public TArray<UObject*>
{
public:
// made an on-demand singleton rather than a static global, to avoid issues with FGCObject initialization
static FOculusAssetRepo& Get()
{
static FOculusAssetRepo AssetRepository;
return AssetRepository;
}
UObject* LoadAndAdd(const FSoftObjectPath& AssetPath)
{
UObject* AssetObj = AssetPath.TryLoad();
if (AssetObj != nullptr)
{
AddUnique(AssetObj);
}
return AssetObj;
}
public:
//~ FGCObject interface
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
Collector.AddReferencedObjects(*this);
}
virtual FString GetReferencerName() const override
{
return TEXT("FOculusAssetRepo");
}
};
void FOculusAssetDirectory::LoadForCook()
{
FOculusAssetRepo& AssetRepro = FOculusAssetRepo::Get();
for (int32 AssetIndex = 0; AssetIndex < UE_ARRAY_COUNT(FOculusAssetDirectory::AssetListing); ++AssetIndex)
{
AssetRepro.LoadAndAdd(FOculusAssetDirectory::AssetListing[AssetIndex]);
}
}
void FOculusAssetDirectory::ReleaseAll()
{
FOculusAssetRepo::Get().Empty();
}
#endif // WITH_EDITORONLY_DATA
/* OculusAssetManager_Impl
*****************************************************************************/
namespace OculusAssetManager_Impl
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FRenderableDevice
{
ovrpNode OVRNode;
ovrpSystemHeadset MinDeviceRange;
ovrpSystemHeadset MaxDeviceRange;
FSoftObjectPath MeshAssetRef;
};
static FRenderableDevice RenderableDevices[] = {
#if PLATFORM_ANDROID
//Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
//Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
//Quest 3
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#else
//PC - Rift S
{ ovrpNode_HandLeft, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[LeftTouchRiftS] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[RightTouchRiftS] },
//PC - Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
//PC - Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
//Quest 3
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#endif
};
static uint32 RenderableDeviceCount = sizeof(RenderableDevices) / sizeof(RenderableDevices[0]);
#endif // #if OCULUS_HMD_SUPPORTED_PLATFORMS
static UObject* FindDeviceMesh(const int32 DeviceID);
}; // namespace OculusAssetManager_Impl
static UObject* OculusAssetManager_Impl::FindDeviceMesh(const int32 DeviceID)
{
UObject* DeviceMesh = nullptr;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const ovrpNode DeviceOVRNode = OculusXRHMD::ToOvrpNode(DeviceID);
bool bUseSystemHeadsetType = false;
ovrpSystemHeadset HeadsetType;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHeadsetType2(&HeadsetType)))
{
bUseSystemHeadsetType = true;
}
if (DeviceOVRNode != ovrpNode_None)
{
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
if (RenderableDevice.OVRNode == DeviceOVRNode)
{
// If we have information about the current headset, load the model based of the headset information, otherwise load defaults.
if (bUseSystemHeadsetType)
{
if (HeadsetType >= RenderableDevice.MinDeviceRange && HeadsetType <= RenderableDevice.MaxDeviceRange)
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
else
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
}
}
#endif
return DeviceMesh;
}
/* FOculusAssetManager
*****************************************************************************/
FOculusAssetManager::FOculusAssetManager()
{
IModularFeatures::Get().RegisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
ResourceHolder = NewObject<UOculusXRResourceHolder>();
ResourceHolder->AddToRoot();
}
FOculusAssetManager::~FOculusAssetManager()
{
if (ResourceHolder)
{
ResourceHolder->ConditionalBeginDestroy();
ResourceHolder = nullptr;
}
IModularFeatures::Get().UnregisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
}
bool FOculusAssetManager::EnumerateRenderableDevices(TArray<int32>& DeviceListOut)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
using namespace OculusAssetManager_Impl;
DeviceListOut.Empty(RenderableDeviceCount);
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
const int32 ExternalDeviceId = OculusXRHMD::ToExternalDeviceId(RenderableDevice.OVRNode);
DeviceListOut.Add(ExternalDeviceId);
}
return true;
#else
return false;
#endif
}
int32 FOculusAssetManager::GetDeviceId(EControllerHand ControllerHand)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpNode ControllerOVRNode = ovrpNode_None;
switch (ControllerHand)
{
case EControllerHand::AnyHand:
// @TODO: maybe check if the right is tracking, if not choose left (if tracking)?
case EControllerHand::Right:
ControllerOVRNode = ovrpNode_HandRight;
break;
case EControllerHand::Left:
ControllerOVRNode = ovrpNode_HandLeft;
break;
case EControllerHand::ExternalCamera:
ControllerOVRNode = ovrpNode_TrackerZero;
break;
// case EControllerHand::Special_1:
// ControllerOVRNode = ovrpNode_TrackerOne;
// break;
// case EControllerHand::Special_2:
// ControllerOVRNode = ovrpNode_TrackerTwo;
// break;
// case EControllerHand::Special_3:
// ControllerOVRNode = ovrpNode_TrackerThree;
// break;
// case EControllerHand::Special_4:
// ControllerOVRNode = ovrpNode_DeviceObjectZero;
// break;
default:
// ControllerOVRNode = ovrpNode_None => returns -1
break;
}
return OculusXRHMD::ToExternalDeviceId(ControllerOVRNode);
#else
return INDEX_NONE;
#endif
}
UPrimitiveComponent* FOculusAssetManager::CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool /*bForceSynchronous*/, const FXRComponentLoadComplete& OnLoadComplete)
{
UPrimitiveComponent* NewRenderComponent = nullptr;
if (UObject* DeviceMesh = OculusAssetManager_Impl::FindDeviceMesh(DeviceId))
{
if (UStaticMesh* AsStaticMesh = Cast<UStaticMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, UStaticMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
UStaticMeshComponent* MeshComponent = NewObject<UStaticMeshComponent>(Owner, ComponentName, Flags);
MeshComponent->SetStaticMesh(AsStaticMesh);
NewRenderComponent = MeshComponent;
}
else if (USkeletalMesh* AsSkeletalMesh = Cast<USkeletalMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, USkeletalMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
USkeletalMeshComponent* SkelMeshComponent = NewObject<USkeletalMeshComponent>(Owner, ComponentName, Flags);
SkelMeshComponent->SetSkeletalMesh(AsSkeletalMesh);
NewRenderComponent = SkelMeshComponent;
}
NewRenderComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
OnLoadComplete.ExecuteIfBound(NewRenderComponent);
return NewRenderComponent;
}

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IXRSystemAssets.h"
#include "OculusXRResourceHolder.h"
#include "UObject/SoftObjectPtr.h"
/**
*
*/
class FOculusAssetManager : public IXRSystemAssets
{
public:
FOculusAssetManager();
virtual ~FOculusAssetManager();
public:
UOculusXRResourceHolder* GetResourceHolder() { return ResourceHolder; }
//~ IXRSystemAssets interface
virtual bool EnumerateRenderableDevices(TArray<int32>& DeviceListOut) override;
virtual int32 GetDeviceId(EControllerHand ControllerHand) override;
virtual UPrimitiveComponent* CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool bForceSynchronous, const FXRComponentLoadComplete& OnLoadComplete) override;
protected:
UOculusXRResourceHolder* ResourceHolder;
};

View File

@@ -0,0 +1,8 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRDelegates.h"
FOculusEventDelegates::FOculusDisplayRefreshRateChangedEvent FOculusEventDelegates::OculusDisplayRefreshRateChanged;
FOculusEventDelegates::FOculusEyeTrackingStateChangedEvent FOculusEventDelegates::OculusEyeTrackingStateChanged;

View File

@@ -0,0 +1,19 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Delegates/Delegate.h"
class FOculusEventDelegates
{
public:
/** When the display refresh rate is changed */
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusDisplayRefreshRateChangedEvent, float /*fromRefreshRate*/, float /*toRefreshRate*/);
static FOculusDisplayRefreshRateChangedEvent OculusDisplayRefreshRateChanged;
/** When the eye tracking status changes */
DECLARE_MULTICAST_DELEGATE_OneParam(FOculusEyeTrackingStateChangedEvent, bool /*bIsEyeTrackingOn*/);
static FOculusEyeTrackingStateChangedEvent OculusEyeTrackingStateChanged;
};

View File

@@ -0,0 +1,21 @@
// @lint-ignore-every LICENSELINT
// Copyright 1998-2020 Epic Games, Inc. All Rights Reserved.
#include "OculusXREventComponent.h"
#include "OculusXRHMD.h"
#include "OculusXRDelegates.h"
void UOculusXREventComponent::OnRegister()
{
Super::OnRegister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.AddUObject(this, &UOculusXREventComponent::OculusDisplayRefreshRateChanged_Handler);
FOculusEventDelegates::OculusEyeTrackingStateChanged.AddUObject(this, &UOculusXREventComponent::OculusEyeTrackingStateChanged_Handler);
}
void UOculusXREventComponent::OnUnregister()
{
Super::OnUnregister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.RemoveAll(this);
FOculusEventDelegates::OculusEyeTrackingStateChanged.RemoveAll(this);
}

View File

@@ -0,0 +1,985 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMD.h"
#include "Logging/MessageLog.h"
#define LOCTEXT_NAMESPACE "OculusFunctionLibrary"
//-------------------------------------------------------------------------------------------------
// UOculusXRFunctionLibrary
//-------------------------------------------------------------------------------------------------
UOculusXRFunctionLibrary::UOculusXRFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
OculusXRHMD::FOculusXRHMD* UOculusXRFunctionLibrary::GetOculusXRHMD()
{
return OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
}
void UOculusXRFunctionLibrary::GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector PositionScale)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD && OculusXRHMD->IsHeadTrackingAllowed())
{
FQuat HeadOrientation = FQuat::Identity;
FVector HeadPosition = FVector::ZeroVector;
OculusXRHMD->GetCurrentPose(OculusXRHMD->HMDDeviceId, HeadOrientation, HeadPosition);
DeviceRotation = HeadOrientation.Rotator();
DevicePosition = HeadPosition;
NeckPosition = OculusXRHMD->GetNeckPosition(HeadOrientation, HeadPosition);
}
else
#endif // #if OCULUS_HMD_SUPPORTED_PLATFORMS
{
DeviceRotation = FRotator::ZeroRotator;
DevicePosition = FVector::ZeroVector;
NeckPosition = FVector::ZeroVector;
}
}
void UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if ((Options == EOrientPositionSelector::Orientation) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseRotation(Rotation);
}
if ((Options == EOrientPositionSelector::Position) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseOffsetInMeters(BaseOffsetInMeters);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRotation = OculusXRHMD->GetBaseRotation();
OutBaseOffsetInMeters = OculusXRHMD->GetBaseOffsetInMeters();
}
else
{
OutRotation = FRotator::ZeroRotator;
OutBaseOffsetInMeters = FVector::ZeroVector;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpPoseStatef state;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePoseState3(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, OculusXRHMD::ToOvrpNode(DeviceType), &state)))
{
AngularAcceleration = OculusXRHMD::ToFVector(state.AngularAcceleration);
LinearAcceleration = OculusXRHMD::ToFVector(state.Acceleration);
AngularVelocity = OculusXRHMD::ToFVector(state.AngularVelocity);
LinearVelocity = OculusXRHMD::ToFVector(state.Velocity);
TimeInSeconds = state.Time;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool UOculusXRFunctionLibrary::IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool Present;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePresent2(OculusXRHMD::ToOvrpNode(DeviceType), &Present)))
{
return Present != ovrpBool_False;
}
else
{
return false;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->GetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->SetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetCPUAndGPULevels(int CPULevel, int GPULevel)
{
// Deprecated. Please use Get/SetSuggestedCpuAndGpuPerformanceLevels instead.
}
bool UOculusXRFunctionLibrary::GetUserProfile(FOculusXRHmdUserProfile& Profile)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FOculusXRHMD::UserProfile Data;
if (OculusXRHMD->GetUserProfile(Data))
{
Profile.Name = "";
Profile.Gender = "Unknown";
Profile.PlayerHeight = 0.0f;
Profile.EyeHeight = Data.EyeHeight;
Profile.IPD = Data.IPD;
Profile.NeckToEyeDistance = FVector2D(Data.EyeDepth, 0.0f);
return true;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (Options == EOrientPositionSelector::Orientation || Options == EOrientPositionSelector::OrientationAndPosition)
{
OculusXRHMD->SetBaseRotation(BaseRot);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRot = OculusXRHMD->GetBaseRotation();
OutPosOffset = FVector::ZeroVector;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
if (bClearBeforeAdd)
{
Splash->ClearSplashes();
}
FOculusXRSplashDesc Desc;
Desc.LoadingTexture = Texture;
Desc.QuadSizeInMeters = SizeInMeters;
Desc.TransformInMeters = FTransform(Rotation, TranslationInMeters);
Desc.DeltaRotation = FQuat(DeltaRotation);
Splash->AddSplash(Desc);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::ClearLoadingSplashScreens()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
Splash->ClearSplashes();
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool UOculusXRFunctionLibrary::HasInputFocus()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus != ovrpBool_False;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
bool UOculusXRFunctionLibrary::HasSystemOverlayPresent()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus == ovrpBool_False;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization)
{
GPUUtilization = 0.0f;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &GPUUtilization)))
{
IsGPUAvailable = true;
GPUUtilization *= 100;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
float UOculusXRFunctionLibrary::GetGPUFrameTime()
{
float FrameTime = 0;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_App_GpuTime_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_App_GpuTime_Float, &FrameTime)))
{
return FrameTime * 1000;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return 0.0f;
}
EOculusXRFoveatedRenderingMethod UOculusXRFunctionLibrary::GetFoveatedRenderingMethod()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTracked(&enabled)))
{
return enabled == ovrpBool_True ? EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering : EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingMethod(Method);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingLevel(level, isDynamic);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
EOculusXRFoveatedRenderingLevel UOculusXRFunctionLibrary::GetFoveatedRenderingLevel()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpTiledMultiResLevel Lvl;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetTiledMultiResLevel(&Lvl)))
{
return (EOculusXRFoveatedRenderingLevel)Lvl;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return EOculusXRFoveatedRenderingLevel::Off;
}
bool UOculusXRFunctionLibrary::GetEyeTrackedFoveatedRenderingSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
// Always return false on other engine releases, since they don't have FDM offset support
#ifdef WITH_OCULUS_BRANCH
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool Supported;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTrackedSupported(&Supported)))
{
return Supported == ovrpBool_True;
}
}
#endif // WITH_OCULUS_BRANCH
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
FString UOculusXRFunctionLibrary::GetDeviceName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
const char* NameString;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemProductName2(&NameString)) && NameString)
{
return FString(NameString);
}
}
#endif
return FString();
}
EOculusXRDeviceType UOculusXRFunctionLibrary::GetDeviceType()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (OculusXRHMD->GetSettings())
{
switch (OculusXRHMD->GetSettings()->SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return EOculusXRDeviceType::OculusQuest_Deprecated;
case ovrpSystemHeadset_Oculus_Quest_2:
return EOculusXRDeviceType::OculusQuest2;
case ovrpSystemHeadset_Meta_Quest_Pro:
return EOculusXRDeviceType::MetaQuestPro;
case ovrpSystemHeadset_Meta_Quest_3:
return EOculusXRDeviceType::MetaQuest3;
case ovrpSystemHeadset_Rift_CV1:
return EOculusXRDeviceType::Rift;
case ovrpSystemHeadset_Rift_S:
return EOculusXRDeviceType::Rift_S;
case ovrpSystemHeadset_Oculus_Link_Quest:
return EOculusXRDeviceType::Quest_Link_Deprecated;
case ovrpSystemHeadset_Oculus_Link_Quest_2:
return EOculusXRDeviceType::Quest2_Link;
case ovrpSystemHeadset_Meta_Link_Quest_Pro:
return EOculusXRDeviceType::MetaQuestProLink;
case ovrpSystemHeadset_Meta_Link_Quest_3:
return EOculusXRDeviceType::MetaQuest3Link;
default:
break;
}
}
}
#endif
return EOculusXRDeviceType::OculusUnknown;
}
EOculusXRControllerType UOculusXRFunctionLibrary::GetControllerType(EControllerHand deviceHand)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
auto getOVRPHand = [](EControllerHand hand) {
switch (hand)
{
case EControllerHand::Left:
return ovrpHand::ovrpHand_Left;
case EControllerHand::Right:
return ovrpHand::ovrpHand_Right;
default:
return ovrpHand::ovrpHand_None;
}
return ovrpHand::ovrpHand_None;
};
auto getEControllerType = [](ovrpInteractionProfile profile) {
switch (profile)
{
case ovrpInteractionProfile::ovrpInteractionProfile_Touch:
return EOculusXRControllerType::MetaQuestTouch;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPro:
return EOculusXRControllerType::MetaQuestTouchPro;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPlus:
return EOculusXRControllerType::MetaQuestTouchPlus;
default:
return EOculusXRControllerType::None;
}
return EOculusXRControllerType::None;
};
ovrpInteractionProfile interactionProfile = ovrpInteractionProfile::ovrpInteractionProfile_None;
ovrpHand hand = getOVRPHand(deviceHand);
if (hand == ovrpHand::ovrpHand_None)
return EOculusXRControllerType::Unknown;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetCurrentInteractionProfile(hand, &interactionProfile)))
{
return getEControllerType(interactionProfile);
}
return EOculusXRControllerType::Unknown;
#endif
return EOculusXRControllerType::Unknown;
}
TArray<float> UOculusXRFunctionLibrary::GetAvailableDisplayFrequencies()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int NumberOfFrequencies;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(nullptr, &NumberOfFrequencies)))
{
TArray<float> freqArray;
freqArray.SetNum(NumberOfFrequencies);
FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(freqArray.GetData(), &NumberOfFrequencies);
return freqArray;
}
}
#endif
return TArray<float>();
}
float UOculusXRFunctionLibrary::GetCurrentDisplayFrequency()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
float Frequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&Frequency)))
{
return Frequency;
}
}
#endif
return 0.0f;
}
void UOculusXRFunctionLibrary::SetDisplayFrequency(float RequestedFrequency)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetSystemDisplayFrequency(RequestedFrequency);
}
#endif
}
void UOculusXRFunctionLibrary::EnablePositionTracking(bool bPositionTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingPositionEnabled2(bPositionTracking);
}
#endif
}
void UOculusXRFunctionLibrary::EnableOrientationTracking(bool bOrientationTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingOrientationEnabled2(bOrientationTracking);
}
#endif
}
void UOculusXRFunctionLibrary::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetColorScaleAndOffset(ColorScale, ColorOffset, bApplyToAllLayers);
}
#endif
}
class IStereoLayers* UOculusXRFunctionLibrary::GetStereoLayers()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return nullptr;
}
/** Helper that converts EOculusXRBoundaryType to ovrpBoundaryType */
#if OCULUS_HMD_SUPPORTED_PLATFORMS
static ovrpBoundaryType ToOvrpBoundaryType(EOculusXRBoundaryType Source)
{
switch (Source)
{
case EOculusXRBoundaryType::Boundary_PlayArea:
return ovrpBoundary_PlayArea;
case EOculusXRBoundaryType::Boundary_Outer:
default:
return ovrpBoundary_Outer;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
bool UOculusXRFunctionLibrary::IsGuardianConfigured()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryConfigured;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&boundaryConfigured)) && boundaryConfigured;
}
#endif
return false;
}
bool UOculusXRFunctionLibrary::IsGuardianDisplayed()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryVisible;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryVisible2(&boundaryVisible)) && boundaryVisible;
}
#endif
return false;
}
TArray<FVector> UOculusXRFunctionLibrary::GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */)
{
TArray<FVector> BoundaryPointList;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
int NumPoints = 0;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, nullptr, &NumPoints)))
{
//allocate points
const int BufferSize = NumPoints;
ovrpVector3f* BoundaryPoints = new ovrpVector3f[BufferSize];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, BoundaryPoints, &NumPoints)))
{
NumPoints = FMath::Min(BufferSize, NumPoints);
check(NumPoints <= BufferSize); // For static analyzer
BoundaryPointList.Reserve(NumPoints);
for (int i = 0; i < NumPoints; i++)
{
FVector point;
if (UsePawnSpace)
{
point = OculusXRHMD->ConvertVector_M2U(BoundaryPoints[i]);
}
else
{
point = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
BoundaryPointList.Add(point);
}
}
delete[] BoundaryPoints;
}
}
}
#endif
return BoundaryPointList;
}
FVector UOculusXRFunctionLibrary::GetGuardianDimensions(EOculusXRBoundaryType BoundaryType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
ovrpVector3f Dimensions;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryDimensions2(obt, &Dimensions)))
return FVector::ZeroVector;
Dimensions.z *= -1.0;
return OculusXRHMD->ConvertVector_M2U(Dimensions);
}
#endif
return FVector::ZeroVector;
}
FTransform UOculusXRFunctionLibrary::GetPlayAreaTransform()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
int NumPoints = 4;
ovrpVector3f BoundaryPoints[4];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(ovrpBoundary_PlayArea, BoundaryPoints, &NumPoints)))
{
FVector ConvertedPoints[4];
for (int i = 0; i < NumPoints; i++)
{
ConvertedPoints[i] = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
float metersScale = OculusXRHMD->GetWorldToMetersScale();
FVector Edge = ConvertedPoints[1] - ConvertedPoints[0];
float Angle = FMath::Acos((Edge).GetSafeNormal() | FVector::RightVector);
FQuat Rotation(FVector::UpVector, Edge.X < 0 ? Angle : -Angle);
FVector Position = (ConvertedPoints[0] + ConvertedPoints[1] + ConvertedPoints[2] + ConvertedPoints[3]) / 4;
FVector Scale(FVector::Distance(ConvertedPoints[3], ConvertedPoints[0]) / metersScale, FVector::Distance(ConvertedPoints[1], ConvertedPoints[0]) / metersScale, 1.0);
return FTransform(Rotation, Position, Scale);
}
}
}
#endif
return FTransform();
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpVector3f OvrpPoint = OculusXRHMD->WorldLocationToOculusPoint(Point);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult InteractionResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryPoint2(OvrpPoint, OvrpBoundaryType, &InteractionResult)))
{
InteractionInfo.IsTriggering = (InteractionResult.IsTriggering != 0);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(InteractionResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(InteractionResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(InteractionResult.ClosestPointNormal);
InteractionInfo.DeviceType = EOculusXRTrackedDeviceType::None;
}
}
#endif
return InteractionInfo;
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpNode OvrpNode = OculusXRHMD::ToOvrpNode(DeviceType);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult TestResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryNode2(OvrpNode, ovrpBoundary_PlayArea, &TestResult)) && TestResult.IsTriggering)
{
InteractionInfo.IsTriggering = true;
InteractionInfo.DeviceType = OculusXRHMD::ToEOculusXRTrackedDeviceType(OvrpNode);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(TestResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(TestResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(TestResult.ClosestPointNormal);
}
}
#endif
return InteractionInfo;
}
void UOculusXRFunctionLibrary::SetGuardianVisibility(bool GuardianVisible)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetBoundaryVisible2(GuardianVisible);
}
#endif
}
bool UOculusXRFunctionLibrary::GetSystemHmd3DofModeEnabled()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHmd3DofModeEnabled(&enabled)) && enabled;
}
#endif
return false;
}
EOculusXRColorSpace UOculusXRFunctionLibrary::GetHmdColorDesc()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace HmdColorSpace;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetHmdColorDesc(&HmdColorSpace)))
{
return (EOculusXRColorSpace)HmdColorSpace;
}
}
#endif
return EOculusXRColorSpace::Unknown;
}
void UOculusXRFunctionLibrary::SetClientColorDesc(EOculusXRColorSpace ColorSpace)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace ClientColorSpace = (ovrpColorSpace)ColorSpace;
#if PLATFORM_ANDROID
if (ClientColorSpace == ovrpColorSpace_Unknown)
{
ClientColorSpace = ovrpColorSpace_Quest;
}
#endif
FOculusXRHMDModule::GetPluginWrapper().SetClientColorDesc(ClientColorSpace);
}
#endif
}
void UOculusXRFunctionLibrary::SetLocalDimmingOn(bool LocalDimmingOn)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
UE_LOG(LogHMD, Log, TEXT("SetLocalDimmingOn %d"), LocalDimmingOn);
FOculusXRHMDModule::GetPluginWrapper().SetLocalDimming(LocalDimmingOn);
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough;
}
return false;
}
#endif
return false;
}
bool UOculusXRFunctionLibrary::IsColorPassthroughSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color;
}
return false;
}
#endif
return false;
}
void UOculusXRFunctionLibrary::StartEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int CreateFlags = 0;
OculusXRHMD->StartEnvironmentDepth(CreateFlags);
}
#endif
}
void UOculusXRFunctionLibrary::StopEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->StopEnvironmentDepth();
}
#endif
}
bool UOculusXRFunctionLibrary::IsEnvironmentDepthStarted()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD->IsEnvironmentDepthStarted();
}
#endif
return false;
}
void UOculusXRFunctionLibrary::SetEnvironmentDepthHandRemoval(bool RemoveHands)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetEnvironmentDepthHandRemoval(RemoveHands);
}
#endif
}
void UOculusXRFunctionLibrary::SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->EnableHardOcclusions(Mode == EOculusXROcclusionsMode::HardOcclusions);
}
#if defined(WITH_OCULUS_BRANCH)
WorldContextObject->GetWorld()->Scene->SetEnableXRPassthroughSoftOcclusions(Mode == EOculusXROcclusionsMode::SoftOcclusions);
#else
ensureMsgf(Mode != EOculusXROcclusionsMode::SoftOcclusions, TEXT("Soft occlusions are only supported with the Oculus branch of the Unreal Engine"));
#endif // defined(WITH_OCULUS_BRANCH)
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
switch (EyeBufferSharpenType)
{
case EOculusXREyeBufferSharpenType::SLST_Normal:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_EfficientSharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Quality:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_QualitySharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Auto:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_AutoLayerFilter);
break;
default:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlags(0));
break;
}
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughRecommended()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* OculusHMD = GetOculusXRHMD();
if (OculusHMD != nullptr)
{
ovrpPassthroughPreferences Preferences;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughPreferences(&Preferences)))
{
return (Preferences.Flags & ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive)
== ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive;
};
}
#endif
return false;
}
#undef LOCTEXT_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,637 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDModule.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMD_Layer.h"
#include "OculusXRHMD_Splash.h"
#include "OculusXRHMD_StressTester.h"
#include "OculusXRHMD_ConsoleCommands.h"
#include "OculusXRHMD_SpectatorScreenController.h"
#include "OculusXRHMD_DynamicResolutionState.h"
#include "OculusXRHMD_DeferredDeletionQueue.h"
#include "OculusXRAssetManager.h"
#include "HeadMountedDisplayBase.h"
#include "HeadMountedDisplay.h"
#include "XRRenderTargetManager.h"
#include "XRRenderBridge.h"
#include "IStereoLayers.h"
#include "Stats/Stats.h"
#include "SceneViewExtension.h"
#include "Engine/Engine.h"
#include "Engine/StaticMeshActor.h"
#include "XRThreadUtils.h"
#include "ProceduralMeshComponent.h"
#include "Shader.h"
#include "Misc/EngineVersionComparison.h"
#include "OculusXRHMD_FoveatedRendering.h"
namespace OculusXRHMD
{
DECLARE_DELEGATE_TwoParams(FOculusXRHMDEventPollingDelegate, ovrpEventDataBuffer*, bool&);
//-------------------------------------------------------------------------------------------------
// FPerformanceStats
//-------------------------------------------------------------------------------------------------
struct FPerformanceStats
{
uint64 Frames;
double Seconds;
FPerformanceStats(uint32 InFrames = 0, double InSeconds = 0.0)
: Frames(InFrames)
, Seconds(InSeconds)
{
}
FPerformanceStats operator-(const FPerformanceStats& PerformanceStats) const
{
return FPerformanceStats(
Frames - PerformanceStats.Frames,
Seconds - PerformanceStats.Seconds);
}
};
enum FRecenterTypes
{
RecenterOrientation = 0x1,
RecenterPosition = 0x2,
RecenterOrientationAndPosition = 0x3
};
//-------------------------------------------------------------------------------------------------
// FOculusXRHMD - Oculus Rift Head Mounted Display
//-------------------------------------------------------------------------------------------------
class FOculusXRHMD : public FHeadMountedDisplayBase, public FXRRenderTargetManager, public IStereoLayers, public FHMDSceneViewExtension, public FOculusAssetManager
{
friend class UOculusXRFunctionLibrary;
friend FOculusXRHMDModule;
friend class FSplash;
friend class FConsoleCommands;
public:
OCULUSXRHMD_API static const FName OculusSystemName;
// IXRSystemIdentifier
virtual FName GetSystemName() const override;
virtual int32 GetXRSystemFlags() const override;
// IXRTrackingSystem
virtual FString GetVersionString() const override;
virtual bool DoesSupportPositionalTracking() const override;
virtual bool HasValidTrackingPosition() override;
virtual bool EnumerateTrackedDevices(TArray<int32>& OutDevices, EXRTrackedDeviceType Type = EXRTrackedDeviceType::Any) override;
virtual bool GetCurrentPose(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetRelativeEyePose(int32 InDeviceId, int32 ViewIndex, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetTrackingSensorProperties(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition, FXRSensorProperties& OutSensorProperties) override;
virtual void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
virtual EHMDTrackingOrigin::Type GetTrackingOrigin() const override;
virtual bool GetFloorToEyeTrackingTransform(FTransform& OutFloorToEye) const override;
//virtual FVector GetAudioListenerOffset(int32 InDeviceId = HMDDeviceId) const override;
virtual void ResetOrientationAndPosition(float Yaw = 0.f) override;
virtual void ResetOrientation(float Yaw = 0.f) override;
virtual void ResetPosition() override;
virtual void SetBaseRotation(const FRotator& BaseRot) override;
virtual FRotator GetBaseRotation() const override;
virtual void SetBaseOrientation(const FQuat& BaseOrient) override;
virtual FQuat GetBaseOrientation() const override;
//virtual TSharedPtr<class IXRCamera, ESPMode::ThreadSafe> GetXRCamera(int32 DeviceId = HMDDeviceId) override;
virtual class IHeadMountedDisplay* GetHMDDevice() override { return this; }
virtual class TSharedPtr<class IStereoRendering, ESPMode::ThreadSafe> GetStereoRenderingDevice() override
{
return SharedThis(this);
}
//virtual class IXRInput* GetXRInput() override;
virtual bool
IsHeadTrackingEnforced() const override;
virtual void SetHeadTrackingEnforced(bool bEnabled) override;
virtual bool IsHeadTrackingAllowed() const override;
virtual void OnBeginPlay(FWorldContext& InWorldContext) override;
virtual void OnEndPlay(FWorldContext& InWorldContext) override;
virtual bool OnStartGameFrame(FWorldContext& WorldContext) override;
virtual bool OnEndGameFrame(FWorldContext& WorldContext) override;
virtual void OnBeginRendering_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
virtual void OnBeginRendering_GameThread() override;
virtual class IXRLoadingScreen* CreateLoadingScreen() override { return GetSplash(); }
virtual FVector2D GetPlayAreaBounds(EHMDTrackingOrigin::Type Origin) const override;
// IHeadMountedDisplay
virtual bool IsHMDConnected() override { return true; }
virtual bool IsHMDEnabled() const override;
virtual EHMDWornState::Type GetHMDWornState() override;
virtual void EnableHMD(bool bEnable = true) override;
virtual bool GetHMDMonitorInfo(MonitorInfo&) override;
virtual void GetFieldOfView(float& InOutHFOVInDegrees, float& InOutVFOVInDegrees) const override;
virtual void SetInterpupillaryDistance(float NewInterpupillaryDistance) override;
virtual float GetInterpupillaryDistance() const override;
//virtual void SetClippingPlanes(float NCP, float FCP) override;
//virtual FVector GetAudioListenerOffset() const override;
virtual bool GetHMDDistortionEnabled(EShadingPath ShadingPath) const override;
//virtual void BeginRendering_RenderThread(const FTransform& NewRelativeTransform, FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
//virtual bool IsSpectatorScreenActive() const override;
//virtual class ISpectatorScreenController* GetSpectatorScreenController() override;
//virtual class ISpectatorScreenController const* GetSpectatorScreenController() const override;
//virtual float GetDistortionScalingFactor() const override;
//virtual float GetLensCenterOffset() const override;
//virtual void GetDistortionWarpValues(FVector4& K) const override;
virtual bool IsChromaAbCorrectionEnabled() const override;
//virtual bool GetChromaAbCorrectionValues(FVector4& K) const override;
virtual bool HasHiddenAreaMesh() const override;
virtual bool HasVisibleAreaMesh() const override;
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
//virtual void DrawDistortionMesh_RenderThread(struct FHeadMountedDisplayPassContext& Context, const FIntPoint& TextureSize) override;
//virtual void UpdateScreenSettings(const FViewport* InViewport) override;
//virtual void UpdatePostProcessSettings(FPostProcessSettings*) override;
//virtual FTexture* GetDistortionTextureLeft() const override;
//virtual FTexture* GetDistortionTextureRight() const override;
//virtual FVector2D GetTextureOffsetLeft() const override;
//virtual FVector2D GetTextureOffsetRight() const override;
//virtual FVector2D GetTextureScaleLeft() const override;
//virtual FVector2D GetTextureScaleRight() const override;
//virtual const float* GetRedDistortionParameters() const override;
//virtual const float* GetGreenDistortionParameters() const override;
//virtual const float* GetBlueDistortionParameters() const override;
//virtual bool NeedsUpscalePostProcessPass() override;
//virtual void RecordAnalytics() override;
//virtual bool DoesAppUseVRFocus() const override;
//virtual bool DoesAppHaveVRFocus() const override;
virtual float GetPixelDenity() const override;
virtual void SetPixelDensity(const float NewPixelDensity) override;
virtual FIntPoint GetIdealRenderTargetSize() const override;
virtual void GetMotionControllerData(UObject* WorldContext, const EControllerHand Hand, FXRMotionControllerData& MotionControllerData) override;
// IStereoRendering interface
virtual bool IsStereoEnabled() const override;
virtual bool IsStereoEnabledOnNextFrame() const override;
virtual bool EnableStereo(bool stereo = true) override;
virtual void AdjustViewRect(int32 ViewIndex, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const override;
virtual void SetFinalViewRect(FRHICommandListImmediate& RHICmdList, const int32 ViewIndex, const FIntRect& FinalViewRect) override;
//virtual FVector2D GetTextSafeRegionBounds() const override;
virtual void CalculateStereoViewOffset(const int32 ViewIndex, FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) override;
virtual FMatrix GetStereoProjectionMatrix(const int32 ViewIndex) const override;
virtual void InitCanvasFromView(class FSceneView* InView, class UCanvas* Canvas) override;
//virtual void GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override;
virtual void RenderTexture_RenderThread(class FRHICommandListImmediate& RHICmdList, class FRHITexture* BackBuffer, class FRHITexture* SrcTexture, FVector2D WindowSize) const override;
//virtual void SetClippingPlanes(float NCP, float FCP) override;
virtual IStereoRenderTargetManager* GetRenderTargetManager() override { return this; }
virtual IStereoLayers* GetStereoLayers() override { return this; }
//virtual void UseImplicitHmdPosition(bool bInImplicitHmdPosition) override;
//virtual bool GetUseImplicitHmdPosition() override;
virtual bool IsStandaloneStereoOnlyDevice() const override { return bIsStandaloneStereoOnlyDevice; }
bool SupportsSpaceWarp() const;
#ifdef WITH_OCULUS_BRANCH
virtual void CalculateScissorRect(const int32 ViewIndex, const FIntRect& ViewRect, FIntRect& OutRect) override;
#endif // WITH_OCULUS_BRANCH
// FHeadMountedDisplayBase interface
virtual FVector2D GetEyeCenterPoint_RenderThread(int32 ViewIndex) const override;
virtual FIntRect GetFullFlatEyeRect_RenderThread(FTexture2DRHIRef EyeTexture) const override;
virtual void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* SrcTexture, FIntRect SrcRect, FRHITexture2D* DstTexture, FIntRect DstRect, bool bClearBlack, bool bNoAlpha) const override;
virtual bool PopulateAnalyticsAttributes(TArray<struct FAnalyticsEventAttribute>& EventAttributes) override;
// FXRRenderTargetManager interface
virtual bool ShouldUseSeparateRenderTarget() const override;
virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override;
virtual bool NeedReAllocateViewportRenderTarget(const class FViewport& Viewport) override;
virtual bool NeedReAllocateDepthTexture(const TRefCountPtr<IPooledRenderTarget>& DepthTarget) override;
virtual bool NeedReAllocateShadingRateTexture(const TRefCountPtr<IPooledRenderTarget>& FoveationTarget) override;
#ifdef WITH_OCULUS_BRANCH
virtual bool NeedReAllocateMotionVectorTexture(const TRefCountPtr<IPooledRenderTarget>& MotionVectorTarget, const TRefCountPtr<IPooledRenderTarget>& MotionVectorDepthTarget) override;
#endif // WITH_OCULUS_BRANCH
virtual bool AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateDepthTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateShadingRateTexture(uint32 Index, uint32 RenderSizeX, uint32 RenderSizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTexture, FIntPoint& OutTextureSize) override;
#ifdef WITH_OCULUS_BRANCH
virtual bool AllocateMotionVectorTexture(uint32 Index, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTexture, FIntPoint& OutTextureSize, FTexture2DRHIRef& OutDepthTexture, FIntPoint& OutDepthTextureSize) override;
virtual bool FindEnvironmentDepthTexture_RenderThread(FTextureRHIRef& OutTexture, FVector2f& OutDepthFactors, FMatrix44f OutScreenToDepthMatrices[2], FMatrix44f OutDepthViewProjMatrices[2]) override;
#endif // WITH_OCULUS_BRANCH
virtual EPixelFormat GetActualColorSwapchainFormat() const override;
virtual void UpdateViewportWidget(bool bUseSeparateRenderTarget, const class FViewport& Viewport, class SViewport* ViewportWidget) override;
virtual FXRRenderBridge* GetActiveRenderBridge_GameThread(bool bUseSeparateRenderTarget);
void AllocateEyeBuffer();
// IStereoLayers interface
virtual uint32 CreateLayer(const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual void DestroyLayer(uint32 LayerId) override;
virtual void SetLayerDesc(uint32 LayerId, const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual bool GetLayerDesc(uint32 LayerId, IStereoLayers::FLayerDesc& OutLayerDesc) override;
virtual void MarkTextureForUpdate(uint32 LayerId) override;
virtual IStereoLayers::FLayerDesc GetDebugCanvasLayerDesc(FTextureRHIRef Texture) override;
virtual void GetAllocatedTexture(uint32 LayerId, FTextureRHIRef& Texture, FTextureRHIRef& LeftTexture) override;
virtual bool ShouldCopyDebugLayersToSpectatorScreen() const override { return true; }
virtual void PushLayerState(bool) override
{ /* Todo */
}
virtual void PopLayerState() override
{ /* Todo */
}
// ISceneViewExtension
virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override;
virtual void PostRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#endif
#else
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#endif
#endif
virtual void PostRenderBasePassDeferred_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView, const FRenderTargetBindingSlots& RenderTargets, TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures) override;
virtual int32 GetPriority() const override;
#ifdef WITH_OCULUS_BRANCH
virtual bool LateLatchingEnabled() const override;
virtual void PreLateLatchingViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override;
#endif
public:
FOculusXRHMD(const FAutoRegister&);
~FOculusXRHMD();
protected:
bool Startup();
void PreShutdown();
void Shutdown();
bool InitializeSession();
void ShutdownSession();
bool InitDevice();
void ReleaseDevice();
void ApplicationPauseDelegate();
void ApplicationResumeDelegate();
bool CheckEyeTrackingPermission(EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod);
void SetupOcclusionMeshes();
void UpdateStereoRenderingParams();
void UpdateHmdRenderInfo();
void InitializeEyeLayer_RenderThread(FRHICommandListImmediate& RHICmdList);
void ApplySystemOverridesOnStereo(bool force = false);
bool OnOculusStateChange(bool bIsEnabledNow);
bool ShouldDisableHiddenAndVisibileAreaMeshForSpectatorScreen_RenderThread() const;
void Recenter(FRecenterTypes RecenterType, float Yaw);
FIntRect GetAsymmetricViewRect(const int32 ViewIndex, const FIntRect& ViewRect);
#if !UE_BUILD_SHIPPING
void DrawDebug(UCanvas* InCanvas, APlayerController* InPlayerController);
#endif
class FSceneViewport* FindSceneViewport();
FOculusXRSplashDesc GetUESplashScreenDesc();
void EyeTrackedFoveatedRenderingFallback();
public:
OCULUSXRHMD_API static FOculusXRHMD* GetOculusXRHMD();
bool IsHMDActive() const;
FSplash* GetSplash() const { return Splash.Get(); }
FCustomPresent* GetCustomPresent_Internal() const { return CustomPresent; }
float GetWorldToMetersScale() const;
ESpectatorScreenMode GetSpectatorScreenMode_RenderThread() const;
FVector GetNeckPosition(const FQuat& HeadOrientation, const FVector& HeadPosition);
/**
* Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position
* to current HMD position (bringing the (0, 0, 0) point to the current HMD position)
* Note, this vector is set by ResetPosition call; use this method with care.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param BaseOffset (in) the vector to be set as base offset, in meters.
*/
void SetBaseOffsetInMeters(const FVector& BaseOffset);
/**
* Returns the currently used base position offset, previously set by the
* ResetPosition or SetBasePositionOffset calls. It represents a vector that translates the HMD's position
* into (0,0,0) point, in meters.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @return Base position offset, in meters.
*/
FVector GetBaseOffsetInMeters() const;
OCULUSXRHMD_API bool ConvertPose(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API bool ConvertPose(const FPose& InPose, ovrpPosef& OutPose) const;
OCULUSXRHMD_API bool ConvertPose_RenderThread(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API static bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
OCULUSXRHMD_API static bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
/** Turns ovrVector3f in Unreal World space to a scaled FVector and applies translation and rotation corresponding to player movement */
FVector ScaleAndMovePointWithPlayer(ovrpVector3f& OculusXRHMDPoint);
/** The inverse of ScaleAndMovePointWithPlayer */
ovrpVector3f WorldLocationToOculusPoint(const FVector& InUnrealPosition);
/** Convert dimension of a float (e.g., a distance) from meters to Unreal Units */
float ConvertFloat_M2U(float OculusFloat) const;
FVector ConvertVector_M2U(ovrpVector3f OculusPoint) const;
struct UserProfile
{
float IPD;
float EyeDepth;
float EyeHeight;
};
bool GetUserProfile(UserProfile& OutProfile);
float GetVsyncToNextVsync() const;
FPerformanceStats GetPerformanceStats() const;
bool DoEnableStereo(bool bStereo);
void ResetControlRotation() const;
void UpdateFoveationOffsets_RenderThread();
bool ComputeEnvironmentDepthParameters_RenderThread(FVector2f& DepthFactors, FMatrix44f ScreenToDepth[ovrpEye_Count], FMatrix44f DepthViewProj[ovrpEye_Count], int& SwapchainIndex);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
void RenderHardOcclusions_RenderThread(FRHICommandListImmediate& RHICmdList, const FSceneView& InView);
#else
void RenderHardOcclusions_RenderThread(FRHICommandList& RHICmdList, const FSceneView& InView);
#endif
FSettingsPtr CreateNewSettings() const;
FGameFramePtr CreateNewGameFrame() const;
FGameFrame* GetFrame()
{
CheckInGameThread();
return Frame.Get();
}
const FGameFrame* GetFrame() const
{
CheckInGameThread();
return Frame.Get();
}
FGameFrame* GetFrame_RenderThread()
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
const FGameFrame* GetFrame_RenderThread() const
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
FGameFrame* GetFrame_RHIThread()
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
const FGameFrame* GetFrame_RHIThread() const
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
FGameFrame* GetNextFrameToRender()
{
CheckInGameThread();
return NextFrameToRender.Get();
}
const FGameFrame* GetNextFrameToRender() const
{
CheckInGameThread();
return NextFrameToRender.Get();
}
FSettings* GetSettings()
{
CheckInGameThread();
return Settings.Get();
}
const FSettings* GetSettings() const
{
CheckInGameThread();
return Settings.Get();
}
FSettings* GetSettings_RenderThread()
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
const FSettings* GetSettings_RenderThread() const
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
FSettings* GetSettings_RHIThread()
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const FSettings* GetSettings_RHIThread() const
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const int GetNextFrameNumber() const { return NextFrameNumber; }
const FRotator GetSplashRotation() const { return SplashRotation; }
void SetSplashRotationToForward();
OCULUSXRHMD_API void StartGameFrame_GameThread(); // Called from OnStartGameFrame or from FOculusXRInput::SendControllerEvents (first actual call of the frame)
void FinishGameFrame_GameThread(); // Called from OnEndGameFrame
void StartRenderFrame_GameThread(); // Called from BeginRenderViewFamily
void FinishRenderFrame_RenderThread(FRDGBuilder& GraphBuilder); // Called from PostRenderViewFamily_RenderThread
void StartRHIFrame_RenderThread(); // Called from PreRenderViewFamily_RenderThread
void FinishRHIFrame_RHIThread(); // Called from FinishRendering_RHIThread
void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel);
void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel);
void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod InFoveationMethod);
void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel InFoveationLevel, bool isDynamic);
void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers);
void SetEnvironmentDepthHandRemoval(bool RemoveHands);
void StartEnvironmentDepth(int CreateFlags);
void StopEnvironmentDepth();
bool IsEnvironmentDepthStarted();
void EnableHardOcclusions(bool bEnable);
OCULUSXRHMD_API void UpdateRTPoses();
FTransform GetLastTrackingToWorld() const { return LastTrackingToWorld; }
OCULUSXRHMD_API void AddEventPollingDelegate(const FOculusXRHMDEventPollingDelegate& NewDelegate);
protected:
FConsoleCommands ConsoleCommands;
void UpdateOnRenderThreadCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void PixelDensityMinCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void PixelDensityMaxCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void HQBufferCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void HQDistortionCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void ShowGlobalMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowQuitMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#if !UE_BUILD_SHIPPING
void StatsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowSettingsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void IPDCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#endif
void LoadFromSettings();
void CheckMultiPlayer();
void DoSessionShutdown();
protected:
void UpdateHMDWornState();
EHMDWornState::Type HMDWornState = EHMDWornState::Unknown;
void UpdateHMDEvents();
void EnableInsightPassthrough_RenderThread(bool bEnablePassthrough);
void DrawHmdViewMesh(
FRHICommandList& RHICmdList,
float X,
float Y,
float SizeX,
float SizeY,
float U,
float V,
float SizeU,
float SizeV,
FIntPoint TargetSize,
FIntPoint TextureSize,
int32 StereoView,
const TShaderRef<class FShader>& VertexShader);
union
{
struct
{
uint64 bApplySystemOverridesOnStereo : 1;
uint64 bNeedEnableStereo : 1;
uint64 bNeedDisableStereo : 1;
};
uint64 Raw;
} Flags;
union
{
struct
{
// set to true when origin was set while OvrSession == null; the origin will be set ASA OvrSession != null
uint64 NeedSetTrackingOrigin : 1;
// enforces exit; used mostly for testing
uint64 EnforceExit : 1;
// set if a game is paused by the plug-in
uint64 AppIsPaused : 1;
// set to indicate that DisplayLost was detected by game thread.
uint64 DisplayLostDetected : 1;
// set to true once new session is created; being handled and reset as soon as session->IsVisible.
uint64 NeedSetFocusToGameViewport : 1;
};
uint64 Raw;
} OCFlags;
TRefCountPtr<FCustomPresent> CustomPresent;
FSplashPtr Splash;
IRendererModule* RendererModule;
FDeferredDeletionQueue DeferredDeletion;
EHMDTrackingOrigin::Type TrackingOrigin;
// Stores difference between ViewRotation and EyeOrientation from previous frame
FQuat LastPlayerOrientation;
// Stores GetFrame()->PlayerLocation (i.e., ViewLocation) from the previous frame
FVector LastPlayerLocation;
FRotator DeltaControlRotation; // used from ApplyHmdRotation
TWeakPtr<SWidget> CachedViewportWidget;
TWeakPtr<SWindow> CachedWindow;
FIntPoint CachedWindowSize;
float CachedWorldToMetersScale;
bool bIsStandaloneStereoOnlyDevice;
// Stores TrackingToWorld from previous frame
FTransform LastTrackingToWorld;
std::atomic<bool> bHardOcclusionsEnabled;
std::atomic<bool> bEnvironmentDepthHandRemovalEnabled;
// These three properties indicate the current state of foveated rendering, which may differ from what's in Settings
// due to cases such as falling back to FFR when eye tracked foveated rendering isn't enabled. Will allow us to resume
// ETFR from situations such as when ET gets paused.
std::atomic<EOculusXRFoveatedRenderingMethod> FoveatedRenderingMethod;
std::atomic<EOculusXRFoveatedRenderingLevel> FoveatedRenderingLevel;
std::atomic<bool> bDynamicFoveatedRendering;
// Game thread
FSettingsPtr Settings;
uint32 NextFrameNumber;
uint32 WaitFrameNumber;
FGameFramePtr Frame; // Valid from OnStartGameFrame to OnEndGameFrame
FGameFramePtr NextFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
FGameFramePtr LastFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
uint32 NextLayerId;
TMap<uint32, FLayerPtr> LayerMap;
bool bNeedReAllocateViewportRenderTarget;
// Render thread
FSettingsPtr Settings_RenderThread;
FGameFramePtr Frame_RenderThread; // Valid from BeginRenderViewFamily to PostRenderViewFamily_RenderThread
TArray<FLayerPtr> Layers_RenderThread;
FLayerPtr EyeLayer_RenderThread; // Valid to be accessed from game thread, since updated only when game thread is waiting
bool bNeedReAllocateDepthTexture_RenderThread;
bool bNeedReAllocateFoveationTexture_RenderThread;
bool bNeedReAllocateMotionVectorTexture_RenderThread;
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
TSharedPtr<FOculusXRFoveatedRenderingImageGenerator, ESPMode::ThreadSafe> FoveationImageGenerator;
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)
// RHI thread
FSettingsPtr Settings_RHIThread;
FGameFramePtr Frame_RHIThread; // Valid from PreRenderViewFamily_RenderThread to FinishRendering_RHIThread
TArray<FLayerPtr> Layers_RHIThread;
FHMDViewMesh HiddenAreaMeshes[2];
FHMDViewMesh VisibleAreaMeshes[2];
FPerformanceStats PerformanceStats;
FRotator SplashRotation; // rotation applied to all splash screens (dependent on HMD orientation as the splash is shown)
TArray<FTextureRHIRef> EnvironmentDepthSwapchain;
#if !UE_BUILD_SHIPPING
FDelegateHandle DrawDebugDelegateHandle;
#endif
enum class FInsightInitStatus
{
NotInitialized,
Initialized,
Failed,
};
FInsightInitStatus InsightInitStatus;
bool bShutdownRequestQueued;
bool bEyeTrackedFoveatedRenderingSupported;
TArray<FOculusXRHMDEventPollingDelegate> EventPollingDelegates;
// MultiPlayer
bool bMultiPlayer;
bool bShouldWait_GameThread;
bool bIsRendering_RenderThread;
};
typedef TSharedPtr<FOculusXRHMD, ESPMode::ThreadSafe> FOculusXRHMDPtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,509 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "Containers/StringConv.h"
#include "Misc/EngineVersion.h"
#include "Misc/Paths.h"
#if PLATFORM_ANDROID
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#include "Interfaces/IPluginManager.h"
#include "ShaderCore.h"
#include "OculusXRTelemetry.h"
#if PLATFORM_WINDOWS
#include "OculusXRSimulator.h"
#include "OculusXRSyntheticEnvironmentServer.h"
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
namespace
{
void __cdecl OvrpLogCallback2(ovrpLogLevel InLevel, const char* Message, int Length)
{
ELogVerbosity::Type OutLevel;
switch (InLevel)
{
case ovrpLogLevel_Debug:
OutLevel = ELogVerbosity::Log;
break;
case ovrpLogLevel_Info:
OutLevel = ELogVerbosity::Display;
break;
case ovrpLogLevel_Error:
OutLevel = ELogVerbosity::Error;
break;
default:
OutLevel = ELogVerbosity::NoLogging;
}
const FString MessageStr(Length, Message);
GLog->CategorizedLogf(TEXT("LogOVRPlugin"), OutLevel, TEXT("%s"), *MessageStr);
}
} // namespace
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
OculusPluginWrapper FOculusXRHMDModule::PluginWrapper{};
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusPluginWrapper& FOculusXRHMDModule::GetPluginWrapper()
{
return PluginWrapper;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
FOculusXRHMDModule::FOculusXRHMDModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
bPreInit = false;
bPreInitCalled = false;
OVRPluginHandle = nullptr;
GraphicsAdapterLuid = 0;
#endif
}
void FOculusXRHMDModule::StartupModule()
{
IHeadMountedDisplayModule::StartupModule();
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/Plugin/OculusXR"), PluginShaderDir);
}
void FOculusXRHMDModule::ShutdownModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (PluginWrapper.IsInitialized())
{
OculusXRTelemetry::FTelemetryBackend::OnEditorShutdown();
PluginWrapper.Shutdown2();
OculusPluginWrapper::DestroyOculusPluginWrapper(&PluginWrapper);
}
if (OVRPluginHandle)
{
FPlatformProcess::FreeDllHandle(OVRPluginHandle);
OVRPluginHandle = nullptr;
}
#endif
}
#if PLATFORM_ANDROID
extern bool AndroidThunkCpp_IsOculusMobileApplication();
#endif
FString FOculusXRHMDModule::GetModuleKeyName() const
{
return FString(TEXT("OculusXRHMD"));
}
void FOculusXRHMDModule::GetModuleAliases(TArray<FString>& AliasesOut) const
{
// Pre-OculusXR rename (5.0.3 v44)
AliasesOut.Add(TEXT("OculusHMD"));
}
bool FOculusXRHMDModule::PreInit()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (!bPreInitCalled)
{
bPreInit = false;
#if PLATFORM_ANDROID
bPreInitCalled = true;
if (!AndroidThunkCpp_IsOculusMobileApplication())
{
UE_LOG(LogHMD, Log, TEXT("App is not packaged for Oculus Mobile"));
return false;
}
#endif
// Init module if app can render
if (FApp::CanEverRender())
{
// Load OVRPlugin
OVRPluginHandle = GetOVRPluginHandle();
if (!OVRPluginHandle)
{
UE_LOG(LogHMD, Log, TEXT("Failed loading OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
return false;
}
if (!OculusPluginWrapper::InitializeOculusPluginWrapper(&PluginWrapper))
{
UE_LOG(LogHMD, Log, TEXT("Failed InitializeOculusPluginWrapper"));
return false;
}
// Initialize OVRPlugin
ovrpRenderAPIType PreinitApiType = ovrpRenderAPI_None;
#if PLATFORM_ANDROID
void* Activity = (void*)FAndroidApplication::GetGameActivityThis();
PreinitApiType = ovrpRenderAPI_Vulkan;
#else
void* Activity = nullptr;
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
PluginWrapper.SetLogCallback2(OvrpLogCallback2);
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
if (OVRP_FAILURE(PluginWrapper.PreInitialize5(Activity, PreinitApiType, ovrpPreinitializeFlags::ovrpPreinitializeFlag_None)))
{
UE_LOG(LogHMD, Log, TEXT("Failed initializing OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
#if WITH_EDITOR && PLATFORM_WINDOWS
// In the editor, we want to allow the headset to connect after the editor has booted.
// To do this, we must have PreInit() return true, to prevent the HMD module from being unloaded.
return GIsEditor;
#else
return false;
#endif
}
#if PLATFORM_WINDOWS
bPreInitCalled = true;
const LUID* DisplayAdapterId;
if (OVRP_SUCCESS(PluginWrapper.GetDisplayAdapterId2((const void**)&DisplayAdapterId)) && DisplayAdapterId)
{
SetGraphicsAdapterLuid(*(const uint64*)DisplayAdapterId);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD display adapter"));
}
const WCHAR* AudioInDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioInDeviceId2((const void**)&AudioInDeviceId)) && AudioInDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio input device"));
}
const WCHAR* AudioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&AudioOutDeviceId)) && AudioOutDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio output device"));
}
#endif
float ModulePriority;
if (!GConfig->GetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni))
{
// if user doesn't set priority set it for them to allow this hmd to be used if enabled
ModulePriority = 45.0f;
GConfig->SetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni);
}
UE_LOG(LogHMD, Log, TEXT("FOculusXRHMDModule PreInit successfully"));
bPreInit = true;
}
}
return bPreInit;
#else
return false;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool FOculusXRHMDModule::IsHMDConnected()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
if (FApp::CanEverRender() && HMDSettings->XrApi != EOculusXRXrApi::NativeOpenXR)
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
uint64 FOculusXRHMDModule::GetGraphicsAdapterLuid()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
if (!GraphicsAdapterLuid)
{
int GraphicsAdapter;
if (GConfig->GetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), GraphicsAdapter, GEngineIni) && GraphicsAdapter >= 0)
{
TRefCountPtr<IDXGIFactory> DXGIFactory;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())) && SUCCEEDED(DXGIFactory->EnumAdapters(GraphicsAdapter, DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
FMemory::Memcpy(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid));
}
}
}
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return GraphicsAdapterLuid;
#else
return 0;
#endif
}
FString FOculusXRHMDModule::GetAudioInputDevice()
{
FString AudioInputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInputDevice, GEngineIni);
#endif
return AudioInputDevice;
}
FString FOculusXRHMDModule::GetAudioOutputDevice()
{
FString AudioOutputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#if PLATFORM_WINDOWS
if (bPreInit)
{
if (FApp::CanEverRender())
{
const WCHAR* audioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&audioOutDeviceId)) && audioOutDeviceId)
{
AudioOutputDevice = audioOutDeviceId;
}
}
}
#else
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutputDevice, GEngineIni);
#endif
#endif
return AudioOutputDevice;
}
TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> FOculusXRHMDModule::CreateTrackingSystem()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit || (GIsEditor && PLATFORM_WINDOWS))
{
//If -HMDSimulator is used as the command option to launch UE, use simulator runtime instead of the physical HMD runtime (like PC-Link).
if (FParse::Param(FCommandLine::Get(), TEXT("HMDSimulator")) && GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MetaXRJsonPath.FilePath.Len())
{
if (!IsSimulatorActivated())
{
ToggleOpenXRRuntime();
}
}
OculusXRHMD::FOculusXRHMDPtr OculusXRHMD = FSceneViewExtensions::NewExtension<OculusXRHMD::FOculusXRHMD>();
if (OculusXRHMD->Startup())
{
HeadMountedDisplay = OculusXRHMD;
return OculusXRHMD;
}
}
HeadMountedDisplay = nullptr;
#endif
return nullptr;
}
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> FOculusXRHMDModule::GetVulkanExtensions()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit)
{
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FVulkanExtensions);
}
}
#if WITH_EDITOR && PLATFORM_WINDOWS
else if (GIsEditor)
{
// OpenXR has no ability to query for possible vulkan extensions without connecting a HMD.
// This is a problem, because we need to create our VkInstance and VkDevice to render in 2D and there's no HMD.
// For now, as a workaround, we hardcode the extensions that Oculus's OpenXR implementation needs.
// Eventually, one of three things has to happen for a proper fix:
//
// 1. OculusXRHMD (or, better, OVRPlugin) maintains a separate VkInstance that has the right extensions,
// and uses the vk_external extensions to transfer data between them when needed.
// 2. OpenXR changes to allow querying instance and device extensions without an active HMD.
// It may still require a physical device handle to list device extensions.
// 3. Oculus's Link implementation for OpenXR changes to allow an XrSystemId to be created before a headset
// is connected (possibly as an opt-in OpenXR extension for backwards compatibility).
//
// (2) or (3) are preferable, but if OpenXR is held constant we will have to do (1).
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FEditorVulkanExtensions);
}
}
#endif
return VulkanExtensions;
#endif
return nullptr;
}
FString FOculusXRHMDModule::GetDeviceSystemName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpSystemHeadset SystemHeadset;
if (PluginWrapper.IsInitialized() && OVRP_SUCCESS(PluginWrapper.GetSystemHeadsetType2(&SystemHeadset)))
{
switch (SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return FString("Oculus Quest");
case ovrpSystemHeadset_Oculus_Quest_2:
default:
return FString("Oculus Quest2");
#ifdef WITH_OCULUS_BRANCH
case ovrpSystemHeadset_Meta_Quest_Pro:
return FString("Meta Quest Pro");
case ovrpSystemHeadset_Meta_Quest_3:
return FString("Meta Quest 3");
#endif // WITH_OCULUS_BRANCH
}
}
return FString();
#else
return FString();
#endif
}
bool FOculusXRHMDModule::IsStandaloneStereoOnlyDevice()
{
#if PLATFORM_ANDROID
return FAndroidMisc::GetDeviceMake() == FString("Oculus");
#else
return false;
#endif
}
bool FOculusXRHMDModule::IsSimulatorActivated()
{
#if PLATFORM_WINDOWS
return FMetaXRSimulator::IsSimulatorActivated();
#else
return false;
#endif
}
void FOculusXRHMDModule::ToggleOpenXRRuntime()
{
#if PLATFORM_WINDOWS
FMetaXRSimulator::ToggleOpenXRRuntime();
#endif
}
void FOculusXRHMDModule::LaunchEnvironment(FString EnvironmentName)
{
#if PLATFORM_WINDOWS
FMetaXRSES::LaunchEnvironment(EnvironmentName);
#endif
}
void FOculusXRHMDModule::StopServer()
{
#if PLATFORM_WINDOWS
FMetaXRSES::StopServer();
#endif
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* FOculusXRHMDModule::GetOVRPluginHandle()
{
void* OVRPluginHandle = nullptr;
#if PLATFORM_WINDOWS
FString XrApi;
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD") || !GConfig->GetString(TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings"), TEXT("XrApi"), XrApi, GEngineIni) || XrApi.Equals(FString("OVRPluginOpenXR")))
{
FString BinariesPath = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/Win64"));
FPlatformProcess::PushDllDirectory(*BinariesPath);
OVRPluginHandle = FPlatformProcess::GetDllHandle(*(BinariesPath / "OpenXR/OVRPlugin.dll"));
FPlatformProcess::PopDllDirectory(*BinariesPath);
}
#elif PLATFORM_ANDROID
OVRPluginHandle = FPlatformProcess::GetDllHandle(TEXT("libOVRPlugin.so"));
#endif // PLATFORM_ANDROID
return OVRPluginHandle;
}
bool FOculusXRHMDModule::PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const
{
OculusXRHMD::CheckInGameThread();
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(HeadMountedDisplay.Pin().Get());
if (OculusXRHMD)
{
ovrpPosef InPose;
InPose.Orientation = OculusXRHMD::ToOvrpQuatf(InOrientation);
InPose.Position = OculusXRHMD::ToOvrpVector3f(InPosition);
OculusXRHMD::FPose OutPose;
if (OculusXRHMD->ConvertPose(InPose, OutPose))
{
OutOrientation = OutPose.Orientation;
OutPosition = OutPose.Position;
return true;
}
}
return false;
}
void FOculusXRHMDModule::SetGraphicsAdapterLuid(uint64 InLuid)
{
GraphicsAdapterLuid = InLuid;
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
TRefCountPtr<IDXGIFactory> DXGIFactory;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())))
{
for (int32 adapterIndex = 0;; adapterIndex++)
{
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (FAILED(DXGIFactory->EnumAdapters(adapterIndex, DXGIAdapter.GetInitReference())) || FAILED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
break;
}
if (!FMemory::Memcmp(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid)))
{
// Remember this adapterIndex so we use the right adapter, even when we startup without HMD connected
GConfig->SetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), adapterIndex, GEngineIni);
break;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRHMDModule, OculusXRHMD)

View File

@@ -0,0 +1,121 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IHeadMountedDisplay.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMD_VulkanExtensions.h"
#include "OculusXRPluginWrapper.h"
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
class FOculusXRHMDModule : public IOculusXRHMDModule
{
public:
FOculusXRHMDModule();
static inline FOculusXRHMDModule& Get()
{
return FModuleManager::LoadModuleChecked<FOculusXRHMDModule>("OculusXRHMD");
}
// IModuleInterface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// IHeadMountedDisplayModule
virtual FString GetModuleKeyName() const override;
virtual void GetModuleAliases(TArray<FString>& AliasesOut) const override;
virtual bool PreInit() override;
virtual bool IsHMDConnected() override;
virtual uint64 GetGraphicsAdapterLuid() override;
virtual FString GetAudioInputDevice() override;
virtual FString GetAudioOutputDevice() override;
virtual FString GetDeviceSystemName() override;
virtual TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> CreateTrackingSystem() override;
virtual TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> GetVulkanExtensions() override;
virtual bool IsStandaloneStereoOnlyDevice() override;
// IOculusXRHMDModule
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector) override
{
UOculusXRFunctionLibrary::GetPose(DeviceRotation, DevicePosition, NeckPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera, PositionScale);
}
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds) override
{
UOculusXRFunctionLibrary::GetRawSensorData(AngularAcceleration, LinearAcceleration, AngularVelocity, LinearVelocity, TimeInSeconds, EOculusXRTrackedDeviceType::HMD);
}
virtual bool GetUserProfile(struct FOculusXRHmdUserProfile& Profile) override
{
return UOculusXRFunctionLibrary::GetUserProfile(Profile);
}
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(Rotation, BaseOffsetInMeters, Options);
}
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(OutRotation, OutBaseOffsetInMeters);
}
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(BaseRot, PosOffset, Options);
}
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(OutRot, OutPosOffset);
}
virtual class IStereoLayers* GetStereoLayers() override
{
return UOculusXRFunctionLibrary::GetStereoLayers();
}
bool IsOVRPluginAvailable() const
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return OVRPluginHandle != nullptr;
#else
return false;
#endif
}
// FMetaXRSimulator
OCULUSXRHMD_API static bool IsSimulatorActivated();
OCULUSXRHMD_API static void ToggleOpenXRRuntime();
// FMetaXRSES
OCULUSXRHMD_API static void LaunchEnvironment(FString EnvironmentName);
OCULUSXRHMD_API static void StopServer();
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OCULUSXRHMD_API static void* GetOVRPluginHandle();
OCULUSXRHMD_API static OculusPluginWrapper& GetPluginWrapper();
virtual bool PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const override;
protected:
void SetGraphicsAdapterLuid(uint64 InLuid);
static OculusPluginWrapper PluginWrapper;
bool bPreInit;
bool bPreInitCalled;
void* OVRPluginHandle;
uint64 GraphicsAdapterLuid;
TWeakPtr<IHeadMountedDisplay, ESPMode::ThreadSafe> HeadMountedDisplay;
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> VulkanExtensions;
friend class ::OculusXRHMD::FOculusXRHMD;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
};

View File

@@ -0,0 +1,95 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDPrivate.h"
#include "RHICommandList.h"
#include "RenderingThread.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
// TODO: Change in case of parallel game threads
bool InGameThread()
{
if (GIsGameThreadIdInitialized)
{
return FPlatformTLS::GetCurrentThreadId() == GGameThreadId;
}
else
{
return true;
}
}
bool InRenderThread()
{
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
{
return IsInParallelRenderingThread();
}
else
{
return InGameThread();
}
}
// TODO: Change in case of parallel RHI threads
bool InRHIThread()
{
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
{
if (IsRHIThreadRunning())
{
if (IsInRHIThread())
{
return true;
}
if (IsInParallelRenderingThread())
{
return GetImmediateCommandList_ForRenderCommand().Bypass();
}
return false;
}
else
{
return IsInParallelRenderingThread();
}
}
else
{
return InGameThread();
}
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
// apply base orientation correction
OutPose.Orientation = BaseOrientation.Inverse() * InPose.Orientation;
OutPose.Orientation.Normalize();
// correct position according to BaseOrientation and BaseOffset.
OutPose.Position = (InPose.Position - BaseOffset) * WorldToMetersScale;
OutPose.Position = BaseOrientation.Inverse().RotateVector(OutPose.Position);
return true;
}
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
return ConvertPose_Internal(FPose(ToFQuat(InPose.Orientation), ToFVector(InPose.Position)), OutPose, BaseOrientation, BaseOffset, WorldToMetersScale);
}
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
OutPose.Orientation = ToOvrpQuatf(BaseOrientation * InPose.Orientation);
OutPose.Position = ToOvrpVector3f(BaseOrientation.RotateVector(InPose.Position) / WorldToMetersScale + BaseOffset);
return true;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,311 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/WorldSettings.h"
#include "IOculusXRHMDModule.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRPassthroughLayerShapes.h"
#include "StereoRendering.h"
#include "HAL/RunnableThread.h"
#include "RHI.h"
#include <functional>
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN (PLATFORM_WINDOWS || PLATFORM_ANDROID)
#else
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN 0
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// OVRPlugin
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRPluginWrapper.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
namespace OculusXRHMD
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FPose
{
FQuat Orientation;
FVector Position;
FPose()
: Orientation(EForceInit::ForceInit)
, Position(EForceInit::ForceInit)
{
}
FPose(const FQuat& InOrientation, const FVector& InPosition)
: Orientation(InOrientation), Position(InPosition) {}
FPose Inverse() const
{
FQuat InvOrientation = Orientation.Inverse();
FVector InvPosition = InvOrientation.RotateVector(-Position);
return FPose(InvOrientation, InvPosition);
}
FPose operator*(const FPose& other) const
{
return FPose(Orientation * other.Orientation, Orientation.RotateVector(other.Position) + Position);
}
};
/** Converts ovrpQuatf to FQuat */
FORCEINLINE FQuat ToFQuat(const ovrpQuatf& InQuat)
{
return FQuat(-InQuat.z, InQuat.x, InQuat.y, -InQuat.w);
}
/** Converts FQuat to ovrpQuatf */
FORCEINLINE ovrpQuatf ToOvrpQuatf(const FQuat& InQuat)
{
return ovrpQuatf{ static_cast<float>(InQuat.Y), static_cast<float>(InQuat.Z), static_cast<float>(-InQuat.X), static_cast<float>(-InQuat.W) };
}
/** Converts vector from Oculus to Unreal */
FORCEINLINE FVector ToFVector(const ovrpVector3f& InVec)
{
return FVector(-InVec.z, InVec.x, InVec.y);
}
/** Converts vector from Unreal to Oculus. */
FORCEINLINE ovrpVector3f ToOvrpVector3f(const FVector& InVec)
{
return ovrpVector3f{ static_cast<float>(InVec.Y), static_cast<float>(InVec.Z), static_cast<float>(-InVec.X) };
}
FORCEINLINE FMatrix ToFMatrix(const ovrpMatrix4f& vtm)
{
// Rows and columns are swapped between ovrpMatrix4f and FMatrix
return FMatrix(
FPlane(vtm.M[0][0], vtm.M[1][0], vtm.M[2][0], vtm.M[3][0]),
FPlane(vtm.M[0][1], vtm.M[1][1], vtm.M[2][1], vtm.M[3][1]),
FPlane(vtm.M[0][2], vtm.M[1][2], vtm.M[2][2], vtm.M[3][2]),
FPlane(vtm.M[0][3], vtm.M[1][3], vtm.M[2][3], vtm.M[3][3]));
}
FORCEINLINE ovrpVector4f LinearColorToOvrpVector4f(const FLinearColor& InColor)
{
return ovrpVector4f{ InColor.R, InColor.G, InColor.B, InColor.A };
}
FORCEINLINE ovrpRecti ToOvrpRecti(const FIntRect& rect)
{
return ovrpRecti{ { rect.Min.X, rect.Min.Y }, { rect.Size().X, rect.Size().Y } };
}
FORCEINLINE ovrpColorf ToOvrpColorf(const FLinearColor LinearColor)
{
return ovrpColorf{ LinearColor.R, LinearColor.G, LinearColor.B, LinearColor.A };
}
FORCEINLINE ovrpMatrix4f ToOvrpMatrix(FMatrix Matrix)
{
ovrpMatrix4f Result;
Result.M[0][0] = Matrix.M[0][0];
Result.M[0][1] = Matrix.M[0][1];
Result.M[0][2] = Matrix.M[0][2];
Result.M[0][3] = Matrix.M[0][3];
Result.M[1][0] = Matrix.M[1][0];
Result.M[1][1] = Matrix.M[1][1];
Result.M[1][2] = Matrix.M[1][2];
Result.M[1][3] = Matrix.M[1][3];
Result.M[2][0] = Matrix.M[2][0];
Result.M[2][1] = Matrix.M[2][1];
Result.M[2][2] = Matrix.M[2][2];
Result.M[2][3] = Matrix.M[2][3];
Result.M[3][0] = Matrix.M[3][0];
Result.M[3][1] = Matrix.M[3][1];
Result.M[3][2] = Matrix.M[3][2];
Result.M[3][3] = Matrix.M[3][3];
return Result;
}
/** Helper that converts ovrTrackedDeviceType to EOculusXRTrackedDeviceType */
FORCEINLINE EOculusXRTrackedDeviceType ToEOculusXRTrackedDeviceType(ovrpNode Source)
{
EOculusXRTrackedDeviceType Destination = EOculusXRTrackedDeviceType::All; // Best attempt at initialization
switch (Source)
{
case ovrpNode_None:
Destination = EOculusXRTrackedDeviceType::None;
break;
case ovrpNode_Head:
Destination = EOculusXRTrackedDeviceType::HMD;
break;
case ovrpNode_HandLeft:
Destination = EOculusXRTrackedDeviceType::LTouch;
break;
case ovrpNode_HandRight:
Destination = EOculusXRTrackedDeviceType::RTouch;
break;
case ovrpNode_DeviceObjectZero:
Destination = EOculusXRTrackedDeviceType::DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
/** Helper that converts EOculusXRTrackedDeviceType to ovrTrackedDeviceType */
FORCEINLINE ovrpNode ToOvrpNode(EOculusXRTrackedDeviceType Source)
{
ovrpNode Destination = ovrpNode_None; // Best attempt at initialization
switch (Source)
{
case EOculusXRTrackedDeviceType::None:
Destination = ovrpNode_None;
break;
case EOculusXRTrackedDeviceType::HMD:
Destination = ovrpNode_Head;
break;
case EOculusXRTrackedDeviceType::LTouch:
Destination = ovrpNode_HandLeft;
break;
case EOculusXRTrackedDeviceType::RTouch:
Destination = ovrpNode_HandRight;
break;
case EOculusXRTrackedDeviceType::DeviceObjectZero:
Destination = ovrpNode_DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
FORCEINLINE int32 ToExternalDeviceId(const ovrpNode Source)
{
int32 ExternalDeviceId = INDEX_NONE;
switch (Source)
{
case ovrpNode_Head:
// required to be zero (see IXRTrackingSystem::HMDDeviceId)
ExternalDeviceId = 0;
break;
case ovrpNode_None:
case ovrpNode_Count:
case ovrpNode_EnumSize:
// ExternalDeviceId = INDEX_NONE;
break;
default:
// add one, in case the enum value is zero (conflicting with the HMD)
ExternalDeviceId = 1 + (int32)Source;
break;
}
return ExternalDeviceId;
}
FORCEINLINE ovrpNode ToOvrpNode(const int32 ExternalDeviceId)
{
ovrpNode Destination = ovrpNode_None;
switch (ExternalDeviceId)
{
case 0:
// zero implies HMD (see ToExternalDeviceId/IXRTrackingSystem::HMDDeviceId)
Destination = ovrpNode_Head;
break;
case -1:
// Destination = ovrpNode_None;
break;
default:
// we added one to avoid collision with the HMD's ID (see ToExternalDeviceId)
Destination = ovrpNode(ExternalDeviceId - 1);
break;
}
return Destination;
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
FORCEINLINE ovrpInsightPassthroughColorMapType ToOVRPColorMapType(EOculusXRColorMapType InColorMapType)
{
switch (InColorMapType)
{
case ColorMapType_GrayscaleToColor:
return ovrpInsightPassthroughColorMapType_MonoToRgba;
case ColorMapType_Grayscale:
return ovrpInsightPassthroughColorMapType_MonoToMono;
case ColorMapType_ColorAdjustment:
return ovrpInsightPassthroughColorMapType_BrightnessContrastSaturation;
case ColorMapType_ColorLut:
return ovrpInsightPassthroughColorMapType_ColorLut;
case ColorMapType_ColorLut_Interpolated:
return ovrpInsightPassthroughColorMapType_InterpolatedColorLut;
default:
return ovrpInsightPassthroughColorMapType_None;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
/** Check currently executing from Game thread */
OCULUSXRHMD_API bool InGameThread();
FORCEINLINE void CheckInGameThread()
{
#if DO_CHECK
check(InGameThread());
#endif
}
/** Check currently executing from Render thread */
OCULUSXRHMD_API bool InRenderThread();
FORCEINLINE void CheckInRenderThread()
{
#if DO_CHECK
check(InRenderThread());
#endif
}
/** Check currently executing from RHI thread */
OCULUSXRHMD_API bool InRHIThread();
FORCEINLINE void CheckInRHIThread()
{
#if DO_CHECK
check(InRHIThread());
#endif
}
FORCEINLINE bool GetUnitScaleFactorFromSettings(UWorld* World, float& outWorldToMeters)
{
if (IsValid(World))
{
const auto* WorldSettings = World->GetWorldSettings();
if (IsValid(WorldSettings))
{
outWorldToMeters = WorldSettings->WorldToMeters;
return true;
}
}
return false;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,64 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// D3D11
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "ID3D11DynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
//-------------------------------------------------------------------------------------------------
// D3D12
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#define GetD3D11CubeFace GetD3D12CubeFace
#define VerifyD3D11Result VerifyD3D12Result
#define GetD3D11TextureFromRHITexture GetD3D12TextureFromRHITexture
#define FRingAllocation FRingAllocation_D3D12
#define GetRenderTargetFormat GetRenderTargetFormat_D3D12
#define ED3D11ShaderOffsetBuffer ED3D12ShaderOffsetBuffer
#define FindShaderResourceDXGIFormat FindShaderResourceDXGIFormat_D3D12
#define FindUnorderedAccessDXGIFormat FindUnorderedAccessDXGIFormat_D3D12
#define FindDepthStencilDXGIFormat FindDepthStencilDXGIFormat_D3D12
#define HasStencilBits HasStencilBits_D3D12
#define FVector4VertexDeclaration FVector4VertexDeclaration_D3D12
#define GLOBAL_CONSTANT_BUFFER_INDEX GLOBAL_CONSTANT_BUFFER_INDEX_D3D12
#define MAX_CONSTANT_BUFFER_SLOTS MAX_CONSTANT_BUFFER_SLOTS_D3D12
#define FD3DGPUProfiler FD3D12GPUProfiler
#define FRangeAllocator FRangeAllocator_D3D12
#include "ID3D12DynamicRHI.h"
#undef GetD3D11CubeFace
#undef VerifyD3D11Result
#undef GetD3D11TextureFromRHITexture
#undef FRingAllocation
#undef GetRenderTargetFormat
#undef ED3D11ShaderOffsetBuffer
#undef FindShaderResourceDXGIFormat
#undef FindUnorderedAccessDXGIFormat
#undef FindDepthStencilDXGIFormat
#undef HasStencilBits
#undef FVector4VertexDeclaration
#undef GLOBAL_CONSTANT_BUFFER_INDEX
#undef MAX_CONSTANT_BUFFER_SLOTS
#undef FD3DGPUProfiler
#undef FRangeAllocator
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
//-------------------------------------------------------------------------------------------------
// Vulkan
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "IVulkanDynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,299 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDRuntimeSettings.h"
//////////////////////////////////////////////////////////////////////////
// UOculusXRHMDRuntimeSettings
#include "OculusXRHMD_Settings.h"
UOculusXRHMDRuntimeSettings::UOculusXRHMDRuntimeSettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bAutoEnabled(false)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
// FSettings is the sole source of truth for Oculus default settings
OculusXRHMD::FSettings DefaultSettings;
SystemSplashBackground = DefaultSettings.SystemSplashBackground;
bSupportsDash = DefaultSettings.Flags.bSupportsDash;
bCompositesDepth = DefaultSettings.Flags.bCompositeDepth;
bHQDistortion = DefaultSettings.Flags.bHQDistortion;
SuggestedCpuPerfLevel = DefaultSettings.SuggestedCpuPerfLevel;
SuggestedGpuPerfLevel = DefaultSettings.SuggestedGpuPerfLevel;
FoveatedRenderingMethod = DefaultSettings.FoveatedRenderingMethod;
FoveatedRenderingLevel = DefaultSettings.FoveatedRenderingLevel;
bDynamicFoveatedRendering = DefaultSettings.bDynamicFoveatedRendering;
bSupportEyeTrackedFoveatedRendering = DefaultSettings.bSupportEyeTrackedFoveatedRendering;
PixelDensityMin = DefaultSettings.PixelDensityMin;
PixelDensityMax = DefaultSettings.PixelDensityMax;
bFocusAware = DefaultSettings.Flags.bFocusAware;
bDynamicResolution = DefaultSettings.Flags.bPixelDensityAdaptive;
XrApi = DefaultSettings.XrApi;
ColorSpace = DefaultSettings.ColorSpace;
ControllerPoseAlignment = DefaultSettings.ControllerPoseAlignment;
bRequiresSystemKeyboard = DefaultSettings.Flags.bRequiresSystemKeyboard;
HandTrackingSupport = DefaultSettings.HandTrackingSupport;
HandTrackingFrequency = DefaultSettings.HandTrackingFrequency;
HandTrackingVersion = DefaultSettings.HandTrackingVersion;
bInsightPassthroughEnabled = DefaultSettings.Flags.bInsightPassthroughEnabled;
bBodyTrackingEnabled = DefaultSettings.Flags.bBodyTrackingEnabled;
bEyeTrackingEnabled = DefaultSettings.Flags.bEyeTrackingEnabled;
bFaceTrackingEnabled = DefaultSettings.Flags.bFaceTrackingEnabled;
bSupportExperimentalFeatures = DefaultSettings.bSupportExperimentalFeatures;
bAnchorSupportEnabled = DefaultSettings.Flags.bAnchorSupportEnabled;
bAnchorSharingEnabled = DefaultSettings.Flags.bAnchorSharingEnabled;
bSceneSupportEnabled = DefaultSettings.Flags.bSceneSupportEnabled;
ProcessorFavor = DefaultSettings.ProcessorFavor;
bTileTurnOffEnabled = DefaultSettings.Flags.bTileTurnOffEnabled;
FaceTrackingDataSource.Empty(static_cast<int8>(EFaceTrackingDataSourceConfig::MAX));
FaceTrackingDataSource.Append(DefaultSettings.FaceTrackingDataSource);
// Default this to false, FSettings doesn't have a separate composite depth flag for mobile
bCompositeDepthMobile = false;
#else
// Some set of reasonable defaults, since blueprints are still available on non-Oculus platforms.
SystemSplashBackground = ESystemSplashBackgroundType::Black;
bSupportsDash = false;
bCompositesDepth = false;
bHQDistortion = false;
SuggestedCpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedLow;
SuggestedGpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedHigh;
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
bDynamicFoveatedRendering = false;
bSupportEyeTrackedFoveatedRendering = false;
PixelDensityMin = 0.8f;
PixelDensityMax = 1.2f;
bDynamicResolution = false;
bCompositeDepthMobile = false;
bFocusAware = true;
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
bLateLatching = false;
ColorSpace = EOculusXRColorSpace::P3;
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Default;
bRequiresSystemKeyboard = false;
HandTrackingSupport = EOculusXRHandTrackingSupport::ControllersOnly;
HandTrackingFrequency = EOculusXRHandTrackingFrequency::Low;
HandTrackingVersion = EOculusXRHandTrackingVersion::Default;
bInsightPassthroughEnabled = false;
bSupportExperimentalFeatures = false;
bBodyTrackingEnabled = false;
bEyeTrackingEnabled = false;
bFaceTrackingEnabled = false;
bAnchorSupportEnabled = false;
bAnchorSharingEnabled = false;
bSceneSupportEnabled = false;
ProcessorFavor = EProcessorFavor::FavorEqually;
bTileTurnOffEnabled = false;
#endif
LoadFromIni();
}
#if WITH_EDITOR
bool UOculusXRHMDRuntimeSettings::CanEditChange(const FProperty* InProperty) const
{
bool bIsEditable = Super::CanEditChange(InProperty);
if (bIsEditable && InProperty)
{
const FName PropertyName = InProperty->GetFName();
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi) && !FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
bIsEditable = false;
}
// Disable settings for marketplace release that are only compatible with the Oculus engine fork
#ifndef WITH_OCULUS_BRANCH
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicResolution))
{
bIsEditable = false;
}
#endif // WITH_OCULUS_BRANCH
}
return bIsEditable;
}
void UOculusXRHMDRuntimeSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property != nullptr)
{
// Automatically switch to Fixed Foveated Rendering when removing Eye Tracked Foveated rendering support
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) && !bSupportEyeTrackedFoveatedRendering)
{
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod)), GetDefaultConfigFilename());
}
// Automatically enable support for eye tracked foveated rendering when selecting the Eye Tracked Foveated Rendering method
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) && FoveatedRenderingMethod == EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering)
{
bSupportEyeTrackedFoveatedRendering = true;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering)), GetDefaultConfigFilename());
}
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices))
{
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
{
// Get a list of all available devices
TArray<EOculusXRSupportedDevices> deviceList;
#define OCULUS_DEVICE_LOOP(device) deviceList.Add(device);
FOREACH_ENUM_EOCULUSXRSUPPORTEDDEVICES(OCULUS_DEVICE_LOOP);
#undef OCULUS_DEVICE_LOOP
// Add last device that isn't already in the list
for (int i = deviceList.Num() - 1; i >= 0; --i)
{
if (!SupportedDevices.Contains(deviceList[i]))
{
SupportedDevices.Last() = deviceList[i];
break;
}
// Just add another copy of the first device if nothing was available
SupportedDevices.Last() = deviceList[deviceList.Num() - 1];
}
}
}
}
}
#endif // WITH_EDITOR
void UOculusXRHMDRuntimeSettings::PostInitProperties()
{
Super::PostInitProperties();
RenameProperties();
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi)), GetDefaultConfigFilename());
}
}
void UOculusXRHMDRuntimeSettings::LoadFromIni()
{
const TCHAR* OculusSettings = TEXT("Oculus.Settings");
bool v;
float f;
FVector vec;
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMax"), f, GEngineIni))
{
check(!FMath::IsNaN(f));
PixelDensityMax = f;
}
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMin"), f, GEngineIni))
{
check(!FMath::IsNaN(f));
PixelDensityMin = f;
}
if (GConfig->GetBool(OculusSettings, TEXT("bHQDistortion"), v, GEngineIni))
{
bHQDistortion = v;
}
if (GConfig->GetBool(OculusSettings, TEXT("bCompositeDepth"), v, GEngineIni))
{
bCompositesDepth = v;
}
}
/** This essentially acts like redirects for plugin settings saved in the engine config.
Anything added here should check for the current setting in the config so that if the dev changes the setting manually, we don't overwrite it with the old setting.
Note: Do not use UpdateSinglePropertyInConfigFile() here, since that uses a temp config to save the single property,
it'll get overwritten when GConfig->RemoveKey() marks the main config as dirty and it gets saved again **/
void UOculusXRHMDRuntimeSettings::RenameProperties()
{
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
bool v = false;
FString str;
// FFRLevel was renamed to FoveatedRenderingLevel
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), str, GetDefaultConfigFilename()) && GConfig->GetString(OculusSettings, TEXT("FFRLevel"), str, GetDefaultConfigFilename()))
{
if (str.Equals(TEXT("FFR_Off")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
}
else if (str.Equals(TEXT("FFR_Low")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Low;
}
else if (str.Equals(TEXT("FFR_Medium")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Medium;
}
else if (str.Equals(TEXT("FFR_High")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::High;
}
else if (str.Equals(TEXT("FFR_HighTop")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::HighTop;
}
// Use GetNameStringByValue() here because GetValueAsString() includes the type name as well
GConfig->SetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), *StaticEnum<EOculusXRFoveatedRenderingLevel>()->GetNameStringByValue((int64)FoveatedRenderingLevel), GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRLevel"), GetDefaultConfigFilename());
}
// FFRDynamic was renamed to bDynamicFoveatedRendering
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), str, GetDefaultConfigFilename()) && GConfig->GetBool(OculusSettings, TEXT("FFRDynamic"), v, GetDefaultConfigFilename()))
{
bDynamicFoveatedRendering = v;
GConfig->SetBool(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), bDynamicFoveatedRendering, GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRDynamic"), GetDefaultConfigFilename());
}
const FString Quest = TEXT("Quest");
#ifndef WITH_OCULUS_BRANCH
const TCHAR* AndroidSettings = TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings");
TArray<FString> PackageList;
const TCHAR* PackageForMobileKey = TEXT("+PackageForOculusMobile");
if (GConfig->GetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename()))
{
const FString Quest2 = TEXT("Quest2");
if (PackageList.Contains(Quest))
{
PackageList.Remove(Quest);
if (!PackageList.Contains(Quest2))
{
PackageList.Add(Quest2);
}
GConfig->SetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename());
}
}
#endif // WITH_OCULUS_BRANCH
TArray<FString> DeviceList;
const TCHAR* SupportedDevicesKey = *FString("+").Append(GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices));
if (GConfig->GetArray(OculusSettings, SupportedDevicesKey, DeviceList, GetDefaultConfigFilename()))
{
const EOculusXRSupportedDevices LastSupportedDevice = EOculusXRSupportedDevices::Quest2;
const FString LastSupportedDeviceString = StaticEnum<EOculusXRSupportedDevices>()->GetNameStringByValue((int64)LastSupportedDevice);
if (DeviceList.Contains(Quest))
{
DeviceList.Remove(Quest);
if (!DeviceList.Contains(LastSupportedDeviceString))
{
DeviceList.Add(LastSupportedDeviceString);
}
GConfig->SetArray(OculusSettings, SupportedDevicesKey, DeviceList, GetDefaultConfigFilename());
// Reflect the config changes in the Project Settings UI
SupportedDevices.Remove((EOculusXRSupportedDevices)0); // Enums that don't exist just have a value of 0
if (!SupportedDevices.Contains(LastSupportedDevice))
{
SupportedDevices.Add(LastSupportedDevice);
}
}
}
}

View File

@@ -0,0 +1,90 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_ConsoleCommands.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "OculusXRSceneCaptureCubemap.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
/// @cond DOXYGEN_WARNINGS
FConsoleCommands::FConsoleCommands(class FOculusXRHMD* InHMDPtr)
: UpdateOnRenderThreadCommand(TEXT("vr.oculus.bUpdateOnRenderThread"),
*NSLOCTEXT("OculusRift", "CCommandText_UpdateRT", "Oculus Rift specific extension.\nEnables or disables updating on the render thread.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::UpdateOnRenderThreadCommandHandler))
, PixelDensityMinCommand(TEXT("vr.oculus.PixelDensity.min"),
*NSLOCTEXT("OculusRift", "CCommandText_PixelDensityMin", "Oculus Rift specific extension.\nMinimum pixel density when adaptive pixel density is enabled").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::PixelDensityMinCommandHandler))
, PixelDensityMaxCommand(TEXT("vr.oculus.PixelDensity.max"),
*NSLOCTEXT("OculusRift", "CCommandText_PixelDensityMax", "Oculus Rift specific extension.\nMaximum pixel density when adaptive pixel density is enabled").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::PixelDensityMaxCommandHandler))
, HQBufferCommand(TEXT("vr.oculus.bHQBuffer"),
*NSLOCTEXT("OculusRift", "CCommandText_HQBuffer", "Oculus Rift specific extension.\nEnable or disable using floating point texture format for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQBufferCommandHandler))
, HQDistortionCommand(TEXT("vr.oculus.bHQDistortion"),
*NSLOCTEXT("OculusRift", "CCommandText_HQDistortion", "Oculus Rift specific extension.\nEnable or disable using multiple mipmap levels for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQDistortionCommandHandler))
, ShowGlobalMenuCommand(TEXT("vr.oculus.ShowGlobalMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_GlobalMenu", "Oculus Rift specific extension.\nOpens the global menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowGlobalMenuCommandHandler))
, ShowQuitMenuCommand(TEXT("vr.oculus.ShowQuitMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_QuitMenu", "Oculus Rift specific extension.\nOpens the quit menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowQuitMenuCommandHandler))
#if !UE_BUILD_SHIPPING
, StatsCommand(TEXT("vr.oculus.Debug.bShowStats"),
*NSLOCTEXT("OculusRift", "CCommandText_Stats", "Oculus Rift specific extension.\nEnable or disable rendering of stats.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::StatsCommandHandler))
, CubemapCommand(TEXT("vr.oculus.Debug.CaptureCubemap"),
*NSLOCTEXT("OculusRift", "CCommandText_Cubemap", "Oculus Rift specific extension.\nCaptures a cubemap for Oculus Home.\nOptional arguments (default is zero for all numeric arguments):\n xoff=<float> -- X axis offset from the origin\n yoff=<float> -- Y axis offset\n zoff=<float> -- Z axis offset\n yaw=<float> -- the direction to look into (roll and pitch is fixed to zero)\n mobile -- Generate a Mobile format cubemap\n (height of the captured cubemap will be 1024 instead of 2048 pixels)\n").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(&UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler))
, ShowSettingsCommand(TEXT("vr.oculus.Debug.Show"),
*NSLOCTEXT("OculusRift", "CCommandText_Show", "Oculus Rift specific extension.\nShows the current value of various stereo rendering params.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowSettingsCommandHandler))
, IPDCommand(TEXT("vr.oculus.Debug.IPD"),
*NSLOCTEXT("OculusRift", "CCommandText_IPD", "Oculus Rift specific extension.\nShows or changes the current interpupillary distance in meters.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::IPDCommandHandler))
#endif // !UE_BUILD_SHIPPING
{
}
bool FConsoleCommands::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
const TCHAR* OrigCmd = Cmd;
FString AliasedCommand;
if (FParse::Command(&Cmd, TEXT("OVRGLOBALMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowGlobalMenu");
}
else if (FParse::Command(&Cmd, TEXT("OVRQUITMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowQuitMenu");
}
#if !UE_BUILD_SHIPPING
else if (FParse::Command(&Cmd, TEXT("vr.oculus.Debug.EnforceHeadTracking")))
{
AliasedCommand = TEXT("vr.HeadTracking.bEnforced");
}
#endif // !UE_BUILD_SHIPPING
if (!AliasedCommand.IsEmpty())
{
Ar.Logf(ELogVerbosity::Warning, TEXT("%s is deprecated. Use %s instead"), OrigCmd, *AliasedCommand);
return IConsoleManager::Get().ProcessUserConsoleInput(*AliasedCommand, Ar, InWorld);
}
return false;
}
/// @endcond
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,45 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "HAL/IConsoleManager.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
class FConsoleCommands : private FSelfRegisteringExec
{
public:
FConsoleCommands(class FOculusXRHMD* InHMDPtr);
// FSelfRegisteringExec interface
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
private:
FAutoConsoleCommand UpdateOnRenderThreadCommand;
FAutoConsoleCommand PixelDensityMinCommand;
FAutoConsoleCommand PixelDensityMaxCommand;
FAutoConsoleCommand HQBufferCommand;
FAutoConsoleCommand HQDistortionCommand;
FAutoConsoleCommand ShowGlobalMenuCommand;
FAutoConsoleCommand ShowQuitMenuCommand;
#if !UE_BUILD_SHIPPING
// Debug console commands
FAutoConsoleCommand StatsCommand;
FAutoConsoleCommand CubemapCommand;
FAutoConsoleCommand ShowSettingsCommand;
FAutoConsoleCommand IPDCommand;
#endif // !UE_BUILD_SHIPPING
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,657 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "ScreenRendering.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "OculusShaders.h"
#include "CommonRenderResources.h"
#include "RHIStaticStates.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidEGL.h"
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#define VULKAN_CUBEMAP_POSITIVE_Y 2
#define VULKAN_CUBEMAP_NEGATIVE_Y 3
namespace OculusXRHMD
{
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainMipLevel"), SF_Pixel);
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSsRGBSourceMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSsRGBSourceMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSsRGBSourceMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSsRGBSourceMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSsRGBSourceMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainsRGBSourceMipLevel"), SF_Pixel);
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
FCustomPresent::FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool bInSupportsSRGB)
: OculusXRHMD(InOculusXRHMD)
, RenderAPI(InRenderAPI)
, DefaultPixelFormat(InDefaultPixelFormat)
, bSupportsSRGB(bInSupportsSRGB)
, bSupportsSubsampled(false)
, bIsStandaloneStereoDevice(false)
{
CheckInGameThread();
DefaultOvrpTextureFormat = GetOvrpTextureFormat(GetDefaultPixelFormat());
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_None;
#if PLATFORM_ANDROID
bIsStandaloneStereoDevice = FAndroidMisc::GetDeviceMake() == FString("Oculus");
#endif
// grab a pointer to the renderer module for displaying our mirror window
static const FName RendererModuleName("Renderer");
RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);
}
void FCustomPresent::ReleaseResources_RHIThread()
{
CheckInRHIThread();
if (MirrorTextureRHI.IsValid())
{
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
MirrorTextureRHI = nullptr;
}
}
void FCustomPresent::Shutdown()
{
CheckInGameThread();
// OculusXRHMD is going away, but this object can live on until viewport is destroyed
ExecuteOnRenderThread([this]() {
ExecuteOnRHIThread([this]() {
OculusXRHMD = nullptr;
});
});
}
bool FCustomPresent::NeedsNativePresent()
{
return !bIsStandaloneStereoDevice;
}
bool FCustomPresent::Present(int32& SyncInterval)
{
CheckInRHIThread();
if (OculusXRHMD)
{
FGameFrame* Frame_RHIThread = OculusXRHMD->GetFrame_RHIThread();
if (Frame_RHIThread)
{
FinishRendering_RHIThread();
}
}
SyncInterval = 0; // VSync off
return NeedsNativePresent();
}
void FCustomPresent::UpdateMirrorTexture_RenderThread()
{
SCOPE_CYCLE_COUNTER(STAT_BeginRendering);
CheckInRenderThread();
const ESpectatorScreenMode MirrorWindowMode = OculusXRHMD->GetSpectatorScreenMode_RenderThread();
const FIntPoint MirrorWindowSize = OculusXRHMD->GetFrame_RenderThread()->WindowSize;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
// Need to destroy mirror texture?
if (MirrorTextureRHI.IsValid())
{
const auto MirrorTextureSize = FIntPoint(MirrorTextureRHI->GetDesc().Extent.X, MirrorTextureRHI->GetDesc().Extent.Y);
if (MirrorWindowMode != ESpectatorScreenMode::Distorted || MirrorWindowSize != MirrorTextureSize)
{
ExecuteOnRHIThread([]() {
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
});
MirrorTextureRHI = nullptr;
}
}
// Need to create mirror texture?
if (!MirrorTextureRHI.IsValid() && MirrorWindowMode == ESpectatorScreenMode::Distorted && MirrorWindowSize.X != 0 && MirrorWindowSize.Y != 0)
{
const int Width = MirrorWindowSize.X;
const int Height = MirrorWindowSize.Y;
ovrpTextureHandle TextureHandle;
ExecuteOnRHIThread([&]() {
FOculusXRHMDModule::GetPluginWrapper().SetupMirrorTexture2(GetOvrpDevice(), Height, Width, GetDefaultOvrpTextureFormat(), &TextureHandle);
});
UE_LOG(LogHMD, Log, TEXT("Allocated a new mirror texture (size %d x %d)"), Width, Height);
ETextureCreateFlags TexCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
MirrorTextureRHI = CreateTexture_RenderThread(Width, Height, GetDefaultPixelFormat(), FClearValueBinding::None, 1, 1, 1, RRT_Texture2D, TextureHandle, TexCreateFlags)->GetTexture2D();
}
}
}
void FCustomPresent::FinishRendering_RHIThread()
{
SCOPE_CYCLE_COUNTER(STAT_FinishRendering);
CheckInRHIThread();
#if STATS
if (OculusXRHMD->GetFrame_RHIThread()->ShowFlags.Rendering)
{
ovrpAppLatencyTimings AppLatencyTimings;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppLatencyTimings2(&AppLatencyTimings)))
{
SET_FLOAT_STAT(STAT_LatencyRender, AppLatencyTimings.LatencyRender * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyTimewarp, AppLatencyTimings.LatencyTimewarp * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyPostPresent, AppLatencyTimings.LatencyPostPresent * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorRender, AppLatencyTimings.ErrorRender * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorTimewarp, AppLatencyTimings.ErrorTimewarp * 1000.0f);
}
}
#endif
OculusXRHMD->FinishRHIFrame_RHIThread();
#if PLATFORM_ANDROID
float GPUFrameTime = 0.0f;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetGPUFrameTime(&GPUFrameTime)))
{
SubmitGPUFrameTime(GPUFrameTime);
}
#endif
}
EPixelFormat FCustomPresent::GetPixelFormat(EPixelFormat Format) const
{
switch (Format)
{
// case PF_B8G8R8A8:
case PF_FloatRGBA:
case PF_FloatR11G11B10:
// case PF_R8G8B8A8:
case PF_G16:
case PF_R16F:
case PF_R32_FLOAT:
case PF_ShadowDepth:
case PF_D24:
return Format;
}
return GetDefaultPixelFormat();
}
EPixelFormat FCustomPresent::GetPixelFormat(ovrpTextureFormat Format) const
{
switch (Format)
{
// case ovrpTextureFormat_R8G8B8A8_sRGB:
// case ovrpTextureFormat_R8G8B8A8:
// return PF_R8G8B8A8;
case ovrpTextureFormat_R16G16B16A16_FP:
return PF_FloatRGBA;
case ovrpTextureFormat_R11G11B10_FP:
return PF_FloatR11G11B10;
// case ovrpTextureFormat_B8G8R8A8_sRGB:
// case ovrpTextureFormat_B8G8R8A8:
// return PF_B8G8R8A8;
case ovrpTextureFormat_R16:
return PF_G16; // G stands for grey here, not green, and is actually R16 in RHI
case ovrpTextureFormat_R16_FP:
return PF_R16F;
case ovrpTextureFormat_R32_FP:
return PF_R32_FLOAT;
case ovrpTextureFormat_D16:
return PF_ShadowDepth; // ShadowDepth maps to D16 in Vulkan
case ovrpTextureFormat_D24_S8:
return PF_D24;
}
return GetDefaultPixelFormat();
}
ovrpTextureFormat FCustomPresent::GetOvrpTextureFormat(EPixelFormat Format, bool usesRGB) const
{
switch (GetPixelFormat(Format))
{
case PF_B8G8R8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_B8G8R8A8_sRGB : ovrpTextureFormat_B8G8R8A8;
case PF_FloatRGBA:
return ovrpTextureFormat_R16G16B16A16_FP;
case PF_FloatR11G11B10:
return ovrpTextureFormat_R11G11B10_FP;
case PF_R8G8B8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_R8G8B8A8_sRGB : ovrpTextureFormat_R8G8B8A8;
case PF_G16:
return ovrpTextureFormat_R16;
case PF_R16F:
return ovrpTextureFormat_R16_FP;
case PF_R32_FLOAT:
return ovrpTextureFormat_R32_FP;
case PF_ShadowDepth:
return ovrpTextureFormat_D16;
case PF_D24:
return ovrpTextureFormat_D24_S8;
}
return ovrpTextureFormat_None;
}
bool FCustomPresent::IsSRGB(ovrpTextureFormat InFormat)
{
switch (InFormat)
{
case ovrpTextureFormat_B8G8R8A8_sRGB:
case ovrpTextureFormat_R8G8B8A8_sRGB:
return true;
}
return false;
}
int FCustomPresent::GetSystemRecommendedMSAALevel() const
{
int SystemRecommendedMSAALevel = 1;
FOculusXRHMDModule::GetPluginWrapper().GetSystemRecommendedMSAALevel2(&SystemRecommendedMSAALevel);
return SystemRecommendedMSAALevel;
}
FXRSwapChainPtr FCustomPresent::CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
TArray<FTextureRHIRef> RHITextureSwapChain = CreateSwapChainTextures_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures, InTexCreateFlags, DebugName);
FTextureRHIRef RHITexture = GDynamicRHI->RHICreateAliasedTexture(RHITextureSwapChain[0]);
return CreateXRSwapChain(MoveTemp(RHITextureSwapChain), RHITexture);
}
TArray<FTextureRHIRef> FCustomPresent::CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
CheckInRenderThread();
TArray<FTextureRHIRef> RHITextureSwapChain;
{
for (int32 TextureIndex = 0; TextureIndex < InTextures.Num(); ++TextureIndex)
{
FTextureRHIRef TexRef = CreateTexture_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures[TextureIndex], InTexCreateFlags);
FString TexName = FString::Printf(TEXT("%s (%d/%d)"), DebugName, TextureIndex, InTextures.Num());
TexRef->SetName(*TexName);
RHIBindDebugLabelName(TexRef, *TexName);
RHITextureSwapChain.Add(TexRef);
}
}
return RHITextureSwapChain;
}
void FCustomPresent::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture,
FIntRect DstRect, FIntRect SrcRect, bool bAlphaPremultiply, bool bNoAlphaWrite, bool bInvertY, bool sRGBSource, bool bInvertAlpha) const
{
CheckInRenderThread();
FIntPoint DstSize;
FIntPoint SrcSize;
if (DstTexture->GetDesc().IsTexture2D() && SrcTexture->GetDesc().IsTexture2D())
{
DstSize = FIntPoint(DstTexture->GetSizeX(), DstTexture->GetSizeY());
SrcSize = FIntPoint(SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
}
else if (DstTexture->GetDesc().IsTextureCube() && SrcTexture->GetDesc().IsTextureCube())
{
DstSize = FIntPoint(DstTexture->GetSize(), DstTexture->GetSize());
SrcSize = FIntPoint(SrcTexture->GetSize(), SrcTexture->GetSize());
}
else
{
return;
}
if (DstRect.IsEmpty())
{
DstRect = FIntRect(FIntPoint::ZeroValue, DstSize);
}
if (SrcRect.IsEmpty())
{
SrcRect = FIntRect(FIntPoint::ZeroValue, SrcSize);
}
const uint32 ViewportWidth = DstRect.Width();
const uint32 ViewportHeight = DstRect.Height();
const FIntPoint TargetSize(ViewportWidth, ViewportHeight);
float U = SrcRect.Min.X / (float)SrcSize.X;
float V = SrcRect.Min.Y / (float)SrcSize.Y;
float USize = SrcRect.Width() / (float)SrcSize.X;
float VSize = SrcRect.Height() / (float)SrcSize.Y;
#if PLATFORM_ANDROID // on android, top-left isn't 0/0 but 1/0.
if (bInvertY)
{
V = 1.0f - V;
VSize = -VSize;
}
#endif
FRHITexture* SrcTextureRHI = SrcTexture;
RHICmdList.Transition(FRHITransitionInfo(SrcTextureRHI, ERHIAccess::Unknown, ERHIAccess::SRVGraphics));
FGraphicsPipelineStateInitializer GraphicsPSOInit;
if (bInvertAlpha)
{
// write RGBA, RGB = src.rgb * 1 + dst.rgb * 0, A = src.a * 0 + dst.a * (1 - src.a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
}
else if (bAlphaPremultiply)
{
if (bNoAlphaWrite)
{
// for quads, write RGB, RGB = src.rgb * 1 + dst.rgb * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
else
{
// for quads, write RGBA, RGB = src.rgb * src.a + dst.rgb * 0, A = src.a + dst.a * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
}
else
{
if (bNoAlphaWrite)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB>::GetRHI();
}
else
{
// for mirror window, write RGBA, RGB = src.rgb * src.a + dst.rgb * (1 - src.a), A = src.a * 1 + dst.a * (1 - src a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
}
}
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const auto FeatureLevel = OculusXRHMD->GetSettings_RenderThread()->CurrentFeatureLevel;
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
if (DstTexture->GetDesc().IsTexture2D())
{
sRGBSource &= EnumHasAnyFlags(SrcTexture->GetFlags(), TexCreate_SRGB);
// Need to copy over mip maps on Android since they are not generated like they are on PC
#if PLATFORM_ANDROID
uint32 NumMips = SrcTexture->GetNumMips();
#else
uint32 NumMips = 1;
#endif
const bool bUseTexArrayShader = SrcTexture->GetDesc().IsTextureArray() && DstTexture->GetDesc().IsTextureArray();
const int32 SliceCount = bUseTexArrayShader ? FMath::Min(SrcTexture->GetDesc().ArraySize, DstTexture->GetDesc().ArraySize) : 1;
for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
RPInfo.ColorRenderTargets[0].MipIndex = MipIndex;
for (int32 SliceIndex = 0; SliceIndex < SliceCount; ++SliceIndex)
{
RPInfo.ColorRenderTargets[0].ArraySlice = SliceIndex;
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTexture"));
{
const uint32 MipViewportWidth = ViewportWidth >> MipIndex;
const uint32 MipViewportHeight = ViewportHeight >> MipIndex;
const FIntPoint MipTargetSize(MipViewportWidth, MipViewportHeight);
if (bNoAlphaWrite || bInvertAlpha)
{
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
if (!sRGBSource)
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
else
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSsRGBSourceMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSsRGBSourceMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Min.X + MipViewportWidth, DstRect.Min.Y + MipViewportHeight, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, MipViewportWidth, MipViewportHeight,
U, V, USize, VSize,
MipTargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
else
{
for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
// On Vulkan the positive and negative Y faces of the cubemap need to be flipped
if (RenderAPI == ovrpRenderAPI_Vulkan)
{
int NewFaceIndex = 0;
if (FaceIndex == VULKAN_CUBEMAP_POSITIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_NEGATIVE_Y;
else if (FaceIndex == VULKAN_CUBEMAP_NEGATIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_POSITIVE_Y;
else
NewFaceIndex = FaceIndex;
RPInfo.ColorRenderTargets[0].ArraySlice = NewFaceIndex;
}
else
{
RPInfo.ColorRenderTargets[0].ArraySlice = FaceIndex;
}
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTextureFace"));
{
if (bNoAlphaWrite)
{
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
TShaderMapRef<FOculusCubemapPS> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, FaceIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, FaceIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, ViewportWidth, ViewportHeight,
#if PLATFORM_ANDROID
U, V, USize, VSize,
#else
U, 1.0 - V, USize, -VSize,
#endif
TargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
void FCustomPresent::SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
RHICmdList.SubmitCommandsHint();
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,117 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "XRSwapChain.h"
#include "RHI.h"
#include "RendererInterface.h"
#include "IStereoLayers.h"
#include "XRRenderBridge.h"
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
DECLARE_STATS_GROUP(TEXT("OculusXRHMD"), STATGROUP_OculusXRHMD, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("BeginRendering"), STAT_BeginRendering, STATGROUP_OculusXRHMD);
DECLARE_CYCLE_STAT(TEXT("FinishRendering"), STAT_FinishRendering, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyRender"), STAT_LatencyRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyTimewarp"), STAT_LatencyTimewarp, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyPostPresent"), STAT_LatencyPostPresent, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorRender"), STAT_ErrorRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorTimewarp"), STAT_ErrorTimewarp, STATGROUP_OculusXRHMD);
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
class FCustomPresent : public FXRRenderBridge
{
public:
FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool InSupportsSRGB);
// FXRRenderBridge/FRHICustomPresent
virtual bool NeedsNativePresent() override;
virtual bool Present(int32& SyncInterval) override;
virtual void FinishRendering_RHIThread();
ovrpRenderAPIType GetRenderAPI() const { return RenderAPI; }
virtual bool IsUsingCorrectDisplayAdapter() const { return true; }
void UpdateMirrorTexture_RenderThread();
void ReleaseResources_RHIThread();
void Shutdown();
FTexture2DRHIRef GetMirrorTexture() { return MirrorTextureRHI; }
virtual void* GetOvrpInstance() const { return nullptr; }
virtual void* GetOvrpPhysicalDevice() const { return nullptr; }
virtual void* GetOvrpDevice() const { return nullptr; }
virtual void* GetOvrpCommandQueue() const { return nullptr; }
EPixelFormat GetPixelFormat(EPixelFormat InFormat) const;
EPixelFormat GetPixelFormat(ovrpTextureFormat InFormat) const;
EPixelFormat GetDefaultPixelFormat() const { return DefaultPixelFormat; }
ovrpTextureFormat GetOvrpTextureFormat(EPixelFormat InFormat, bool usesRGB = true) const;
ovrpTextureFormat GetDefaultOvrpTextureFormat() const { return DefaultOvrpTextureFormat; }
ovrpTextureFormat GetDefaultDepthOvrpTextureFormat() const { return DefaultDepthOvrpTextureFormat; }
static bool IsSRGB(ovrpTextureFormat InFormat);
virtual int GetSystemRecommendedMSAALevel() const;
virtual int GetLayerFlags() const { return 0; }
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags TexCreateFlags) = 0;
FXRSwapChainPtr CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
TArray<FTextureRHIRef> CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture, FIntRect DstRect = FIntRect(), FIntRect SrcRect = FIntRect(), bool bAlphaPremultiply = false, bool bNoAlphaWrite = false, bool bInvertY = true, bool sRGBSource = false, bool bInvertAlpha = false) const;
void SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList);
virtual void SubmitGPUFrameTime(float GPUFrameTime) {}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread(){};
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2]){};
#endif // WITH_OCULUS_BRANCH
bool SupportsSRGB()
{
return bSupportsSRGB;
}
bool SupportsSubsampled() { return bSupportsSubsampled; }
protected:
FOculusXRHMD* OculusXRHMD;
ovrpRenderAPIType RenderAPI;
EPixelFormat DefaultPixelFormat;
bool bSupportsSRGB;
bool bSupportsSubsampled;
ovrpTextureFormat DefaultOvrpTextureFormat;
ovrpTextureFormat DefaultDepthOvrpTextureFormat;
IRendererModule* RendererModule;
FTexture2DRHIRef MirrorTextureRHI;
bool bIsStandaloneStereoDevice;
};
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD);
#endif
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,118 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FD3D11CustomPresent
//-------------------------------------------------------------------------------------------------
class FD3D11CustomPresent : public FCustomPresent
{
public:
FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D11CustomPresent::FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D11, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D11CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D11Device> D3D11Device;
ExecuteOnRenderThread([&D3D11Device]() {
D3D11Device = (ID3D11Device*)RHIGetNativeDevice();
});
if (D3D11Device)
{
TRefCountPtr<IDXGIDevice> DXGIDevice;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(D3D11Device->QueryInterface(__uuidof(IDXGIDevice), (void**)DXGIDevice.GetInitReference())) && SUCCEEDED(DXGIDevice->GetAdapter(DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
return !FMemory::Memcmp(luid, &DXGIAdapterDesc.AdapterLuid, sizeof(LUID));
}
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D11CustomPresent::GetOvrpDevice() const
{
return GetID3D11DynamicRHI()->RHIGetDevice();
}
FTextureRHIRef FD3D11CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
switch (InResourceType)
{
case RRT_Texture2D:
return GetID3D11DynamicRHI()->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_Texture2DArray:
return GetID3D11DynamicRHI()->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_TextureCube:
return GetID3D11DynamicRHI()->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags | TexCreate_TargetArraySlicesIndependently, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D11CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11

View File

@@ -0,0 +1,114 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentD3D12
//-------------------------------------------------------------------------------------------------
class FD3D12CustomPresent : public FCustomPresent
{
public:
FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D12CustomPresent::FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D12, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D12CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D12Device> D3DDevice;
ExecuteOnRenderThread([&D3DDevice]() {
D3DDevice = (ID3D12Device*)RHIGetNativeDevice();
});
if (D3DDevice)
{
LUID AdapterLuid = D3DDevice->GetAdapterLuid();
return !FMemory::Memcmp(luid, &AdapterLuid, sizeof(LUID));
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D12CustomPresent::GetOvrpDevice() const
{
return GetID3D12DynamicRHI()->RHIGetCommandQueue();
}
FTextureRHIRef FD3D12CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
switch (InResourceType)
{
case RRT_Texture2D:
return DynamicRHI->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_Texture2DArray:
return DynamicRHI->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_TextureCube:
return DynamicRHI->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D12CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12

View File

@@ -0,0 +1,171 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "OculusXRHMD.h"
#include "IVulkanDynamicRHI.h"
#if PLATFORM_WINDOWS
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentVulkan
//-------------------------------------------------------------------------------------------------
class FVulkanCustomPresent : public FCustomPresent
{
public:
FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpInstance() const override;
virtual void* GetOvrpPhysicalDevice() const override;
virtual void* GetOvrpDevice() const override;
virtual void* GetOvrpCommandQueue() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread() override;
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseTileOffsets, FIntPoint TileOffsets[2]) override;
#endif // WITH_OCULUS_BRANCH
};
FVulkanCustomPresent::FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_Vulkan, PF_R8G8B8A8, true)
{
#if PLATFORM_ANDROID
if (GRHISupportsRHIThread && GIsThreadedRendering && GUseRHIThread_InternalUseOnly)
{
SetRHIThreadEnabled(false, false);
}
#endif
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case VK_FORMAT_D24_UNORM_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
bSupportsSubsampled = GetIVulkanDynamicRHI()->RHISupportsEXTFragmentDensityMap2();
}
bool FVulkanCustomPresent::IsUsingCorrectDisplayAdapter() const
{
#if PLATFORM_WINDOWS
const void* AdapterId = nullptr;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&AdapterId)) && AdapterId)
{
return GetIVulkanDynamicRHI()->RHIDoesAdapterMatchDevice(AdapterId);
}
#endif
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FVulkanCustomPresent::GetOvrpInstance() const
{
return GetIVulkanDynamicRHI()->RHIGetVkInstance();
}
void* FVulkanCustomPresent::GetOvrpPhysicalDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkPhysicalDevice();
}
void* FVulkanCustomPresent::GetOvrpDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkDevice();
}
void* FVulkanCustomPresent::GetOvrpCommandQueue() const
{
return GetIVulkanDynamicRHI()->RHIGetGraphicsVkQueue();
}
FTextureRHIRef FVulkanCustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
const VkImageSubresourceRange SubresourceRangeAll = { VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS };
if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_RenderTargetable))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, SubresourceRangeAll);
}
else if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_Foveation))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT, SubresourceRangeAll);
}
switch (InResourceType)
{
case RRT_Texture2D:
return VulkanRHI->RHICreateTexture2DFromResource(InFormat, InSizeX, InSizeY, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_Texture2DArray:
return VulkanRHI->RHICreateTexture2DArrayFromResource(InFormat, InSizeX, InSizeY, 2, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_TextureCube:
return VulkanRHI->RHICreateTextureCubeFromResource(InFormat, InSizeX, false, 1, InNumMips, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
default:
return nullptr;
}
}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
void FVulkanCustomPresent::UseFragmentDensityMapOverShadingRate_RHIThread()
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UseFragmentDensityMapOverShadingRate_RHIThread, FColor::Red);
GRHIVariableRateShadingImageDataType = VRSImage_Fractional;
GRHIVariableRateShadingImageFormat = PF_R8G8;
}
#ifdef WITH_OCULUS_BRANCH
void FVulkanCustomPresent::UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2])
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UpdateFoveationOffsets_RHIThread, FColor::Red);
GetIVulkanDynamicRHI()->RHISetQcomFragmentDensityMapOffsets(bUseOffsets, Offsets);
}
#endif // WITH_OCULUS_BRANCH
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD)
{
return new FVulkanCustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN

View File

@@ -0,0 +1,71 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DeferredDeletionQueue.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMDPrivate.h"
#include "XRThreadUtils.h"
#include "OculusXRHMDModule.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
uint32 GOculusXRHMDLayerDeletionFrameNumber = 0;
const uint32 NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE = 3;
const uint32 NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE = 7;
void FDeferredDeletionQueue::AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr)
{
DeferredDeletionEntry Entry;
Entry.Layer = ptr;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::Layer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID)
{
DeferredDeletionEntry Entry;
Entry.OvrpLayerId = layerID;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately)
{
// Traverse list backwards so the swap switches to elements already tested
for (int32 Index = DeferredDeletionArray.Num() - 1; Index >= 0; --Index)
{
DeferredDeletionEntry* Entry = &DeferredDeletionArray[Index];
if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::Layer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE)
{
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
}
}
else if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE)
{
ExecuteOnRHIThread_DoNotWait([OvrpLayerId = Entry->OvrpLayerId]() {
UE_LOG(LogHMD, Warning, TEXT("Destroying layer %d"), OvrpLayerId);
FOculusXRHMDModule::GetPluginWrapper().DestroyLayer(OvrpLayerId);
});
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
}
}
}
// if the function is to be called multiple times, move this increment somewhere unique!
++GOculusXRHMDLayerDeletionFrameNumber;
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,45 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Layer.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue
{
public:
void AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr);
void AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID);
void HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately = false);
private:
struct DeferredDeletionEntry
{
enum class DeferredDeletionEntryType
{
Layer,
OvrpLayer
};
FLayerPtr Layer;
uint32 OvrpLayerId;
uint32 FrameEnqueued;
DeferredDeletionEntryType EntryType;
};
TArray<DeferredDeletionEntry> DeferredDeletionArray;
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,90 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DynamicResolutionState.h"
#include "LegacyScreenPercentageDriver.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "SceneView.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState implementation
//-------------------------------------------------------------------------------------------------
FDynamicResolutionState::FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings)
: Settings(InSettings)
, ResolutionFraction(-1.0f)
, ResolutionFractionUpperBound(-1.0f)
{
check(Settings.IsValid());
}
void FDynamicResolutionState::ResetHistory(){
// Empty - Oculus drives resolution fraction externally
};
bool FDynamicResolutionState::IsSupported() const
{
return true;
}
void FDynamicResolutionState::SetupMainViewFamily(class FSceneViewFamily& ViewFamily)
{
check(IsInGameThread());
check(ViewFamily.EngineShowFlags.ScreenPercentage == true);
if (IsEnabled())
{
// Compute desired resolution fraction range
float MinResolutionFraction = Settings->PixelDensityMin;
float MaxResolutionFraction = Settings->PixelDensityMax;
// Clamp resolution fraction to what the renderer can do.
MinResolutionFraction = FMath::Max(MinResolutionFraction, ISceneViewFamilyScreenPercentage::kMinResolutionFraction);
MaxResolutionFraction = FMath::Min(MaxResolutionFraction, ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
ResolutionFraction = FMath::Clamp(Settings->PixelDensity, MinResolutionFraction, MaxResolutionFraction);
ResolutionFractionUpperBound = MaxResolutionFraction;
ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(ViewFamily, ResolutionFraction, ResolutionFractionUpperBound));
}
}
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsApproximation() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFraction;
return ResolutionFractions;
}
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsUpperBound() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFractionUpperBound;
return ResolutionFractionUpperBound;
}
void FDynamicResolutionState::SetEnabled(bool bEnable)
{
check(IsInGameThread());
Settings->Flags.bPixelDensityAdaptive = bEnable;
}
bool FDynamicResolutionState::IsEnabled() const
{
check(IsInGameThread());
return Settings->Flags.bPixelDensityAdaptive;
}
void FDynamicResolutionState::ProcessEvent(EDynamicResolutionStateEvent Event){
// Empty - Oculus drives resolution fraction externally
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "DynamicResolutionState.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState
//-------------------------------------------------------------------------------------------------
class FDynamicResolutionState : public IDynamicResolutionState
{
public:
FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings);
// ISceneViewFamilyScreenPercentage
virtual void ResetHistory() override;
virtual bool IsSupported() const override;
virtual void SetupMainViewFamily(class FSceneViewFamily& ViewFamily) override;
protected:
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsApproximation() const override;
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsUpperBound() const override;
virtual void SetEnabled(bool bEnable) override;
virtual bool IsEnabled() const override;
virtual void ProcessEvent(EDynamicResolutionStateEvent Event) override;
private:
const OculusXRHMD::FSettingsPtr Settings;
float ResolutionFraction;
float ResolutionFractionUpperBound;
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_FoveatedRendering.h"
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
#include "RenderGraphBuilder.h"
#include "HeadMountedDisplayTypes.h" // For the LogHMD log category
FOculusXRFoveatedRenderingImageGenerator::FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain)
: FoveationSwapchain(Swapchain)
{
GVRSImageManager.RegisterExternalImageGenerator(this);
}
FOculusXRFoveatedRenderingImageGenerator::~FOculusXRFoveatedRenderingImageGenerator()
{
GVRSImageManager.UnregisterExternalImageGenerator(this);
}
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType)
{
if (!FoveationSwapchain.IsValid())
{
return nullptr;
}
FTexture2DRHIRef SwapchainTexture = FoveationSwapchain->GetTexture2DArray() ? FoveationSwapchain->GetTexture2DArray() : FoveationSwapchain->GetTexture2D();
FIntPoint TexSize = SwapchainTexture->GetSizeXY();
// Only set texture and return true if we have a valid texture of compatible size
if (SwapchainTexture->IsValid() && TexSize.X > 0 && TexSize.Y > 0)
{
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget = CreateRenderTarget(SwapchainTexture, *SwapchainTexture->GetName().ToString());
return GraphBuilder.RegisterExternalTexture(PooledRenderTarget, *SwapchainTexture->GetName().ToString(), ERDGTextureFlags::SkipTracking);
}
return nullptr;
}
void FOculusXRFoveatedRenderingImageGenerator::PrepareImages(FRDGBuilder& GraphBuilder, const FSceneViewFamily& ViewFamily, const FMinimalSceneTextures& SceneTextures)
{
return;
}
bool FOculusXRFoveatedRenderingImageGenerator::IsEnabledForView(const FSceneView& View) const
{
return View.StereoPass != EStereoscopicPass::eSSP_FULL;
}
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetDebugImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType)
{
return nullptr;
}
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)

View File

@@ -0,0 +1,31 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
#include "VariableRateShadingImageManager.h"
#include "XRSwapchain.h"
class FOculusXRFoveatedRenderingImageGenerator : public IVariableRateShadingImageGenerator
{
public:
FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain);
virtual ~FOculusXRFoveatedRenderingImageGenerator() override;
// IVariableRateShadingImageGenerator interface
virtual FRDGTextureRef GetImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType) override;
virtual void PrepareImages(FRDGBuilder& GraphBuilder, const FSceneViewFamily& ViewFamily, const FMinimalSceneTextures& SceneTextures) override;
virtual bool IsEnabledForView(const FSceneView& View) const override;
virtual FRDGTextureRef GetDebugImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType) override;
virtual FVariableRateShadingImageManager::EVRSSourceType GetType() const override
{
return FVariableRateShadingImageManager::EVRSSourceType::FixedFoveation;
}
private:
const FXRSwapChainPtr& FoveationSwapchain;
};
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)

View File

@@ -0,0 +1,33 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_GameFrame.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "GameFramework/WorldSettings.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
FGameFrame::FGameFrame()
: FrameNumber(0), WorldToMetersScale(100.f), ShowFlags(ESFIM_All0), PlayerOrientation(FQuat::Identity), PlayerLocation(FVector::ZeroVector), FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering), FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off), bDynamicFoveatedRendering(false)
{
Flags.Raw = 0;
Fov[0] = Fov[1] = SymmetricFov[0] = SymmetricFov[1] = ovrpFovf{ 0, 0, 0, 0 };
}
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFrame::Clone() const
{
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> NewFrame = MakeShareable(new FGameFrame(*this));
return NewFrame;
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,65 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "ShowFlags.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
class FGameFrame : public TSharedFromThis<FGameFrame, ESPMode::ThreadSafe>
{
public:
uint32 FrameNumber; // current frame number. (StartGameFrame_GameThread)
float WorldToMetersScale; // World units (UU) to Meters scale. (OnStartGameFrame)
FIntPoint WindowSize; // actual window size (StartGameFrame_GameThread)
FEngineShowFlags ShowFlags; // (PreRenderViewFamily_RenderThread)
FQuat HeadOrientation; // (CalculateStereoViewOffset)
FQuat PlayerOrientation; // (CalculateStereoViewOffset)
FVector PlayerLocation; // (CalculateStereoViewOffset)
float NearClippingPlane; // (GetStereoProjectionMatrix)
FTransform TrackingToWorld; // (OnEndGameFrame)
FTransform LastTrackingToWorld; // (OnEndGameFrame)
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod; // OnStartGameFrame
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel; // OnStartGameFrame
bool bDynamicFoveatedRendering; // OnStartGameFrame
ovrpFovf Fov[ovrpEye_Count]; // UpdateStereoRenderingParams
ovrpFovf SymmetricFov[ovrpEye_Count]; // UpdateStereoRenderingParams, symmetric FOV if frame is using symmetricFOV.
union
{
struct
{
/** True, if splash is shown */
uint64 bSplashIsShown : 1;
/** True, if spectator screen is active */
uint64 bSpectatorScreenActive : 1;
/** True if the frame's positions have been updated on the render thread */
uint64 bRTLateUpdateDone : 1;
};
uint64 Raw;
} Flags;
public:
FGameFrame();
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFramePtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "ProceduralMeshComponent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_CustomPresent.h"
#include "XRSwapChain.h"
#include "OculusXRPassthroughLayerShapes.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FOvrpLayer
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue;
class FOvrpLayer : public TSharedFromThis<FOvrpLayer, ESPMode::ThreadSafe>
{
public:
FOvrpLayer(uint32 InOvrpLayerId, FDeferredDeletionQueue* InDeferredDeletion);
~FOvrpLayer();
protected:
uint32 OvrpLayerId;
private:
FDeferredDeletionQueue* DeferredDeletion; // necessary for deferred deletion queue of the actual OvrpLayer
};
typedef TSharedPtr<FOvrpLayer, ESPMode::ThreadSafe> FOvrpLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayer
//-------------------------------------------------------------------------------------------------
class FLayer : public TSharedFromThis<FLayer, ESPMode::ThreadSafe>
{
public:
FLayer(uint32 InId);
FLayer(const FLayer& InLayer);
~FLayer();
uint32 GetId() const { return Id; }
int GetOvrpId() const { return OvrpLayerId; }
void SetDesc(const IStereoLayers::FLayerDesc& InDesc);
void SetDesc(const FSettings* Settings, const IStereoLayers::FLayerDesc& InDesc);
const IStereoLayers::FLayerDesc& GetDesc() const { return Desc; }
void SetEyeLayerDesc(const ovrpLayerDesc_EyeFov& InEyeLayerDesc);
const FXRSwapChainPtr& GetSwapChain() const { return SwapChain; }
const FXRSwapChainPtr& GetRightSwapChain() const { return RightSwapChain; }
const FXRSwapChainPtr& GetDepthSwapChain() const { return DepthSwapChain; }
const FXRSwapChainPtr& GetFoveationSwapChain() const { return FoveationSwapChain; }
const FXRSwapChainPtr& GetMotionVectorSwapChain() const { return MotionVectorSwapChain; }
const FXRSwapChainPtr& GetMotionVectorDepthSwapChain() const { return MotionVectorDepthSwapChain; }
void MarkTextureForUpdate() { bUpdateTexture = true; }
bool NeedsPokeAHole();
void HandlePokeAHoleComponent();
void BuildPokeAHoleMesh(TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector2D>& UV0);
bool NeedsPassthroughPokeAHole();
bool ShapeNeedsTextures(ovrpShape shape);
FTextureRHIRef GetTexture() { return Desc.Texture; }
TSharedPtr<FLayer, ESPMode::ThreadSafe> Clone() const;
bool CanReuseResources(const FLayer* InLayer) const;
bool Initialize_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FDeferredDeletionQueue* DeferredDeletion, FRHICommandListImmediate& RHICmdList, const FLayer* InLayer = nullptr);
void UpdateTexture_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList);
void UpdatePassthrough_RenderThread(FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList, const FGameFrame* Frame);
const ovrpLayerSubmit* UpdateLayer_RHIThread(const FSettings* Settings, const FGameFrame* Frame, const int LayerIndex);
void IncrementSwapChainIndex_RHIThread(FCustomPresent* CustomPresent);
void ReleaseResources_RHIThread();
bool IsVisible() { return (Desc.Flags & IStereoLayers::LAYER_FLAG_HIDDEN) == 0; }
bool bNeedsTexSrgbCreate;
void AddPassthroughMesh_RenderThread(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, FMatrix Transformation, uint64_t& OutMeshHandle, uint64_t& OutInstanceHandle);
void UpdatePassthroughMeshTransform_RenderThread(uint64_t InstanceHandle, FMatrix Transformation);
void RemovePassthroughMesh_RenderThread(uint64_t MeshHandle, uint64_t InstanceHandle);
void DestroyLayer();
protected:
struct FPassthroughMesh
{
FPassthroughMesh(uint64_t MeshHandle, uint64_t InstanceHandle)
: MeshHandle(MeshHandle)
, InstanceHandle(InstanceHandle)
{
}
uint64_t MeshHandle;
uint64_t InstanceHandle;
};
typedef TSharedPtr<TMap<FString, FPassthroughMesh>, ESPMode::ThreadSafe> FUserDefinedGeometryMapPtr;
void UpdatePassthroughStyle_RenderThread(const FEdgeStyleParameters& EdgeStyleParameters);
struct FPassthroughPokeActor
{
FPassthroughPokeActor(){};
FPassthroughPokeActor(UProceduralMeshComponent* PokeAHoleComponentPtr, AActor* PokeAHoleActor)
: PokeAHoleComponentPtr(PokeAHoleComponentPtr)
, PokeAHoleActor(PokeAHoleActor){};
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
};
typedef TSharedPtr<TMap<FString, FPassthroughPokeActor>, ESPMode::ThreadSafe> FPassthroughPokeActorMapPtr;
bool BuildPassthroughPokeActor(FOculusPassthroughMeshRef PassthroughMesh, FPassthroughPokeActor& OutPassthroughPokeActor);
void UpdatePassthroughPokeActors_GameThread();
uint32 Id;
IStereoLayers::FLayerDesc Desc;
int OvrpLayerId;
ovrpLayerDescUnion OvrpLayerDesc;
ovrpLayerSubmitUnion OvrpLayerSubmit;
FOvrpLayerPtr OvrpLayer;
FXRSwapChainPtr SwapChain;
FXRSwapChainPtr DepthSwapChain;
FXRSwapChainPtr FoveationSwapChain;
FXRSwapChainPtr RightSwapChain;
FXRSwapChainPtr RightDepthSwapChain;
FXRSwapChainPtr MotionVectorSwapChain;
FXRSwapChainPtr MotionVectorDepthSwapChain;
FTextureRHIRef InvAlphaTexture;
bool bUpdateTexture;
bool bInvertY;
bool bHasDepth;
bool bSupportDepthComposite;
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
FUserDefinedGeometryMapPtr UserDefinedGeometryMap;
FPassthroughPokeActorMapPtr PassthroughPokeActorMap;
};
typedef TSharedPtr<FLayer, ESPMode::ThreadSafe> FLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayerPtr_CompareId
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_CompareId
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
return A->GetId() < B->GetId();
}
};
//-------------------------------------------------------------------------------------------------
// FLayerPtr_ComparePriority
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_ComparePriority
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
if (A->GetDesc().Priority < B->GetDesc().Priority)
return true;
if (A->GetDesc().Priority > B->GetDesc().Priority)
return false;
return A->GetId() < B->GetId();
}
};
struct FLayerPtr_CompareTotal
{
FORCEINLINE int32 GetLayerTypePriority(const FLayerPtr& A) const
{
// Draw FReconstructedLayer, PoleAHole layers (Android only), EyeFov layer, followed by other layers
const bool IsEyeFov = (A->GetId() == 0);
const bool IsPokeAHole = A->NeedsPokeAHole() || A->NeedsPassthroughPokeAHole();
bool IsUnderlay = false;
if (A->GetDesc().HasShape<FReconstructedLayer>())
{
const FReconstructedLayer& ReconstructedLayerProps = A->GetDesc().GetShape<FReconstructedLayer>();
IsUnderlay = (ReconstructedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
else if (A->GetDesc().HasShape<FUserDefinedLayer>())
{
const FUserDefinedLayer& UserDefinedLayerProps = A->GetDesc().GetShape<FUserDefinedLayer>();
IsUnderlay = (UserDefinedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
const int32 Priority = IsUnderlay ? -2 : IsPokeAHole ? -1
: IsEyeFov ? 0
: 1;
return Priority;
}
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
// First order layers by type
int32 PassA = GetLayerTypePriority(A);
int32 PassB = GetLayerTypePriority(B);
if (PassA != PassB)
return PassA < PassB;
// Draw non-FaceLocked layers first
const IStereoLayers::FLayerDesc& DescA = A->GetDesc();
const IStereoLayers::FLayerDesc& DescB = B->GetDesc();
bool bFaceLockedA = (DescA.PositionType == IStereoLayers::ELayerType::FaceLocked);
bool bFaceLockedB = (DescB.PositionType == IStereoLayers::ELayerType::FaceLocked);
if (bFaceLockedA != bFaceLockedB)
return !bFaceLockedA;
// Draw layers by ascending priority
if (DescA.Priority != DescB.Priority)
return DescA.Priority < DescB.Priority;
// Draw layers by ascending id
return A->GetId() < B->GetId();
}
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,135 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Settings.h"
#include "Engine/Engine.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
FSettings::FSettings()
: BaseOffset(0, 0, 0)
, BaseOrientation(FQuat::Identity)
, PixelDensity(1.0f)
, PixelDensityMin(0.8f)
, PixelDensityMax(1.2f)
, SystemHeadset(ovrpSystemHeadset_None)
, SuggestedCpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedLow)
, SuggestedGpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedHigh)
, FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering)
, FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off)
, bDynamicFoveatedRendering(true)
, bSupportEyeTrackedFoveatedRendering(false)
, SystemSplashBackground(ESystemSplashBackgroundType::Black)
, XrApi(EOculusXRXrApi::OVRPluginOpenXR)
, ColorSpace(EOculusXRColorSpace::P3)
, ControllerPoseAlignment(EOculusXRControllerPoseAlignment::Default)
, HandTrackingSupport(EOculusXRHandTrackingSupport::ControllersOnly)
, HandTrackingFrequency(EOculusXRHandTrackingFrequency::LOW)
, HandTrackingVersion(EOculusXRHandTrackingVersion::Default)
, ColorScale(ovrpVector4f{ 1, 1, 1, 1 })
, ColorOffset(ovrpVector4f{ 0, 0, 0, 0 })
, bApplyColorScaleAndOffsetToAllLayers(false)
, CurrentFeatureLevel(GMaxRHIFeatureLevel)
, bLateLatching(false)
, bSupportExperimentalFeatures(false)
, ProcessorFavor(EProcessorFavor::FavorEqually)
, BodyTrackingFidelity(EOculusXRHMDBodyTrackingFidelity::Low)
, BodyTrackingJointSet(EOculusXRHMDBodyJointSet::UpperBody)
{
Flags.Raw = 0;
Flags.bHMDEnabled = true;
Flags.bUpdateOnRT = true;
Flags.bHQBuffer = false;
Flags.bCompositeDepth = true;
#if PLATFORM_ANDROID
Flags.bsRGBEyeBuffer = true;
//oculus mobile is always-on stereo, no need for enableStereo codepaths
Flags.bStereoEnabled = true;
#else
Flags.bsRGBEyeBuffer = false;
Flags.bStereoEnabled = false;
#endif
CurrentFeatureLevel = GEngine ? GEngine->GetDefaultWorldFeatureLevel() : GMaxRHIFeatureLevel;
CurrentShaderPlatform = GShaderPlatformForFeatureLevel[CurrentFeatureLevel];
Flags.bSupportsDash = true;
Flags.bFocusAware = true;
Flags.bRequiresSystemKeyboard = false;
Flags.bInsightPassthroughEnabled = false;
Flags.bAnchorSupportEnabled = false;
Flags.bAnchorSharingEnabled = false;
Flags.bSceneSupportEnabled = false;
Flags.bBodyTrackingEnabled = false;
Flags.bEyeTrackingEnabled = false;
Flags.bFaceTrackingEnabled = false;
EyeRenderViewport[0] = EyeRenderViewport[1] = FIntRect(0, 0, 0, 0);
RenderTargetSize = FIntPoint(0, 0);
#ifdef WITH_OCULUS_BRANCH
Flags.bTileTurnOffEnabled = false;
#else
Flags.bTileTurnOffEnabled = true;
#endif
}
TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettings::Clone() const
{
TSharedPtr<FSettings, ESPMode::ThreadSafe> NewSettings = MakeShareable(new FSettings(*this));
return NewSettings;
}
void FSettings::SetPixelDensity(float NewPixelDensity)
{
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewPixelDensity, PixelDensityMin, PixelDensityMax);
}
else
{
PixelDensity = FMath::Clamp(NewPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
void FSettings::SetPixelDensitySmooth(float NewPixelDensity)
{
// Pixel Density changes need to be smooth both for artifacts with FFR/TTO (FFR/tile-turnoff is one frame late so shouldn't change too fast)
// but also so that if the developer uses the CVar and not the runtime (which is already smooth) there is no jump artifacts.
constexpr float MaxPerFrameIncrease = 0.010;
constexpr float MaxPerFrameDecrease = 0.045;
float NewClampedPixelDensity = FMath::Clamp(NewPixelDensity, PixelDensity - MaxPerFrameDecrease, PixelDensity + MaxPerFrameIncrease);
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, PixelDensityMin, PixelDensityMax);
}
else
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
void FSettings::SetPixelDensityMin(float NewPixelDensityMin)
{
PixelDensityMin = FMath::Clamp(NewPixelDensityMin, ClampPixelDensityMin, ClampPixelDensityMax);
PixelDensityMax = FMath::Max(PixelDensityMin, PixelDensityMax);
SetPixelDensity(PixelDensity);
}
void FSettings::SetPixelDensityMax(float NewPixelDensityMax)
{
PixelDensityMax = FMath::Clamp(NewPixelDensityMax, ClampPixelDensityMin, ClampPixelDensityMax);
PixelDensityMin = FMath::Min(PixelDensityMin, PixelDensityMax);
SetPixelDensity(PixelDensity);
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,174 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
static const float ClampPixelDensityMin = 0.5f;
static const float ClampPixelDensityMax = 2.0f;
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
class FSettings : public TSharedFromThis<FSettings, ESPMode::ThreadSafe>
{
public:
union
{
struct
{
/** Whether stereo is currently on or off. */
uint64 bStereoEnabled : 1;
/** Whether or not switching to stereo is allowed */
uint64 bHMDEnabled : 1;
/** Turns on/off updating view's orientation/position on a RenderThread. When it is on,
latency should be significantly lower.
See 'HMD UPDATEONRT ON|OFF' console command.
*/
uint64 bUpdateOnRT : 1;
/** Enforces headtracking to work even in non-stereo mode (for debugging or screenshots).
See 'MOTION ENFORCE' console command. */
uint64 bHeadTrackingEnforced : 1;
/** Allocate an high quality OVR_FORMAT_R11G11B10_FLOAT buffer for Rift */
uint64 bHQBuffer : 1;
/** Rendering should be (could be) paused */
uint64 bPauseRendering : 1;
/** HQ Distortion */
uint64 bHQDistortion : 1;
/** Send the depth buffer to the compositor */
uint64 bCompositeDepth : 1;
/** Supports Dash in-game compositing */
uint64 bSupportsDash : 1;
#if !UE_BUILD_SHIPPING
/** Show status / statistics on screen. See 'hmd stats' cmd */
uint64 bShowStats : 1;
#endif
/** Dynamically update pixel density to maintain framerate */
uint64 bPixelDensityAdaptive : 1;
/** All future eye buffers will need to be created with TexSRGB_Create flag due to the current feature level (ES31) */
uint64 bsRGBEyeBuffer : 1;
/** Supports Focus Aware state on Quest */
uint64 bFocusAware : 1;
/** Requires the Oculus system keyboard */
uint64 bRequiresSystemKeyboard : 1;
/** Whether passthrough functionality can be used with the app */
uint64 bInsightPassthroughEnabled : 1;
/** Whether Anchors can be used with the app */
uint64 bAnchorSupportEnabled : 1;
/** Whether Anchor Sharing can be used with the app */
uint64 bAnchorSharingEnabled : 1;
/** Whether Scene can be used with the app */
uint64 bSceneSupportEnabled : 1;
/** Whether body tracking functionality can be used with the app */
uint64 bBodyTrackingEnabled : 1;
/** Whether eye tracking functionality can be used with the app */
uint64 bEyeTrackingEnabled : 1;
/** Whether face tracking functionality can be used with the app */
uint64 bFaceTrackingEnabled : 1;
/** Whether tile turn off can be used with the app */
uint64 bTileTurnOffEnabled : 1;
};
uint64 Raw;
} Flags;
/** HMD base values, specify forward orientation and zero pos offset */
FVector BaseOffset; // base position, in meters, relatively to the sensor //@todo hmd: clients need to stop using oculus space
FQuat BaseOrientation; // base orientation
/** Viewports for each eye, in render target texture coordinates */
FIntRect EyeRenderViewport[ovrpEye_Count];
/** Viewports for each eye, without DynamicResolution scaling applied */
FIntRect EyeUnscaledRenderViewport[ovrpEye_Count];
ovrpMatrix4f EyeProjectionMatrices[ovrpEye_Count]; // 0 - left, 1 - right same as Views
ovrpMatrix4f MonoProjectionMatrix;
FIntPoint RenderTargetSize;
float PixelDensity;
float PixelDensityMin;
float PixelDensityMax;
ovrpSystemHeadset SystemHeadset;
float VsyncToNextVsync;
EOculusXRProcessorPerformanceLevel SuggestedCpuPerfLevel;
EOculusXRProcessorPerformanceLevel SuggestedGpuPerfLevel;
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod;
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel;
bool bDynamicFoveatedRendering;
bool bSupportEyeTrackedFoveatedRendering;
ESystemSplashBackgroundType SystemSplashBackground;
EOculusXRXrApi XrApi;
EOculusXRColorSpace ColorSpace;
EOculusXRControllerPoseAlignment ControllerPoseAlignment;
EOculusXRHandTrackingSupport HandTrackingSupport;
EOculusXRHandTrackingFrequency HandTrackingFrequency;
EOculusXRHandTrackingVersion HandTrackingVersion;
ovrpVector4f ColorScale, ColorOffset;
bool bApplyColorScaleAndOffsetToAllLayers;
FStaticFeatureLevel CurrentFeatureLevel;
EShaderPlatform CurrentShaderPlatform;
bool bLateLatching;
bool bSupportExperimentalFeatures;
EProcessorFavor ProcessorFavor;
EOculusXRHMDBodyTrackingFidelity BodyTrackingFidelity;
EOculusXRHMDBodyJointSet BodyTrackingJointSet;
TSet<EFaceTrackingDataSourceConfig> FaceTrackingDataSource;
public:
FSettings();
virtual ~FSettings() {}
bool IsStereoEnabled() const { return Flags.bStereoEnabled && Flags.bHMDEnabled; }
void SetPixelDensity(float NewPixelDensity);
void SetPixelDensitySmooth(float NewPixelDensity);
void SetPixelDensityMin(float NewPixelDensityMin);
void SetPixelDensityMax(float NewPixelDensityMax);
TSharedPtr<FSettings, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettingsPtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,121 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_SpectatorScreenController.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "TextureResource.h"
#include "Engine/TextureRenderTarget2D.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
FSpectatorScreenController::FSpectatorScreenController(FOculusXRHMD* InOculusXRHMD)
: FDefaultSpectatorScreenController(InOculusXRHMD)
, OculusXRHMD(InOculusXRHMD)
, SpectatorMode(EMRSpectatorScreenMode::Default)
, ForegroundRenderTexture(nullptr)
, BackgroundRenderTexture(nullptr)
{
}
void FSpectatorScreenController::RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* BackBuffer, FTexture2DRHIRef RenderTexture, FVector2D WindowSize)
{
CheckInRenderThread();
if (OculusXRHMD->GetCustomPresent_Internal())
{
if (SpectatorMode == EMRSpectatorScreenMode::ExternalComposition)
{
auto ForegroundResource = ForegroundRenderTexture->GetRenderTargetResource();
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (ForegroundResource && BackgroundResource)
{
RenderSpectatorModeExternalComposition(
RHICmdList,
FTexture2DRHIRef(BackBuffer),
ForegroundResource->GetRenderTargetTexture(),
BackgroundResource->GetRenderTargetTexture());
return;
}
}
else if (SpectatorMode == EMRSpectatorScreenMode::DirectComposition)
{
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (BackgroundResource)
{
RenderSpectatorModeDirectComposition(
RHICmdList,
FTexture2DRHIRef(BackBuffer),
BackgroundRenderTexture->GetRenderTargetResource()->GetRenderTargetTexture());
return;
}
}
FDefaultSpectatorScreenController::RenderSpectatorScreen_RenderThread(RHICmdList, BackBuffer, RenderTexture, WindowSize);
}
}
void FSpectatorScreenController::RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
FIntRect DestRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
for (int i = 0; i < 2; ++i)
{
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, Settings->EyeRenderViewport[i], TargetTexture, DestRect, false, true);
DestRect.Min.X += TargetTexture->GetSizeX() / 2;
DestRect.Max.X += TargetTexture->GetSizeX() / 2;
}
}
void FSpectatorScreenController::RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FCustomPresent* CustomPresent = OculusXRHMD->GetCustomPresent_Internal();
FTexture2DRHIRef MirrorTexture = CustomPresent->GetMirrorTexture();
if (MirrorTexture)
{
FIntRect SrcRect(0, 0, MirrorTexture->GetSizeX(), MirrorTexture->GetSizeY());
FIntRect DestRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, MirrorTexture, SrcRect, TargetTexture, DestRect, false, true);
}
}
void FSpectatorScreenController::RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
const FIntRect SrcRect = Settings->EyeRenderViewport[0];
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef SrcTexture) const
{
CheckInRenderThread();
const FIntRect SrcRect(0, 0, SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, SrcTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef FrontTexture, const FTexture2DRHIRef BackTexture) const
{
CheckInRenderThread();
const FIntRect FrontSrcRect(0, 0, FrontTexture->GetSizeX(), FrontTexture->GetSizeY());
const FIntRect FrontDstRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
const FIntRect BackSrcRect(0, 0, BackTexture->GetSizeX(), BackTexture->GetSizeY());
const FIntRect BackDstRect(TargetTexture->GetSizeX() / 2, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, FrontTexture, FrontSrcRect, TargetTexture, FrontDstRect, false, true);
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, BackTexture, BackSrcRect, TargetTexture, BackDstRect, false, true);
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "DefaultSpectatorScreenController.h"
class UTextureRenderTarget2D;
namespace OculusXRHMD
{
// Oculus specific spectator screen modes that override the regular VR spectator screens
enum class EMRSpectatorScreenMode : uint8
{
Default,
ExternalComposition,
DirectComposition
};
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
class FSpectatorScreenController : public FDefaultSpectatorScreenController
{
public:
FSpectatorScreenController(class FOculusXRHMD* InOculusXRHMD);
void SetMRSpectatorScreenMode(EMRSpectatorScreenMode Mode) { SpectatorMode = Mode; }
void SetMRForeground(UTextureRenderTarget2D* Texture) { ForegroundRenderTexture = Texture; }
void SetMRBackground(UTextureRenderTarget2D* Texture) { BackgroundRenderTexture = Texture; }
virtual void RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* BackBuffer, FTexture2DRHIRef RenderTarget, FVector2D WindowSize) override;
virtual void RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
private:
FOculusXRHMD* OculusXRHMD;
EMRSpectatorScreenMode SpectatorMode;
UTextureRenderTarget2D* ForegroundRenderTexture;
UTextureRenderTarget2D* BackgroundRenderTexture;
void RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef SrcTexture) const;
void RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef FrontTexture, const FTexture2DRHIRef BackTexture) const;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,666 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Splash.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "RenderingThread.h"
#include "Misc/ScopeLock.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "StereoLayerFunctionLibrary.h"
#include "TextureResource.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidEGL.h"
#include "Android/AndroidApplication.h"
#include "OculusXRHMDTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
FSplash::FSplash(FOculusXRHMD* InOculusXRHMD)
: OculusXRHMD(InOculusXRHMD), CustomPresent(InOculusXRHMD->GetCustomPresent_Internal()), FramesOutstanding(0), NextLayerId(1), bInitialized(false), bIsShown(false), bNeedSplashUpdate(false), bShouldShowSplash(false), SystemDisplayInterval(1 / 90.0f)
{
// Create empty quad layer for UE layer
{
IStereoLayers::FLayerDesc LayerDesc;
LayerDesc.QuadSize = FVector2D(0.01f, 0.01f);
LayerDesc.Priority = 0;
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = nullptr;
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(LayerDesc);
}
}
FSplash::~FSplash()
{
// Make sure RenTicker is freed in Shutdown
check(!Ticker.IsValid())
}
void FSplash::Tick_RenderThread(float DeltaTime)
{
CheckInRenderThread();
if (FramesOutstanding > 0)
{
UE_LOG(LogHMD, VeryVerbose, TEXT("Splash skipping frame; too many frames outstanding"));
return;
}
const double TimeInSeconds = FPlatformTime::Seconds();
const double DeltaTimeInSeconds = TimeInSeconds - LastTimeInSeconds;
if (DeltaTimeInSeconds > 2.f * SystemDisplayInterval && Layers_RenderThread_DeltaRotation.Num() > 0)
{
FScopeLock ScopeLock(&RenderThreadLock);
for (TTuple<FLayerPtr, FQuat>& Info : Layers_RenderThread_DeltaRotation)
{
FLayerPtr Layer = Info.Key;
const FQuat& DeltaRotation = Info.Value;
check(Layer.IsValid());
check(!DeltaRotation.Equals(FQuat::Identity)); // Only layers with non-zero delta rotation should be in the DeltaRotation array.
IStereoLayers::FLayerDesc LayerDesc = Layer->GetDesc();
LayerDesc.Transform.SetRotation(LayerDesc.Transform.GetRotation() * DeltaRotation);
LayerDesc.Transform.NormalizeRotation();
Layer->SetDesc(LayerDesc);
}
LastTimeInSeconds = TimeInSeconds;
}
RenderFrame_RenderThread(FRHICommandListExecutor::GetImmediateCommandList());
}
void FSplash::LoadSettings()
{
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
ClearSplashes();
for (const FOculusXRSplashDesc& SplashDesc : HMDSettings->SplashDescs)
{
AddSplash(SplashDesc);
}
if (HMDSettings->bAutoEnabled)
{
if (!PreLoadLevelDelegate.IsValid())
{
PreLoadLevelDelegate = FCoreUObjectDelegates::PreLoadMap.AddSP(this, &FSplash::OnPreLoadMap);
}
if (!PostLoadLevelDelegate.IsValid())
{
PostLoadLevelDelegate = FCoreUObjectDelegates::PostLoadMapWithWorld.AddSP(this, &FSplash::OnPostLoadMap);
}
}
else
{
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
}
}
void FSplash::OnPreLoadMap(const FString&)
{
DoShow();
}
void FSplash::OnPostLoadMap(UWorld* LoadedWorld)
{
// Don't auto-hide splash if show loading screen is called explicitly
if (!bShouldShowSplash)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::OnPostLoadMap Hide Auto Splash"));
HideLoadingScreen();
}
}
#if WITH_EDITOR
void FSplash::OnPieBegin(bool bIsSimulating)
{
LoadSettings();
}
#endif
void FSplash::Startup()
{
CheckInGameThread();
if (!bInitialized)
{
Settings = OculusXRHMD->CreateNewSettings();
Frame = OculusXRHMD->CreateNewGameFrame();
// keep units in meters rather than UU (because UU make not much sense).
Frame->WorldToMetersScale = 1.0f;
float SystemDisplayFrequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&SystemDisplayFrequency)))
{
SystemDisplayInterval = 1.0f / SystemDisplayFrequency;
}
LoadSettings();
OculusXRHMD->InitDevice();
#if WITH_EDITOR
PieBeginDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FSplash::OnPieBegin);
#else
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
if (HMDSettings->bAutoEnabled)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::Startup Show Splash on Startup"));
DoShow();
}
#endif
OculusXRHMD->Settings_RenderThread = OculusXRHMD->Settings->Clone();
bInitialized = true;
}
}
void FSplash::StopTicker()
{
CheckInGameThread();
if (!bIsShown)
{
ExecuteOnRenderThread([this]() {
if (Ticker.IsValid())
{
Ticker->Unregister();
Ticker = nullptr;
}
});
UnloadTextures();
}
}
void FSplash::StartTicker()
{
CheckInGameThread();
if (!Ticker.IsValid())
{
Ticker = MakeShareable(new FTicker(this));
ExecuteOnRenderThread([this]() {
LastTimeInSeconds = FPlatformTime::Seconds();
Ticker->Register();
});
}
}
void FSplash::RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
FScopeLock ScopeLock(&RenderThreadLock);
// RenderFrame
FSettingsPtr XSettings = Settings->Clone();
FGameFramePtr XFrame = Frame->Clone();
XFrame->FrameNumber = OculusXRHMD->NextFrameNumber;
XFrame->ShowFlags.Rendering = true;
TArray<FLayerPtr> XLayers = Layers_RenderThread_Input;
ensure(XLayers.Num() != 0);
ovrpResult Result;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OculusXRHMD->WaitFrameNumber != XFrame->FrameNumber)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame(XFrame->FrameNumber)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u failed (%d)"), XFrame->FrameNumber, Result);
XFrame->ShowFlags.Rendering = false;
}
else
{
OculusXRHMD->WaitFrameNumber = XFrame->FrameNumber;
OculusXRHMD->NextFrameNumber = XFrame->FrameNumber + 1;
FPlatformAtomics::InterlockedIncrement(&FramesOutstanding);
}
}
else
{
XFrame->ShowFlags.Rendering = false;
}
if (XFrame->ShowFlags.Rendering)
{
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().Update3(ovrpStep_Render, XFrame->FrameNumber, 0.0)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().Update3 %u failed (%d)"), XFrame->FrameNumber, Result);
}
}
{
int32 LayerIndex = 0;
int32 LayerIndex_RenderThread = 0;
while (LayerIndex < XLayers.Num() && LayerIndex_RenderThread < Layers_RenderThread.Num())
{
uint32 LayerIdA = XLayers[LayerIndex]->GetId();
uint32 LayerIdB = Layers_RenderThread[LayerIndex_RenderThread]->GetId();
if (LayerIdA < LayerIdB)
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
else if (LayerIdA > LayerIdB)
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
else
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList, Layers_RenderThread[LayerIndex_RenderThread++].Get());
}
}
while (LayerIndex < XLayers.Num())
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
while (LayerIndex_RenderThread < Layers_RenderThread.Num())
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
}
Layers_RenderThread = XLayers;
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->UpdateTexture_RenderThread(XSettings.Get(), CustomPresent, RHICmdList);
}
// This submit is required since splash happens before the game is rendering, so layers won't be submitted with game render commands
CustomPresent->SubmitGPUCommands_RenderThread(RHICmdList);
// RHIFrame
for (int32 LayerIndex = 0; LayerIndex < XLayers.Num(); LayerIndex++)
{
XLayers[LayerIndex] = XLayers[LayerIndex]->Clone();
}
ExecuteOnRHIThread_DoNotWait([this, XSettings, XFrame, XLayers]() {
ovrpResult ResultT;
if (XFrame->ShowFlags.Rendering)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().BeginFrame4(XFrame->FrameNumber, CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
XFrame->ShowFlags.Rendering = false;
}
}
FPlatformAtomics::InterlockedDecrement(&FramesOutstanding);
Layers_RHIThread = XLayers;
Layers_RHIThread.Sort(FLayerPtr_ComparePriority());
if (XFrame->ShowFlags.Rendering)
{
TArray<const ovrpLayerSubmit*> LayerSubmitPtr;
LayerSubmitPtr.SetNum(Layers_RHIThread.Num());
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
LayerSubmitPtr[LayerIndex] = Layers_RHIThread[LayerIndex]->UpdateLayer_RHIThread(XSettings.Get(), XFrame.Get(), LayerIndex);
}
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().EndFrame4(XFrame->FrameNumber, LayerSubmitPtr.GetData(), LayerSubmitPtr.Num(), CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
}
else
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->IncrementSwapChainIndex_RHIThread(CustomPresent);
}
}
}
});
}
void FSplash::ReleaseResources_RHIThread()
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->ReleaseResources_RHIThread();
}
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->ReleaseResources_RHIThread();
}
Layers_RenderThread.Reset();
Layers_RHIThread.Reset();
}
void FSplash::PreShutdown()
{
CheckInGameThread();
}
void FSplash::Shutdown()
{
CheckInGameThread();
#if WITH_EDITOR
if (PieBeginDelegateHandle.IsValid())
{
FEditorDelegates::BeginPIE.Remove(PieBeginDelegateHandle);
PieBeginDelegateHandle.Reset();
}
#endif
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
if (bInitialized)
{
ExecuteOnRenderThread([this]() {
if (Ticker)
{
Ticker->Unregister();
Ticker = nullptr;
}
ExecuteOnRHIThread([this]() {
SplashLayers.Reset();
Layers_RenderThread.Reset();
Layers_RenderThread_Input.Reset();
Layers_RHIThread.Reset();
});
});
bInitialized = false;
}
}
int FSplash::AddSplash(const FOculusXRSplashDesc& Desc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
return SplashLayers.Add(FSplashLayer(Desc));
}
void FSplash::AddSplash(const FSplashDesc& Splash)
{
FOculusXRSplashDesc OculusDesc;
OculusDesc.TransformInMeters = Splash.Transform;
OculusDesc.QuadSizeInMeters = Splash.QuadSize;
OculusDesc.DeltaRotation = Splash.DeltaRotation;
OculusDesc.bNoAlphaChannel = Splash.bIgnoreAlpha;
OculusDesc.bIsDynamic = Splash.bIsDynamic || Splash.bIsExternal;
OculusDesc.TextureOffset = Splash.UVRect.Min;
OculusDesc.TextureScale = Splash.UVRect.Max;
OculusDesc.LoadedTexture = Splash.Texture;
AddSplash(OculusDesc);
}
void FSplash::ClearSplashes()
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
SplashLayers.Reset();
}
bool FSplash::GetSplash(unsigned InSplashLayerIndex, FOculusXRSplashDesc& OutDesc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
if (InSplashLayerIndex < unsigned(SplashLayers.Num()))
{
OutDesc = SplashLayers[int32(InSplashLayerIndex)].Desc;
return true;
}
return false;
}
IStereoLayers::FLayerDesc FSplash::StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc)
{
IStereoLayers::FLayerDesc LayerDesc;
if (OculusDesc.LoadedTexture->GetTextureCube() != nullptr)
{
LayerDesc.SetShape<FCubemapLayer>();
}
// else LayerDesc.Shape defaults to FQuadLayer
LayerDesc.Transform = OculusDesc.TransformInMeters * FTransform(OculusXRHMD->GetSplashRotation().Quaternion());
LayerDesc.QuadSize = OculusDesc.QuadSizeInMeters;
LayerDesc.UVRect = FBox2D(OculusDesc.TextureOffset, OculusDesc.TextureOffset + OculusDesc.TextureScale);
LayerDesc.Priority = INT32_MAX - (int32)(OculusDesc.TransformInMeters.GetTranslation().X * 1000.f);
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = OculusDesc.LoadedTexture;
LayerDesc.Flags = IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO | (OculusDesc.bNoAlphaChannel ? IStereoLayers::LAYER_FLAG_TEX_NO_ALPHA_CHANNEL : 0) | (OculusDesc.bIsDynamic ? IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE : 0);
return LayerDesc;
}
void FSplash::DoShow()
{
CheckInGameThread();
OculusXRHMD->SetSplashRotationToForward();
// Create new textures
UnloadTextures();
// Make sure all UTextures are loaded and contain Resource->TextureRHI
bool bWaitForRT = false;
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Desc.TexturePath.IsValid())
{
// load temporary texture (if TexturePath was specified)
LoadTexture(SplashLayer);
}
if (SplashLayer.Desc.LoadingTexture && SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
SplashLayer.Desc.LoadingTexture->UpdateResource();
bWaitForRT = true;
}
}
FlushRenderingCommands();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
//@DBG BEGIN
if (SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
if (SplashLayer.Desc.LoadingTexture->GetResource() && SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI)
{
SplashLayer.Desc.LoadedTexture = SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI;
}
else
{
UE_LOG(LogHMD, Warning, TEXT("Splash, %s - no Resource"), *SplashLayer.Desc.LoadingTexture->GetDesc());
}
}
//@DBG END
if (SplashLayer.Desc.LoadedTexture)
{
SplashLayer.Layer = MakeShareable(new FLayer(NextLayerId++));
SplashLayer.Layer->SetDesc(StereoLayerDescFromOculusSplashDesc(SplashLayer.Desc));
}
}
{
//add oculus-generated layers through the OculusVR settings area
FScopeLock ScopeLock(&RenderThreadLock);
Layers_RenderThread_DeltaRotation.Reset();
Layers_RenderThread_Input.Reset();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); SplashLayerIndex++)
{
const FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Layer.IsValid())
{
FLayerPtr ClonedLayer = SplashLayer.Layer->Clone();
Layers_RenderThread_Input.Add(ClonedLayer);
// Register layers that need to be rotated every n ticks
if (!SplashLayer.Desc.DeltaRotation.Equals(FQuat::Identity))
{
Layers_RenderThread_DeltaRotation.Emplace(ClonedLayer, SplashLayer.Desc.DeltaRotation);
}
}
}
//add UE VR splash screen
FOculusXRSplashDesc UESplashDesc = OculusXRHMD->GetUESplashScreenDesc();
if (UESplashDesc.LoadedTexture != nullptr)
{
UELayer.Reset();
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(StereoLayerDescFromOculusSplashDesc(UESplashDesc));
Layers_RenderThread_Input.Add(UELayer->Clone());
}
Layers_RenderThread_Input.Sort(FLayerPtr_CompareId());
}
if (Layers_RenderThread_Input.Num() > 0)
{
// If no textures are loaded, this will push black frame
StartTicker();
bIsShown = true;
UE_LOG(LogHMD, Log, TEXT("FSplash::DoShow"));
}
else
{
UE_LOG(LogHMD, Log, TEXT("No splash layers in FSplash::DoShow"));
}
}
void FSplash::DoHide()
{
CheckInGameThread();
UE_LOG(LogHMD, Log, TEXT("FSplash::DoHide"));
bIsShown = false;
StopTicker();
}
void FSplash::UpdateLoadingScreen_GameThread()
{
if (bNeedSplashUpdate)
{
if (bShouldShowSplash)
{
DoShow();
}
else
{
DoHide();
}
bNeedSplashUpdate = false;
}
}
void FSplash::ShowLoadingScreen()
{
bShouldShowSplash = true;
// DoShow will be called from UpdateSplashScreen_Gamethread().
// This can can happen if the splashes are already being shown, as it will reset the relative positions and delta rotations of the layers.
bNeedSplashUpdate = true;
}
void FSplash::HideLoadingScreen()
{
bShouldShowSplash = false;
bNeedSplashUpdate = bIsShown; // no need to call DoHide when the splash is already hidden
}
void FSplash::UnloadTextures()
{
CheckInGameThread();
// unload temporary loaded textures
FScopeLock ScopeLock(&RenderThreadLock);
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
if (SplashLayers[SplashLayerIndex].Desc.TexturePath.IsValid())
{
UnloadTexture(SplashLayers[SplashLayerIndex]);
}
}
}
void FSplash::LoadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
UnloadTexture(InSplashLayer);
UE_LOG(LogLoadingSplash, Log, TEXT("Loading texture for splash %s..."), *InSplashLayer.Desc.TexturePath.GetAssetName());
InSplashLayer.Desc.LoadingTexture = Cast<UTexture>(InSplashLayer.Desc.TexturePath.TryLoad());
if (InSplashLayer.Desc.LoadingTexture != nullptr)
{
UE_LOG(LogLoadingSplash, Log, TEXT("...Success. "));
}
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
void FSplash::UnloadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
InSplashLayer.Desc.LoadingTexture = nullptr;
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,146 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IXRLoadingScreen.h"
#if WITH_EDITOR
#include "Editor.h"
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_Layer.h"
#include "TickableObjectRenderThread.h"
#include "OculusXRHMDTypes.h"
namespace OculusXRHMD
{
class FOculusXRHMD;
//-------------------------------------------------------------------------------------------------
// FSplashLayer
//-------------------------------------------------------------------------------------------------
struct FSplashLayer
{
FOculusXRSplashDesc Desc;
FLayerPtr Layer;
public:
FSplashLayer(const FOculusXRSplashDesc& InDesc)
: Desc(InDesc) {}
FSplashLayer(const FSplashLayer& InSplashLayer)
: Desc(InSplashLayer.Desc), Layer(InSplashLayer.Layer) {}
};
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
class FSplash : public IXRLoadingScreen, public TSharedFromThis<FSplash>
{
protected:
class FTicker : public FTickableObjectRenderThread, public TSharedFromThis<FTicker>
{
public:
FTicker(FSplash* InSplash)
: FTickableObjectRenderThread(false, true), pSplash(InSplash) {}
virtual void Tick(float DeltaTime) override { pSplash->Tick_RenderThread(DeltaTime); }
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(FSplash, STATGROUP_Tickables); }
virtual bool IsTickable() const override { return true; }
protected:
FSplash* pSplash;
};
public:
FSplash(FOculusXRHMD* InPlugin);
virtual ~FSplash();
void Tick_RenderThread(float DeltaTime);
void Startup();
void LoadSettings();
void ReleaseResources_RHIThread();
void PreShutdown();
void Shutdown();
void OnPreLoadMap(const FString&);
void OnPostLoadMap(UWorld* LoadedWorld);
#if WITH_EDITOR
void OnPieBegin(bool bIsSimulating);
#endif
// Called from FOculusXRHMD
void UpdateLoadingScreen_GameThread();
// Internal extended API
int AddSplash(const FOculusXRSplashDesc&);
bool GetSplash(unsigned index, FOculusXRSplashDesc& OutDesc);
void StopTicker();
void StartTicker();
// The standard IXRLoadingScreen interface
virtual void ShowLoadingScreen() override;
virtual void HideLoadingScreen() override;
virtual void ClearSplashes() override;
virtual void AddSplash(const FSplashDesc& Splash) override;
virtual bool IsShown() const override { return bIsShown; }
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
virtual bool IsPlayingLoadingMovie() const override
{
return false;
}
#endif
protected:
void DoShow();
void DoHide();
void UnloadTextures();
void LoadTexture(FSplashLayer& InSplashLayer);
void UnloadTexture(FSplashLayer& InSplashLayer);
void RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList);
IStereoLayers::FLayerDesc StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc);
protected:
FOculusXRHMD* OculusXRHMD;
FCustomPresent* CustomPresent;
TSharedPtr<FTicker> Ticker;
int32 FramesOutstanding;
FCriticalSection RenderThreadLock;
FSettingsPtr Settings;
FGameFramePtr Frame;
TArray<FSplashLayer> SplashLayers;
uint32 NextLayerId;
FLayerPtr BlackLayer;
FLayerPtr UELayer;
TArray<TTuple<FLayerPtr, FQuat>> Layers_RenderThread_DeltaRotation;
TArray<FLayerPtr> Layers_RenderThread_Input;
TArray<FLayerPtr> Layers_RenderThread;
TArray<FLayerPtr> Layers_RHIThread;
// All these flags are only modified from the Game thread
bool bInitialized;
bool bIsShown;
bool bNeedSplashUpdate;
bool bShouldShowSplash;
float SystemDisplayInterval;
double LastTimeInSeconds;
FDelegateHandle PreLoadLevelDelegate;
FDelegateHandle PostLoadLevelDelegate;
#if WITH_EDITOR
FDelegateHandle PieBeginDelegateHandle;
#endif
};
typedef TSharedPtr<FSplash> FSplashPtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

Some files were not shown because too many files have changed in this diff Show More