Started the project... finally...
Started the project. This right now only includes Meta XR and Android setup with VR template. More improvements to come!
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class OculusXRScene : ModuleRules
|
||||
{
|
||||
public OculusXRScene(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
bUseUnity = true;
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"OculusXRHMD",
|
||||
"OculusXRAnchors",
|
||||
"OVRPluginXR",
|
||||
"ProceduralMeshComponent",
|
||||
});
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
|
||||
"OculusXRHMD/Private",
|
||||
"OculusXRAnchors/Private"
|
||||
});
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"Runtime/Engine/Classes/Components",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRSceneDelegates.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define OCULUS_SCENE_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 IOculusXRSceneModule : 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 IOculusXRSceneModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<IOculusXRSceneModule>("OculusXRScene");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("OculusXRScene");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRAnchorTypes.h"
|
||||
#include "OculusXRSceneTypes.h"
|
||||
|
||||
namespace OculusXRScene
|
||||
{
|
||||
struct OCULUSXRSCENE_API FOculusXRScene
|
||||
{
|
||||
|
||||
};
|
||||
} // namespace OculusXRScene
|
||||
@@ -0,0 +1,178 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "OculusXRRoomLayoutManagerComponent.h"
|
||||
#include "OculusXRAnchorComponent.h"
|
||||
#include "OculusXRFunctionLibrary.h"
|
||||
#include "OculusXRSceneAnchorComponent.h"
|
||||
#include "OculusXRSceneTypes.h"
|
||||
#include "OculusXRSceneActor.generated.h"
|
||||
|
||||
/** EOculusXRLaunchCaptureFlowWhenMissingScene
|
||||
* Used to dictate whether the actor should launch the Capture Flow application when a scene is not detected on the device.
|
||||
* The Actor will check if a scene capture is either non-existent or invalid (ie. missing walls/ceiling/floor) before checking if Capture Flow
|
||||
* should be launched.
|
||||
*
|
||||
* NEVER: will never launch Flow Capture.
|
||||
* ONCE: will only launch it once. If the actor still doesn't detect that a scene was captured, it will not launch Capture Flow again.
|
||||
* ALWAYS: will always re-launch Flow Capture if a scene was not detected on the device.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum EOculusXRLaunchCaptureFlowWhenMissingScene
|
||||
{
|
||||
NEVER UMETA(DisplayName = "Never"),
|
||||
ONCE UMETA(DisplayName = "Once"),
|
||||
ALWAYS UMETA(DisplayName = "Always")
|
||||
};
|
||||
|
||||
/** FOculusXRSpawnedSceneAnchorProperties
|
||||
* Properties/Components that a spawned scene anchor will use.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FOculusXRSpawnedSceneAnchorProperties
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Spawned Scene Anchor Properties")
|
||||
TSoftClassPtr<UOculusXRSceneAnchorComponent> ActorComponent = TSoftClassPtr<UOculusXRSceneAnchorComponent>(FSoftClassPath(UOculusXRSceneAnchorComponent::StaticClass()));
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Spawned Scene Anchor Properties")
|
||||
TSoftObjectPtr<UStaticMesh> StaticMesh;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Spawned Scene Anchor Properties", Meta = (DeprecatedProperty, DeprecationMessage = "This property is deprecated. Alignment is done automatically at lower level."))
|
||||
bool ForceParallelToFloor = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Spawned Scene Anchor Properties")
|
||||
FVector AddOffset = FVector::ZeroVector;
|
||||
};
|
||||
|
||||
/**
|
||||
* AOculusXRSceneActor
|
||||
* The purpose of this actor is to be able to spawn "scene anchor" actors.
|
||||
*
|
||||
* Each actor type (based on their semantic label) can be configured to be spawned with a specific mesh and actor component.
|
||||
*
|
||||
* Overall, it provides a simple interface to be able to quickly get a captured scene from Capture Flow populated at runtime.
|
||||
* It also provides a basic and flexible template to making use of the OculusAnchorSDK and UOculusXRRoomLayoutManagerComponent
|
||||
* to drive the actor's logic. This removes the need for the developer to implement a system from scratch that makes use of
|
||||
* the native methods and components.
|
||||
*
|
||||
* TLDR:
|
||||
* - This actor populates a captured scene (created in Capture Flow) by spawning child actors with predefined actor and mesh components.
|
||||
* - Can be used as is, or can be derived or modified as needed depending on the application's needs.
|
||||
*/
|
||||
UCLASS(ClassGroup = OculusXRScene)
|
||||
class OCULUSXRSCENE_API AOculusXRSceneActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
void LaunchCaptureFlow();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
bool IsScenePopulated();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor", Meta = (DeprecatedFunction, DeprecationMessage = "Is Room Layout Valid is deprecated and no longer returns any value but true. Please validate your room configuration in the way your application requires."))
|
||||
bool IsRoomLayoutValid();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
void PopulateScene();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
void ClearScene();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
void SetVisibilityToAllSceneAnchors(const bool bIsVisible);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
void SetVisibilityToSceneAnchorsBySemanticLabel(const FString SemanticLabel, const bool bIsVisible);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
TArray<AActor*> GetActorsBySemanticLabel(const FString SemanticLabel);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "OculusXR|Scene Actor")
|
||||
TArray<FOculusXRRoomLayout> GetRoomLayouts() const;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "OculusXR|Scene Actor")
|
||||
TEnumAsByte<EOculusXRLaunchCaptureFlowWhenMissingScene> LauchCaptureFlowWhenMissingScene = EOculusXRLaunchCaptureFlowWhenMissingScene::ALWAYS;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "OculusXR|Scene Actor", meta = (UIMin = 1, ClampMin = 1, UIMax = 1024, ClampMax = 1024))
|
||||
int32 MaxQueries = 64;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "OculusXR|Scene Actor")
|
||||
bool bPopulateSceneOnBeginPlay = true;
|
||||
|
||||
// If true then when the scene model is loaded we will only attempt to populate the room the user is standing in.
|
||||
// Otherwise all rooms and all scene anchors will be loaded.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "OculusXR|Scene Actor")
|
||||
bool bActiveRoomOnly = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "OculusXR|Scene Actor")
|
||||
TMap<FString, FOculusXRSpawnedSceneAnchorProperties> ScenePlaneSpawnedSceneAnchorProperties;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "OculusXR|Scene Actor")
|
||||
TMap<FString, FOculusXRSpawnedSceneAnchorProperties> SceneVolumeSpawnedSceneAnchorProperties;
|
||||
|
||||
|
||||
public:
|
||||
AOculusXRSceneActor(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(EEndPlayReason::Type Reason) override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual void PostLoad() override;
|
||||
|
||||
private:
|
||||
EOculusXRAnchorResult::Type QueryAllRooms();
|
||||
void RoomLayoutQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults);
|
||||
|
||||
EOculusXRAnchorResult::Type QueryRoomUUIDs(const FOculusXRUInt64 RoomSpaceID, const TArray<FOculusXRUUID>& RoomUUIDs);
|
||||
void SceneRoomQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults, const FOculusXRUInt64 RoomSpaceID);
|
||||
|
||||
void StartSingleRoomQuery(FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout);
|
||||
EOculusXRAnchorResult::Type QueryFloorForActiveRoom(FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout);
|
||||
void ActiveRoomFloorQueryComplete(EOculusXRAnchorResult::Type AnchorResult, const TArray<FOculusXRSpaceQueryResult>& QueryResults, FOculusXRUInt64 RoomSpaceID, FOculusXRRoomLayout RoomLayout);
|
||||
bool PointInPolygon2D(FVector2f PointToTest, const TArray<FVector2f>& PolyVerts) const;
|
||||
|
||||
void GetSemanticClassifications(uint64 Space, TArray<FString>& OutSemanticLabels) const;
|
||||
|
||||
// Scene capture event handler
|
||||
void SceneCaptureComplete_Handler(FOculusXRUInt64 RequestId, bool bResult);
|
||||
|
||||
// Launches Capture Flow if (based on LauchCaptureFlowWhenMissingScene member value)
|
||||
void LaunchCaptureFlowIfNeeded();
|
||||
|
||||
// Resets states of the Actor
|
||||
void ResetStates();
|
||||
|
||||
// Validates UUID
|
||||
bool IsValidUuid(const FOculusXRUUID& Uuid);
|
||||
|
||||
// Helper method to spawn an actor for anchor
|
||||
AActor* SpawnActorWithSceneComponent(const FOculusXRUInt64& Space, const FOculusXRUInt64& RoomSpaceID, const TArray<FString>& SemanticClassifications, UClass* sceneAnchorComponentInstanceClass);
|
||||
|
||||
// Spawns a scene anchor
|
||||
AActor* SpawnOrUpdateSceneAnchor(AActor* Anchor, const FOculusXRUInt64& Space, const FOculusXRUInt64& RoomSpaceID, const FVector& BoundedPos, const FVector& BoundedSize, const TArray<FString>& SemanticClassifications, const EOculusXRSpaceComponentType AnchorComponentType);
|
||||
|
||||
|
||||
// Components for room layout and spatial anchors functionalities
|
||||
UOculusXRRoomLayoutManagerComponent* RoomLayoutManagerComponent = nullptr;
|
||||
|
||||
class UOculusXRSceneGlobalMeshComponent* SceneGlobalMeshComponent = nullptr;
|
||||
|
||||
// Whether Capture Flow was already launched once
|
||||
bool bCaptureFlowWasLaunched;
|
||||
|
||||
// Whether last room layout was valid
|
||||
bool bRoomLayoutIsValid;
|
||||
|
||||
// Whether we found a captured scene
|
||||
bool bFoundCapturedScene;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TMap<FOculusXRUInt64, FOculusXRRoomLayout> RoomLayouts;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRAnchorComponent.h"
|
||||
#include "OculusXRSceneAnchorComponent.generated.h"
|
||||
|
||||
UCLASS(meta = (DisplayName = "OculusXR Scene Anchor Component", BlueprintSpawnableComponent))
|
||||
class OCULUSXRSCENE_API UOculusXRSceneAnchorComponent : public UOculusXRAnchorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
public:
|
||||
UOculusXRSceneAnchorComponent(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "OculusXR|Scene Anchor Component")
|
||||
TArray<FString> SemanticClassifications;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "OculusXR|Scene Anchor Component")
|
||||
FOculusXRUInt64 RoomSpaceID = 0;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "OculusXRSceneTypes.h"
|
||||
#include "Delegates/Delegate.h"
|
||||
|
||||
class FOculusXRSceneEventDelegates
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Subsystems/EngineSubsystem.h>
|
||||
#include "OculusXRSceneTypes.h"
|
||||
#include "OculusXRSceneEventDelegates.generated.h"
|
||||
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UOculusXRSceneEventDelegates : public UEngineSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRAnchorTypes.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "OculusXRSceneFunctionLibrary.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class OCULUSXRSCENE_API UOculusXRSceneFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRSceneAnchorComponent.h"
|
||||
#include "Engine/CollisionProfile.h"
|
||||
#include "OculusXRRoomLayoutManagerComponent.h"
|
||||
#include "OculusXRSceneGlobalMeshComponent.generated.h"
|
||||
|
||||
class UMaterialInterface;
|
||||
|
||||
UCLASS(meta = (DisplayName = "OculusXR Scene Global Mesh Component", BlueprintSpawnableComponent))
|
||||
class OCULUSXRSCENE_API UOculusXRSceneGlobalMeshComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
public:
|
||||
UOculusXRSceneGlobalMeshComponent(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
void CreateMeshComponent(const FOculusXRUInt64& Space, AActor* GlobalMeshAnchor, const UOculusXRRoomLayoutManagerComponent* RoomLayoutManagerComponent) const;
|
||||
|
||||
static const FString GlobalMeshSemanticLabel;
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OculusXR")
|
||||
bool Collision = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OculusXR")
|
||||
FCollisionProfileName CollisionProfileName;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OculusXR")
|
||||
bool Visible = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OculusXR")
|
||||
UMaterialInterface* Material = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OculusXR")
|
||||
TSoftClassPtr<UOculusXRSceneAnchorComponent> SceneAnchorComponent = TSoftClassPtr<UOculusXRSceneAnchorComponent>(FSoftClassPath(UOculusXRSceneAnchorComponent::StaticClass()));
|
||||
|
||||
public:
|
||||
bool HasCollision() const;
|
||||
|
||||
bool IsVisible() const;
|
||||
|
||||
UClass* GetAnchorComponentClass() const;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRSceneTypes.h"
|
||||
#include <Subsystems/GameInstanceSubsystem.h>
|
||||
#include <Tickable.h>
|
||||
|
||||
#include "OculusXRSceneSubsystem.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UOculusXRSceneSubsystem : public UGameInstanceSubsystem, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UOculusXRSceneSubsystem();
|
||||
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
// FTickableGameObject implementation Begin
|
||||
virtual ETickableTickType GetTickableTickType() const override;
|
||||
virtual bool IsAllowedToTick() const override final;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(UOculusXRSceneSubsystem, STATGROUP_Tickables); }
|
||||
// FTickableGameObject implementation End
|
||||
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRSceneTypes.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct FSceneTypesPlaceholder
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user