193 lines
5.5 KiB
C++
193 lines
5.5 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
#include "OculusXREyeTrackingXR.h"
|
|
#include "OpenXRCore.h"
|
|
#include "IOpenXRHMDModule.h"
|
|
#include "OpenXRHMD.h"
|
|
#include "OculusXRMovementLog.h"
|
|
#include "OpenXR/OculusXROpenXRUtilities.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
|
|
|
namespace XRMovement
|
|
{
|
|
PFN_xrCreateEyeTrackerFB xrCreateEyeTrackerFB = nullptr;
|
|
PFN_xrDestroyEyeTrackerFB xrDestroyEyeTrackerFB = nullptr;
|
|
PFN_xrGetEyeGazesFB xrGetEyeGazesFB = nullptr;
|
|
|
|
FEyeTrackingXR::FEyeTrackingXR()
|
|
: bExtEyeTrackingEnabled(false)
|
|
, OpenXRHMD(nullptr)
|
|
, EyeTracker(nullptr)
|
|
{
|
|
CachedEyeState.EyeGazes.SetNum(2);
|
|
}
|
|
|
|
FEyeTrackingXR::~FEyeTrackingXR()
|
|
{
|
|
}
|
|
|
|
void FEyeTrackingXR::RegisterAsOpenXRExtension()
|
|
{
|
|
#if defined(WITH_OCULUS_BRANCH)
|
|
// Feature not enabled on Marketplace build. Currently only for the meta fork
|
|
RegisterOpenXRExtensionModularFeature();
|
|
#endif
|
|
}
|
|
|
|
bool FEyeTrackingXR::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
OutExtensions.Add(XR_FB_EYE_TRACKING_SOCIAL_EXTENSION_NAME);
|
|
return true;
|
|
}
|
|
|
|
bool FEyeTrackingXR::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
const void* FEyeTrackingXR::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
|
{
|
|
if (InModule != nullptr)
|
|
{
|
|
bExtEyeTrackingEnabled = InModule->IsExtensionEnabled(XR_FB_EYE_TRACKING_SOCIAL_EXTENSION_NAME);
|
|
}
|
|
return InNext;
|
|
}
|
|
|
|
const void* FEyeTrackingXR::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
|
{
|
|
InitOpenXRFunctions(InInstance);
|
|
|
|
OpenXRHMD = (FOpenXRHMD*)GEngine->XRSystem.Get();
|
|
|
|
return InNext;
|
|
}
|
|
|
|
void FEyeTrackingXR::OnDestroySession(XrSession InSession)
|
|
{
|
|
OpenXRHMD = nullptr;
|
|
}
|
|
|
|
void* FEyeTrackingXR::OnWaitFrame(XrSession InSession, void* InNext)
|
|
{
|
|
Update_GameThread(InSession);
|
|
return InNext;
|
|
}
|
|
|
|
XrResult FEyeTrackingXR::StartEyeTracking()
|
|
{
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartEyeTracking] Cannot start eye tracking, the instance or session is null."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (!IsEyeTrackingSupported())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartEyeTracking] Cannot start eye tracking, body tracking is unsupported."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (EyeTracker != XR_NULL_HANDLE)
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartEyeTracking] Cannot start eye tracking, body tracking is already started."));
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
XrEyeTrackerCreateInfoFB createInfo = { XR_TYPE_EYE_TRACKER_CREATE_INFO_FB, nullptr };
|
|
|
|
auto result = XRMovement::xrCreateEyeTrackerFB(OpenXRHMD->GetSession(), &createInfo, &EyeTracker);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartEyeTracking] Failed to start eye tracking. Result(%d)"), result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
XrResult FEyeTrackingXR::StopEyeTracking()
|
|
{
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopEyeTracking] Cannot stop eye tracking, the instance or session is null."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (!IsEyeTrackingSupported())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopEyeTracking] Cannot stop eye tracking, eye tracking is unsupported."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
XrResult result = XR_SUCCESS;
|
|
if (IsEyeTrackingEnabled())
|
|
{
|
|
result = XRMovement::xrDestroyEyeTrackerFB(EyeTracker);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopEyeTracking] Failed to stop eye tracking. Result(%d)"), result);
|
|
}
|
|
}
|
|
|
|
EyeTracker = XR_NULL_HANDLE;
|
|
|
|
return result;
|
|
}
|
|
|
|
XrResult FEyeTrackingXR::GetCachedEyeState(FOculusXREyeGazesState& OutState)
|
|
{
|
|
if (!IsEyeTrackingEnabled())
|
|
{
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
OutState = CachedEyeState;
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
void FEyeTrackingXR::InitOpenXRFunctions(XrInstance InInstance)
|
|
{
|
|
// XR_FB_Eye_Tracking_Social
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrCreateEyeTrackerFB", &xrCreateEyeTrackerFB);
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrDestroyEyeTrackerFB", &xrDestroyEyeTrackerFB);
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetEyeGazesFB", &xrGetEyeGazesFB);
|
|
}
|
|
|
|
void FEyeTrackingXR::Update_GameThread(XrSession InSession)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession() || !IsEyeTrackingSupported() || !IsEyeTrackingEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
XrEyeGazesInfoFB info{ XR_TYPE_EYE_GAZES_INFO_FB, nullptr };
|
|
info.baseSpace = OpenXRHMD->GetTrackingSpace();
|
|
info.time = OpenXRHMD->GetDisplayTime();
|
|
|
|
XrEyeGazesFB gazes{ XR_TYPE_EYE_GAZES_FB, nullptr };
|
|
|
|
auto result = XRMovement::xrGetEyeGazesFB(EyeTracker, &info, &gazes);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[EyeGazeStateUpdate] Failed to get gazes state. Result(%d)"), result);
|
|
return;
|
|
}
|
|
|
|
auto ApplyGazeToUEType = [this](const XrEyeGazeFB& xrGaze, FOculusXREyeGazeState& outUEGaze) {
|
|
outUEGaze.bIsValid = static_cast<bool>(xrGaze.isValid);
|
|
outUEGaze.Confidence = xrGaze.gazeConfidence;
|
|
outUEGaze.Orientation = FRotator(ToFQuat(xrGaze.gazePose.orientation));
|
|
outUEGaze.Position = ToFVector(xrGaze.gazePose.position) * OpenXRHMD->GetWorldToMetersScale();
|
|
};
|
|
|
|
ApplyGazeToUEType(gazes.gaze[0], CachedEyeState.EyeGazes[0]);
|
|
ApplyGazeToUEType(gazes.gaze[1], CachedEyeState.EyeGazes[1]);
|
|
}
|
|
|
|
} // namespace XRMovement
|
|
|
|
#undef LOCTEXT_NAMESPACE
|