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