Config for building for Quest
This commit is contained in:
@@ -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",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRAnchorsModule.h"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
146
Plugins/MetaXR/Source/OculusXRAnchors/Public/OculusXRAnchors.h
Normal file
146
Plugins/MetaXR/Source/OculusXRAnchors/Public/OculusXRAnchors.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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:
|
||||
};
|
||||
55
Plugins/MetaXR/Source/OculusXREditor/OculusXREditor.Build.cs
Normal file
55
Plugins/MetaXR/Source/OculusXREditor/OculusXREditor.Build.cs
Normal 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"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXREditorSettings.h"
|
||||
|
||||
UOculusXREditorSettings::UOculusXREditorSettings()
|
||||
: PerfToolTargetPlatform(EOculusXRPlatform::PC)
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
namespace OculusXRTelemetry
|
||||
{
|
||||
void SpawnNotification();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
{
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRTelemetry.h"
|
||||
|
||||
namespace OculusXRTelemetry::Events
|
||||
{
|
||||
using FMovementSDKEyeTrackerCreated = TMarker<191957973>;
|
||||
}
|
||||
549
Plugins/MetaXR/Source/OculusXRHMD/OculusMobile_APL.xml
Normal file
549
Plugins/MetaXR/Source/OculusXRHMD/OculusMobile_APL.xml
Normal 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>
|
||||
139
Plugins/MetaXR/Source/OculusXRHMD/OculusXRHMD.Build.cs
Normal file
139
Plugins/MetaXR/Source/OculusXRHMD/OculusXRHMD.Build.cs
Normal 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
4876
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD.cpp
Normal file
4876
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD.cpp
Normal file
File diff suppressed because it is too large
Load Diff
637
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD.h
Normal file
637
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD.h
Normal 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
|
||||
509
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDModule.cpp
Normal file
509
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDModule.cpp
Normal 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)
|
||||
121
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDModule.h
Normal file
121
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDModule.h
Normal 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
|
||||
};
|
||||
@@ -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
|
||||
311
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDPrivate.h
Normal file
311
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMDPrivate.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
1484
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Layer.cpp
Normal file
1484
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Layer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
233
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Layer.h
Normal file
233
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Layer.h
Normal 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
|
||||
@@ -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
|
||||
174
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Settings.h
Normal file
174
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Settings.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
666
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Splash.cpp
Normal file
666
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Splash.cpp
Normal 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
|
||||
146
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Splash.h
Normal file
146
Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_Splash.h
Normal 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
Reference in New Issue
Block a user