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