Config for building for Quest

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

View File

@@ -0,0 +1,17 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRScene.h"
#include "OculusXRSceneModule.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMD.h"
#include "OculusXRPluginWrapper.h"
#define LOCTEXT_NAMESPACE "OculusXRScene"
namespace OculusXRScene
{
} // namespace OculusXRScene
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,757 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSceneActor.h"
#include "OculusXRSceneModule.h"
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRAnchorManager.h"
#include "OculusXRAnchorTypes.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRSceneDelegates.h"
#include "OculusXRDelegates.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/AssetManager.h"
#include "Engine/StaticMesh.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/World.h"
#include "GameFramework/WorldSettings.h"
#include "ProceduralMeshComponent.h"
#include "OculusXRSceneGlobalMeshComponent.h"
#define LOCTEXT_NAMESPACE "OculusXRSceneActor"
//////////////////////////////////////////////////////////////////////////
// ASceneActor
AOculusXRSceneActor::AOculusXRSceneActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ResetStates();
// Create required components
RoomLayoutManagerComponent = CreateDefaultSubobject<UOculusXRRoomLayoutManagerComponent>(TEXT("OculusXRRoomLayoutManagerComponent"));
// Following are the semantic labels we want to support default properties for. User can always add new ones through the properties panel if needed.
const FString default2DSemanticClassifications[] = {
TEXT("WALL_FACE"),
TEXT("CEILING"),
TEXT("FLOOR"),
TEXT("COUCH"),
TEXT("TABLE"),
TEXT("DOOR_FRAME"),
TEXT("WINDOW_FRAME"),
TEXT("WALL_ART"),
TEXT("INVISIBLE_WALL_FACE"),
TEXT("OTHER")
};
const FString default3DSemanticClassifications[] = {
TEXT("COUCH"),
TEXT("TABLE"),
TEXT("SCREEN"),
TEXT("BED"),
TEXT("LAMP"),
TEXT("PLANT"),
TEXT("STORAGE"),
TEXT("OTHER")
};
FOculusXRSpawnedSceneAnchorProperties spawnedAnchorProps;
spawnedAnchorProps.ActorComponent = nullptr;
spawnedAnchorProps.StaticMesh = nullptr;
// Setup initial scene plane and volume properties
for (auto& semanticLabel2D : default2DSemanticClassifications)
{
FOculusXRSpawnedSceneAnchorProperties& props = ScenePlaneSpawnedSceneAnchorProperties.Add(semanticLabel2D, spawnedAnchorProps);
props.ForceParallelToFloor = (semanticLabel2D != "WALL_FACE");
}
for (auto& semanticLabel3D : default3DSemanticClassifications)
{
FOculusXRSpawnedSceneAnchorProperties& props = SceneVolumeSpawnedSceneAnchorProperties.Add(semanticLabel3D, spawnedAnchorProps);
props.ForceParallelToFloor = true;
}
}
void AOculusXRSceneActor::ResetStates()
{
bCaptureFlowWasLaunched = false;
ClearScene();
}
void AOculusXRSceneActor::BeginPlay()
{
Super::BeginPlay();
// Create a scene component as root so we can attach spawned actors to it
USceneComponent* rootSceneComponent = NewObject<USceneComponent>(this, USceneComponent::StaticClass());
rootSceneComponent->SetMobility(EComponentMobility::Static);
rootSceneComponent->RegisterComponent();
SetRootComponent(rootSceneComponent);
SceneGlobalMeshComponent = FindComponentByClass<UOculusXRSceneGlobalMeshComponent>();
// Register delegates
RoomLayoutManagerComponent->OculusXRRoomLayoutSceneCaptureCompleteNative.AddUObject(this, &AOculusXRSceneActor::SceneCaptureComplete_Handler);
// Make an initial request to query for the room layout if bPopulateSceneOnBeginPlay was set to true
if (bPopulateSceneOnBeginPlay)
{
PopulateScene();
}
}
void AOculusXRSceneActor::EndPlay(EEndPlayReason::Type Reason)
{
// Unregister delegates
RoomLayoutManagerComponent->OculusXRRoomLayoutSceneCaptureCompleteNative.RemoveAll(this);
// Calling ResetStates will reset member variables to their default values (including the request IDs).
ResetStates();
Super::EndPlay(Reason);
}
void AOculusXRSceneActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
bool AOculusXRSceneActor::IsValidUuid(const FOculusXRUUID& Uuid)
{
return Uuid.UUIDBytes != nullptr;
}
void AOculusXRSceneActor::LaunchCaptureFlow()
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("Launch capture flow"));
if (RoomLayoutManagerComponent)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("Launch capture flow -- RoomLayoutManagerComponent"));
const bool bResult = RoomLayoutManagerComponent->LaunchCaptureFlow();
if (!bResult)
{
UE_LOG(LogOculusXRScene, Error, TEXT("LaunchCaptureFlow() failed!"));
}
}
}
void AOculusXRSceneActor::LaunchCaptureFlowIfNeeded()
{
#if WITH_EDITOR
UE_LOG(LogOculusXRScene, Display, TEXT("Scene Capture does not work over Link. Please capture a scene with the HMD in standalone mode, then access the scene model over Link."));
#else
// Depending on LauchCaptureFlowWhenMissingScene, we might not want to launch Capture Flow
if (LauchCaptureFlowWhenMissingScene != EOculusXRLaunchCaptureFlowWhenMissingScene::NEVER)
{
if (LauchCaptureFlowWhenMissingScene == EOculusXRLaunchCaptureFlowWhenMissingScene::ALWAYS || (!bCaptureFlowWasLaunched && LauchCaptureFlowWhenMissingScene == EOculusXRLaunchCaptureFlowWhenMissingScene::ONCE))
{
LaunchCaptureFlow();
}
}
#endif
}
AActor* AOculusXRSceneActor::SpawnActorWithSceneComponent(const FOculusXRUInt64& Space, const FOculusXRUInt64& RoomSpaceID, const TArray<FString>& SemanticClassifications, UClass* sceneAnchorComponentInstanceClass)
{
FActorSpawnParameters actorSpawnParams;
actorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AActor* Anchor = GetWorld()->SpawnActor<AActor>(AActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, actorSpawnParams);
USceneComponent* rootComponent = NewObject<USceneComponent>(Anchor, USceneComponent::StaticClass());
rootComponent->SetMobility(EComponentMobility::Movable);
rootComponent->RegisterComponent();
Anchor->SetRootComponent(rootComponent);
rootComponent->SetWorldLocation(FVector::ZeroVector);
Anchor->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);
#if WITH_EDITOR
if (SemanticClassifications.Num() > 0)
{
Anchor->SetActorLabel(FString::Join(SemanticClassifications, TEXT("-")), false);
}
#endif
UOculusXRSceneAnchorComponent* sceneAnchorComponent = NewObject<UOculusXRSceneAnchorComponent>(Anchor, sceneAnchorComponentInstanceClass);
sceneAnchorComponent->RegisterComponent();
sceneAnchorComponent->SetHandle(Space);
sceneAnchorComponent->SemanticClassifications = SemanticClassifications;
sceneAnchorComponent->RoomSpaceID = RoomSpaceID;
EOculusXRAnchorResult::Type Result;
OculusXRAnchors::FOculusXRAnchors::SetAnchorComponentStatus(sceneAnchorComponent, EOculusXRSpaceComponentType::Locatable, true, 0.0f, FOculusXRAnchorSetComponentStatusDelegate(), Result);
return Anchor;
}
AActor* AOculusXRSceneActor::SpawnOrUpdateSceneAnchor(AActor* Anchor, const FOculusXRUInt64& Space, const FOculusXRUInt64& RoomSpaceID, const FVector& BoundedPos, const FVector& BoundedSize, const TArray<FString>& SemanticClassifications, const EOculusXRSpaceComponentType AnchorComponentType)
{
if (Space.Value == 0)
{
UE_LOG(LogOculusXRScene, Error, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor Invalid Space handle."));
return Anchor;
}
if (!(AnchorComponentType == EOculusXRSpaceComponentType::ScenePlane || AnchorComponentType == EOculusXRSpaceComponentType::SceneVolume))
{
UE_LOG(LogOculusXRScene, Error, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor Anchor doesn't have ScenePlane or SceneVolume component active."));
return Anchor;
}
if (0 == SemanticClassifications.Num())
{
UE_LOG(LogOculusXRScene, Error, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor No semantic classification found."));
return Anchor;
}
FOculusXRSpawnedSceneAnchorProperties* foundProperties = (AnchorComponentType == EOculusXRSpaceComponentType::ScenePlane) ? ScenePlaneSpawnedSceneAnchorProperties.Find(SemanticClassifications[0]) : SceneVolumeSpawnedSceneAnchorProperties.Find(SemanticClassifications[0]);
if (!foundProperties)
{
UE_LOG(LogOculusXRScene, Warning, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor Scene object has an unknown semantic label. Will not be spawned."));
return Anchor;
}
TSoftClassPtr<UOculusXRSceneAnchorComponent>* sceneAnchorComponentClassPtrRef = &foundProperties->ActorComponent;
TSoftObjectPtr<UStaticMesh>* staticMeshObjPtrRef = &foundProperties->StaticMesh;
UClass* sceneAnchorComponentInstanceClass = sceneAnchorComponentClassPtrRef->LoadSynchronous();
if (!sceneAnchorComponentInstanceClass)
{
UE_LOG(LogOculusXRScene, Error, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor Scene anchor component class is invalid! Cannot spawn actor to populate the scene."));
return Anchor;
}
if (!Anchor)
{
Anchor = SpawnActorWithSceneComponent(Space, RoomSpaceID, SemanticClassifications, sceneAnchorComponentInstanceClass);
}
if (staticMeshObjPtrRef && staticMeshObjPtrRef->IsPending())
{
staticMeshObjPtrRef->LoadSynchronous();
}
UStaticMesh* refStaticMesh = staticMeshObjPtrRef ? staticMeshObjPtrRef->Get() : nullptr;
if (refStaticMesh == nullptr)
{
UE_LOG(LogOculusXRScene, Warning, TEXT("AOculusXRSceneActor::SpawnOrUpdateSceneAnchor Spawn scene anchor mesh is invalid for %s!"), *SemanticClassifications[0]);
return Anchor;
}
UStaticMeshComponent* staticMeshComponent = NewObject<UStaticMeshComponent>(Anchor, UStaticMeshComponent::StaticClass());
staticMeshComponent->RegisterComponent();
staticMeshComponent->SetStaticMesh(refStaticMesh);
staticMeshComponent->AttachToComponent(Anchor->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform);
const float worldToMeters = GetWorld()->GetWorldSettings()->WorldToMeters;
FVector offset(0.0f, BoundedSize.Y / 2.0f, BoundedSize.Z / 2.0f);
staticMeshComponent->SetRelativeLocation(foundProperties->AddOffset + ((BoundedPos + offset) * worldToMeters), false, nullptr, ETeleportType::ResetPhysics);
// Setup scale based on bounded size and the actual size of the mesh
UStaticMesh* staticMesh = staticMeshComponent->GetStaticMesh();
FBoxSphereBounds staticMeshBounds;
staticMeshBounds.BoxExtent = FVector{ 1.f, 1.f, 1.f };
if (staticMesh)
{
staticMeshBounds = staticMesh->GetBounds();
}
staticMeshComponent->SetRelativeScale3D(FVector(
(BoundedSize.X < UE_SMALL_NUMBER) ? 1 : (BoundedSize.X / (staticMeshBounds.BoxExtent.X * 2.f)) * worldToMeters,
(BoundedSize.Y < UE_SMALL_NUMBER) ? 1 : (BoundedSize.Y / (staticMeshBounds.BoxExtent.Y * 2.f)) * worldToMeters,
(BoundedSize.Z < UE_SMALL_NUMBER) ? 1 : (BoundedSize.Z / (staticMeshBounds.BoxExtent.Z * 2.f)) * worldToMeters));
return Anchor;
}
bool AOculusXRSceneActor::IsScenePopulated()
{
if (!RootComponent)
{
return false;
}
return RootComponent->GetNumChildrenComponents() > 0;
}
bool AOculusXRSceneActor::IsRoomLayoutValid()
{
return true;
}
void AOculusXRSceneActor::PopulateScene()
{
if (!RootComponent)
{
return;
}
const EOculusXRAnchorResult::Type result = QueryAllRooms();
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(result))
{
UE_LOG(LogOculusXRScene, Error, TEXT("PopulateScene Failed to query available rooms"));
}
}
void AOculusXRSceneActor::ClearScene()
{
if (!RootComponent)
return;
TArray<USceneComponent*> childrenComponents = RootComponent->GetAttachChildren();
for (USceneComponent* SceneComponent : childrenComponents)
{
Cast<AActor>(SceneComponent->GetOuter())->Destroy();
}
bRoomLayoutIsValid = false;
bFoundCapturedScene = false;
}
void AOculusXRSceneActor::SetVisibilityToAllSceneAnchors(const bool bIsVisible)
{
if (!RootComponent)
return;
TArray<USceneComponent*> childrenComponents = RootComponent->GetAttachChildren();
for (USceneComponent* sceneComponent : childrenComponents)
{
sceneComponent->SetVisibility(bIsVisible, true);
}
}
void AOculusXRSceneActor::SetVisibilityToSceneAnchorsBySemanticLabel(const FString SemanticLabel, const bool bIsVisible)
{
FString label = SemanticLabel;
if (SemanticLabel == TEXT("DESK"))
{
label = TEXT("TABLE");
UE_LOG(LogOculusXRScene, Warning, TEXT("XR Scene Actor semantic lable 'DESK' is deprecated, use 'TABLE' instead."));
}
if (!RootComponent)
return;
TArray<USceneComponent*> childrenComponents = RootComponent->GetAttachChildren();
for (USceneComponent* sceneComponent : childrenComponents)
{
UObject* outerObject = sceneComponent->GetOuter();
if (!outerObject)
{
continue;
}
AActor* outerActor = Cast<AActor>(outerObject);
if (!outerActor)
{
continue;
}
UActorComponent* sceneAnchorComponent = outerActor->GetComponentByClass(UOculusXRSceneAnchorComponent::StaticClass());
if (!sceneAnchorComponent)
{
continue;
}
if (Cast<UOculusXRSceneAnchorComponent>(sceneAnchorComponent)->SemanticClassifications.Contains(label))
{
sceneComponent->SetVisibility(bIsVisible, true);
}
}
}
TArray<AActor*> AOculusXRSceneActor::GetActorsBySemanticLabel(const FString SemanticLabel)
{
FString label = SemanticLabel;
if (SemanticLabel == TEXT("DESK"))
{
label = TEXT("TABLE");
UE_LOG(LogOculusXRScene, Warning, TEXT("XR Scene Actor semantic lable 'DESK' is deprecated, use 'TABLE' instead."));
}
TArray<AActor*> actors;
if (!RootComponent)
return actors;
TArray<USceneComponent*> childrenComponents = RootComponent->GetAttachChildren();
for (USceneComponent* sceneComponent : childrenComponents)
{
UObject* outerObject = sceneComponent->GetOuter();
if (!outerObject)
{
continue;
}
AActor* outerActor = Cast<AActor>(outerObject);
if (!outerActor)
{
continue;
}
UActorComponent* sceneAnchorComponent = outerActor->GetComponentByClass(UOculusXRSceneAnchorComponent::StaticClass());
if (!sceneAnchorComponent)
{
continue;
}
if (Cast<UOculusXRSceneAnchorComponent>(sceneAnchorComponent)->SemanticClassifications.Contains(label))
{
actors.Add(outerActor);
}
}
return actors;
}
TArray<FOculusXRRoomLayout> AOculusXRSceneActor::GetRoomLayouts() const
{
TArray<FOculusXRRoomLayout> layouts;
RoomLayouts.GenerateValueArray(layouts);
return layouts;
}
EOculusXRAnchorResult::Type AOculusXRSceneActor::QueryAllRooms()
{
FOculusXRSpaceQueryInfo queryInfo;
queryInfo.MaxQuerySpaces = MaxQueries;
queryInfo.FilterType = EOculusXRSpaceQueryFilterType::FilterByComponentType;
queryInfo.ComponentFilter.Add(EOculusXRSpaceComponentType::RoomLayout);
EOculusXRAnchorResult::Type anchorQueryResult;
OculusXRAnchors::FOculusXRAnchors::QueryAnchorsAdvanced(queryInfo,
FOculusXRAnchorQueryDelegate::CreateUObject(this, &AOculusXRSceneActor::RoomLayoutQueryComplete), anchorQueryResult);
return anchorQueryResult;
}
void AOculusXRSceneActor::RoomLayoutQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("RoomLayoutQueryComplete (Result = %d)"), AnchorResult);
for (auto& QueryElement : QueryResults)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("RoomLayoutQueryComplete -- Query Element (space = %llu, uuid = %s"), QueryElement.Space.Value, *BytesToHex(QueryElement.UUID.UUIDBytes, OCULUSXR_UUID_SIZE));
FOculusXRRoomLayout roomLayout;
const bool bGetRoomLayoutResult = RoomLayoutManagerComponent->GetRoomLayout(QueryElement.Space.Value, roomLayout, MaxQueries);
if (!bGetRoomLayoutResult)
{
UE_LOG(LogOculusXRScene, Error, TEXT("RoomLayoutQueryComplete -- Failed to get room layout for space (space = %llu, uuid = %s"),
QueryElement.Space.Value, *BytesToHex(QueryElement.UUID.UUIDBytes, OCULUSXR_UUID_SIZE));
continue;
}
roomLayout.RoomAnchorHandle = QueryElement.Space;
roomLayout.RoomUuid = QueryElement.UUID;
// If we're only loading the active room we start that floor check query here, otherwise do the room query
if (bActiveRoomOnly)
{
QueryFloorForActiveRoom(QueryElement.Space, roomLayout);
}
else
{
StartSingleRoomQuery(QueryElement.Space, roomLayout);
}
}
}
EOculusXRAnchorResult::Type AOculusXRSceneActor::QueryRoomUUIDs(const FOculusXRUInt64 RoomSpaceID, const TArray<FOculusXRUUID>& RoomUUIDs)
{
EOculusXRAnchorResult::Type startAnchorQueryResult;
OculusXRAnchors::FOculusXRAnchors::QueryAnchors(
RoomUUIDs,
EOculusXRSpaceStorageLocation::Local,
FOculusXRAnchorQueryDelegate::CreateUObject(this, &AOculusXRSceneActor::SceneRoomQueryComplete, RoomSpaceID),
startAnchorQueryResult);
return startAnchorQueryResult;
}
void AOculusXRSceneActor::SceneRoomQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults, const FOculusXRUInt64 RoomSpaceID)
{
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(AnchorResult))
{
return;
}
bool bOutPending = false;
for (auto& AnchorQueryElement : QueryResults)
{
if (SceneGlobalMeshComponent)
{
TArray<FString> semanticClassifications;
GetSemanticClassifications(AnchorQueryElement.Space.Value, semanticClassifications);
UE_LOG(LogOculusXRScene, Log, TEXT("SpatialAnchor Scene label is %s"), semanticClassifications.Num() > 0 ? *semanticClassifications[0] : TEXT("unknown"));
if (semanticClassifications.Contains(UOculusXRSceneGlobalMeshComponent::GlobalMeshSemanticLabel))
{
bool bIsTriangleMesh = false;
EOculusXRAnchorResult::Type result = OculusXRAnchors::FOculusXRAnchorManager::GetSpaceComponentStatus(
AnchorQueryElement.Space.Value, EOculusXRSpaceComponentType::TriangleMesh, bIsTriangleMesh, bOutPending);
if (!UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(result) || !bIsTriangleMesh)
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to load Triangle Mesh Component for a GLOBAL_MESH"));
continue;
}
UClass* sceneAnchorComponentInstanceClass = SceneGlobalMeshComponent->GetAnchorComponentClass();
AActor* globalMeshAnchor = SpawnActorWithSceneComponent(AnchorQueryElement.Space.Value, RoomSpaceID, semanticClassifications, sceneAnchorComponentInstanceClass);
SceneGlobalMeshComponent->CreateMeshComponent(AnchorQueryElement.Space, globalMeshAnchor, RoomLayoutManagerComponent);
continue;
}
}
bool bIsScenePlane = false;
bool bIsSceneVolume = false;
EOculusXRAnchorResult::Type isPlaneResult = OculusXRAnchors::FOculusXRAnchorManager::GetSpaceComponentStatus(
AnchorQueryElement.Space.Value, EOculusXRSpaceComponentType::ScenePlane, bIsScenePlane, bOutPending);
EOculusXRAnchorResult::Type isVolumeResult = OculusXRAnchors::FOculusXRAnchorManager::GetSpaceComponentStatus(
AnchorQueryElement.Space.Value, EOculusXRSpaceComponentType::SceneVolume, bIsSceneVolume, bOutPending);
bool bIsPlaneResultSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(isPlaneResult);
bool bIsVolumeResultSuccess = UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(isVolumeResult);
AActor* anchor = nullptr;
if (bIsPlaneResultSuccess && bIsScenePlane)
{
EOculusXRAnchorResult::Type Result;
FVector scenePlanePos;
FVector scenePlaneSize;
bool ResultSuccess = OculusXRAnchors::FOculusXRAnchors::GetSpaceScenePlane(AnchorQueryElement.Space.Value, scenePlanePos, scenePlaneSize, Result);
if (ResultSuccess)
{
UE_LOG(LogOculusXRScene, Log, TEXT("SpatialAnchorQueryResult_Handler ScenePlane pos = [%.2f, %.2f, %.2f], size = [%.2f, %.2f, %.2f]."),
scenePlanePos.X, scenePlanePos.Y, scenePlanePos.Z,
scenePlaneSize.X, scenePlaneSize.Y, scenePlaneSize.Z);
TArray<FString> semanticClassifications;
GetSemanticClassifications(AnchorQueryElement.Space.Value, semanticClassifications);
UE_LOG(LogOculusXRScene, Log, TEXT("SpatialAnchor ScenePlane label is %s"), semanticClassifications.Num() > 0 ? *semanticClassifications[0] : TEXT("unknown"));
anchor = SpawnOrUpdateSceneAnchor(anchor, AnchorQueryElement.Space, RoomSpaceID, scenePlanePos, scenePlaneSize, semanticClassifications, EOculusXRSpaceComponentType::ScenePlane);
if (!anchor)
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to spawn scene anchor."));
}
}
else
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to get bounds for ScenePlane space."));
}
}
if (bIsVolumeResultSuccess && bIsSceneVolume)
{
EOculusXRAnchorResult::Type Result;
FVector sceneVolumePos;
FVector sceneVolumeSize;
bool ResultSuccess = OculusXRAnchors::FOculusXRAnchors::GetSpaceSceneVolume(AnchorQueryElement.Space.Value, sceneVolumePos, sceneVolumeSize, Result);
if (ResultSuccess)
{
UE_LOG(LogOculusXRScene, Log, TEXT("SpatialAnchorQueryResult_Handler SceneVolume pos = [%.2f, %.2f, %.2f], size = [%.2f, %.2f, %.2f]."),
sceneVolumePos.X, sceneVolumePos.Y, sceneVolumePos.Z,
sceneVolumeSize.X, sceneVolumeSize.Y, sceneVolumeSize.Z);
TArray<FString> semanticClassifications;
GetSemanticClassifications(AnchorQueryElement.Space.Value, semanticClassifications);
UE_LOG(LogOculusXRScene, Log, TEXT("SpatialAnchor SceneVolume label is %s"), semanticClassifications.Num() > 0 ? *semanticClassifications[0] : TEXT("unknown"));
anchor = SpawnOrUpdateSceneAnchor(anchor, AnchorQueryElement.Space, RoomSpaceID, sceneVolumePos, sceneVolumeSize, semanticClassifications, EOculusXRSpaceComponentType::SceneVolume);
if (!anchor)
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to spawn scene anchor."));
}
}
else
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to get bounds for SceneVolume space."));
}
}
}
}
void AOculusXRSceneActor::StartSingleRoomQuery(FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout)
{
EOculusXRAnchorResult::Type startQueryResult = QueryRoomUUIDs(RoomSpaceID, RoomLayout.RoomObjectUUIDs);
if (UOculusXRAnchorBPFunctionLibrary::IsAnchorResultSuccess(startQueryResult))
{
RoomLayouts.Add(RoomSpaceID, std::move(RoomLayout));
}
}
EOculusXRAnchorResult::Type AOculusXRSceneActor::QueryFloorForActiveRoom(FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout)
{
EOculusXRAnchorResult::Type anchorQueryResult;
OculusXRAnchors::FOculusXRAnchors::QueryAnchors(
TArray<FOculusXRUUID>({ RoomLayout.FloorUuid }),
EOculusXRSpaceStorageLocation::Local,
FOculusXRAnchorQueryDelegate::CreateUObject(this, &AOculusXRSceneActor::ActiveRoomFloorQueryComplete, RoomSpaceID, RoomLayout),
anchorQueryResult);
return anchorQueryResult;
}
void AOculusXRSceneActor::ActiveRoomFloorQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults, FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout)
{
if (QueryResults.Num() != 1)
{
UE_LOG(LogOculusXRScene, Error, TEXT("Wrong number of elements returned from query for floor UUID. Result count (%d), UUID (%s), Room Space ID (%llu)"), QueryResults.Num(), *RoomLayout.FloorUuid.ToString(), RoomSpaceID.Value);
return;
}
const FOculusXRSpaceQueryResult& floorQueryResult = QueryResults[0];
TArray<FString> semanticClassifications;
GetSemanticClassifications(floorQueryResult.Space.Value, semanticClassifications);
if (!semanticClassifications.Contains("FLOOR"))
{
UE_LOG(LogOculusXRScene, Error, TEXT("Queried floor in room doesn't contain a floor semantic label. UUID (%s), Room Space ID (%llu)"), *RoomLayout.FloorUuid.ToString(), RoomSpaceID.Value);
return;
}
EOculusXRAnchorResult::Type getBoundaryResult;
TArray<FVector2f> boundaryVertices;
if (!OculusXRAnchors::FOculusXRAnchors::GetSpaceBoundary2D(floorQueryResult.Space.Value, boundaryVertices, getBoundaryResult))
{
UE_LOG(LogOculusXRScene, Error, TEXT("Failed to get space boundary vertices for floor. UUID (%s), Room Space ID (%llu)"), *RoomLayout.FloorUuid.ToString(), RoomSpaceID.Value);
return;
}
OculusXRHMD::FOculusXRHMD* HMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
check(HMD);
OculusXRHMD::FPose headPose;
HMD->GetCurrentPose(OculusXRHMD::ToExternalDeviceId(ovrpNode_Head), headPose.Orientation, headPose.Position);
TArray<FVector> convertedBoundaryPoints;
FTransform floorTransform;
if (!UOculusXRAnchorBPFunctionLibrary::GetAnchorTransformByHandle(floorQueryResult.Space, floorTransform))
{
UE_LOG(LogOculusXRScene, Error, TEXT("Failed to get the floor anchor transform. Floor Space ID (%llu)"), floorQueryResult.Space.Value);
return;
}
// Convert the boundary vertices to game engine world space
for (auto& it : boundaryVertices)
{
FVector pos = floorTransform.TransformPosition(FVector(0, it.X * HMD->GetWorldToMetersScale(), it.Y * HMD->GetWorldToMetersScale()));
convertedBoundaryPoints.Add(pos);
}
// Create the new 2D boundary
TArray<FVector2f> new2DBoundary;
for (auto& it : convertedBoundaryPoints)
{
new2DBoundary.Add(FVector2f(it.X, it.Y));
}
// Check if inside poly
if (!PointInPolygon2D(FVector2f(headPose.Position.X, headPose.Position.Y), new2DBoundary))
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("Floor failed active room check. UUID (%s), Room Space ID (%llu)"), *RoomLayout.FloorUuid.ToString(), RoomSpaceID.Value);
return;
}
StartSingleRoomQuery(RoomSpaceID, RoomLayout);
}
bool AOculusXRSceneActor::PointInPolygon2D(FVector2f PointToTest, const TArray<FVector2f>& PolyVerts) const
{
if (PolyVerts.Num() < 3)
{
return false;
}
int collision = 0;
float x = PointToTest.X;
float y = PointToTest.Y;
int vertCount = PolyVerts.Num();
for (int i = 0; i < vertCount; i++)
{
float x1 = PolyVerts[i].X;
float y1 = PolyVerts[i].Y;
float x2 = PolyVerts[(i + 1) % vertCount].X;
float y2 = PolyVerts[(i + 1) % vertCount].Y;
if (y < y1 != y < y2 && x < x1 + ((y - y1) / (y2 - y1)) * (x2 - x1))
{
collision += (y1 < y2) ? 1 : -1;
}
}
return collision != 0;
}
void AOculusXRSceneActor::GetSemanticClassifications(uint64 Space, TArray<FString>& OutSemanticLabels) const
{
EOculusXRAnchorResult::Type SemanticLabelAnchorResult;
bool Result = OculusXRAnchors::FOculusXRAnchors::GetSpaceSemanticClassification(Space, OutSemanticLabels, SemanticLabelAnchorResult);
if (Result)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("GetSemanticClassifications -- Space (%llu) Classifications:"), Space);
for (FString& label : OutSemanticLabels)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("%s"), *label);
}
}
else
{
UE_LOG(LogOculusXRScene, Error, TEXT("SpatialAnchorQueryResult_Handler Failed to get semantic classification space."));
}
}
// DELEGATE HANDLERS
void AOculusXRSceneActor::SceneCaptureComplete_Handler(FOculusXRUInt64 RequestId, bool bResult)
{
if (!bResult)
{
UE_LOG(LogOculusXRScene, Error, TEXT("Scene Capture Complete failed!"));
return;
}
// Mark that we already launched Capture Flow and try to query spatial anchors again
bCaptureFlowWasLaunched = true;
ClearScene();
PopulateScene();
}
void AOculusXRSceneActor::PostLoad()
{
Super::PostLoad();
FOculusXRSpawnedSceneAnchorProperties desk;
if (ScenePlaneSpawnedSceneAnchorProperties.RemoveAndCopyValue(TEXT("DESK"), desk))
{
UE_LOG(LogOculusXRScene, Log, TEXT("Running XR Scene Actor plane semantic lable migration: 'DESK' to 'TABLE'"));
ScenePlaneSpawnedSceneAnchorProperties[TEXT("TABLE")] = desk;
}
if (SceneVolumeSpawnedSceneAnchorProperties.RemoveAndCopyValue(TEXT("DESK"), desk))
{
UE_LOG(LogOculusXRScene, Log, TEXT("Running XR Scene Actor volume semantic lable migration: 'DESK' to 'TABLE'"));
SceneVolumeSpawnedSceneAnchorProperties[TEXT("TABLE")] = desk;
}
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,21 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSceneAnchorComponent.h"
#include "Engine/StaticMeshActor.h"
UOculusXRSceneAnchorComponent::UOculusXRSceneAnchorComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bUpdateHeadSpaceTransform = false;
}
void UOculusXRSceneAnchorComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (GetHandle().Value == 0)
{
return;
}
}

View File

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

View File

@@ -0,0 +1,38 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSceneEventHandling.h"
#include "OculusXRHMD.h"
#include "IOculusXRSceneModule.h"
#include "OculusXRSceneDelegates.h"
#include "OculusXRSceneEventDelegates.h"
namespace OculusXRScene
{
template <typename T>
void GetEventData(ovrpEventDataBuffer& Buffer, T& OutEventData)
{
unsigned char* BufData = Buffer.EventData;
BufData -= sizeof(Buffer.EventType); // Offset buffer data to get to the actual event payload
memcpy(&OutEventData, BufData, sizeof(T));
}
void FOculusXRSceneEventHandling::OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult)
{
ovrpEventDataBuffer& buf = *EventDataBuffer;
EventPollResult = true;
switch (buf.EventType)
{
case ovrpEventType_None:
default:
{
EventPollResult = false;
break;
}
}
}
} // namespace OculusXRScene

View File

@@ -0,0 +1,19 @@
/*
Copyright (c) Meta Platforms, Inc. and affiliates.
All rights reserved.
This source code is licensed under the license found in the
LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "CoreMinimal.h"
#include "OculusXRHMDPrivate.h"
namespace OculusXRScene
{
struct OCULUSXRSCENE_API FOculusXRSceneEventHandling
{
static void OnPollEvent(ovrpEventDataBuffer* EventDataBuffer, bool& EventPollResult);
};
} // namespace OculusXRScene

View File

@@ -0,0 +1,11 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSceneFunctionLibrary.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRScene.h"
#include "OculusXRSceneSubsystem.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMD.h"

View File

@@ -0,0 +1,67 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSceneGlobalMeshComponent.h"
#include "OculusXRSceneModule.h"
#include "OculusXRRoomLayoutManagerComponent.h"
#include "ProceduralMeshComponent.h"
#include "Engine/World.h"
#include "GameFramework/WorldSettings.h"
#include "Materials/MaterialInterface.h"
const FString UOculusXRSceneGlobalMeshComponent::GlobalMeshSemanticLabel = TEXT("GLOBAL_MESH");
UOculusXRSceneGlobalMeshComponent::UOculusXRSceneGlobalMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UOculusXRSceneGlobalMeshComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
bool UOculusXRSceneGlobalMeshComponent::HasCollision() const
{
return Collision;
}
bool UOculusXRSceneGlobalMeshComponent::IsVisible() const
{
return Visible;
}
UClass* UOculusXRSceneGlobalMeshComponent::GetAnchorComponentClass() const
{
UClass* sceneAnchorComponentInstanceClass = SceneAnchorComponent ? SceneAnchorComponent.LoadSynchronous() : nullptr;
return sceneAnchorComponentInstanceClass;
}
void UOculusXRSceneGlobalMeshComponent::CreateMeshComponent(const FOculusXRUInt64& Space, AActor* GlobalMeshAnchor, const UOculusXRRoomLayoutManagerComponent* RoomLayoutManagerComponent) const
{
bool hasCollision = HasCollision();
UProceduralMeshComponent* proceduralMeshComponent = NewObject<UProceduralMeshComponent>(GlobalMeshAnchor);
proceduralMeshComponent->RegisterComponent();
bool bLoaded = RoomLayoutManagerComponent->LoadTriangleMesh(Space.Value, proceduralMeshComponent, hasCollision);
ensure(bLoaded);
UMaterialInterface* refMaterial = Material;
if (refMaterial != nullptr)
{
UE_LOG(LogOculusXRScene, Verbose, TEXT("GLOBAL MESH Set Material %s"), *refMaterial->GetName());
proceduralMeshComponent->SetMaterial(0, refMaterial);
}
if (hasCollision)
{
FName refCollisionProfile = CollisionProfileName.Name;
proceduralMeshComponent->SetCollisionProfileName(refCollisionProfile);
UE_LOG(LogOculusXRScene, Verbose, TEXT("GLOBAL MESH Set Collision Profile %s"), *refCollisionProfile.ToString());
}
GlobalMeshAnchor->AddOwnedComponent(proceduralMeshComponent);
proceduralMeshComponent->AttachToComponent(GlobalMeshAnchor->GetRootComponent(), FAttachmentTransformRules::KeepWorldTransform);
proceduralMeshComponent->SetRelativeLocation(FVector::ZeroVector, false, nullptr, ETeleportType::ResetPhysics);
proceduralMeshComponent->SetVisibility(IsVisible());
const float worldToMeters = GetWorld()->GetWorldSettings()->WorldToMeters;
proceduralMeshComponent->SetRelativeScale3D(FVector(worldToMeters, worldToMeters, worldToMeters));
}

View File

@@ -0,0 +1,44 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSceneModule.h"
#if OCULUS_SCENE_SUPPORTED_PLATFORMS
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRSceneEventHandling.h"
DEFINE_LOG_CATEGORY(LogOculusXRScene);
#define LOCTEXT_NAMESPACE "OculusXRScene"
//-------------------------------------------------------------------------------------------------
// FOculusXRSceneModule
//-------------------------------------------------------------------------------------------------
void FOculusXRSceneModule::StartupModule()
{
if (!GEngine)
{
return;
}
OculusXRHMD::FOculusXRHMD* HMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (!HMD)
{
UE_LOG(LogOculusXRScene, Warning, TEXT("Unable to retrieve OculusXRHMD, cannot add event polling delegates."));
return;
}
HMD->AddEventPollingDelegate(OculusXRHMD::FOculusXRHMDEventPollingDelegate::CreateStatic(&OculusXRScene::FOculusXRSceneEventHandling::OnPollEvent));
}
void FOculusXRSceneModule::ShutdownModule()
{
}
#endif // OCULUS_SCENE_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRSceneModule, OculusXRScene)
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,37 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IOculusXRSceneModule.h"
#define LOCTEXT_NAMESPACE "OculusXRScene"
//-------------------------------------------------------------------------------------------------
// FOculusXRSceneModule
//-------------------------------------------------------------------------------------------------
#if OCULUS_SCENE_SUPPORTED_PLATFORMS
DECLARE_LOG_CATEGORY_EXTERN(LogOculusXRScene, Log, All);
class FOculusXRSceneModule : public IOculusXRSceneModule
{
public:
virtual ~FOculusXRSceneModule() = default;
// IModuleInterface interface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
};
#else // OCULUS_SCENE_SUPPORTED_PLATFORMS
class FOculusXRSceneModule : public FDefaultModuleImpl
{
};
#endif // OCULUS_SCENE_SUPPORTED_PLATFORMS
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,43 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRSceneSubsystem.h"
#include "OculusXRSceneTypes.h"
#include "OculusXRScene.h"
#include "OculusXRSceneDelegates.h"
#include "OculusXRAnchorBPFunctionLibrary.h"
#include "OculusXRHMD.h"
#include "OculusXRHMDRuntimeSettings.h"
UOculusXRSceneSubsystem::UOculusXRSceneSubsystem()
{
}
bool UOculusXRSceneSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
return false;
}
void UOculusXRSceneSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
}
void UOculusXRSceneSubsystem::Deinitialize()
{
}
ETickableTickType UOculusXRSceneSubsystem::GetTickableTickType() const
{
return IsTemplate() ? ETickableTickType::Never : FTickableGameObject::GetTickableTickType();
}
bool UOculusXRSceneSubsystem::IsAllowedToTick() const
{
return false;
}
void UOculusXRSceneSubsystem::Tick(float DeltaTime)
{
}