260 lines
8.2 KiB
C++
260 lines
8.2 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
#include "OculusXRFaceTrackingXR.h"
|
|
#include "OpenXRCore.h"
|
|
#include "IOpenXRHMDModule.h"
|
|
#include "OpenXRHMD.h"
|
|
#include "OculusXRMovementLog.h"
|
|
#include "OpenXR/OculusXROpenXRUtilities.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
|
|
|
namespace XRMovement
|
|
{
|
|
PFN_xrCreateFaceTracker2FB xrCreateFaceTracker2FB = nullptr;
|
|
PFN_xrDestroyFaceTracker2FB xrDestroyFaceTracker2FB = nullptr;
|
|
PFN_xrGetFaceExpressionWeights2FB xrGetFaceExpressionWeights2FB = nullptr;
|
|
|
|
FFaceTrackingXR::FFaceTrackingXR()
|
|
: bExtFaceTrackingSupported(false)
|
|
, bExtFaceTrackingVisemesSupported(false)
|
|
, bVisemesEnabled(false)
|
|
, OpenXRHMD(nullptr)
|
|
, FaceTracker(nullptr)
|
|
{
|
|
CachedFaceState.ExpressionWeights.SetNum(XR_FACE_EXPRESSION2_COUNT_FB);
|
|
CachedFaceState.ExpressionWeightConfidences.SetNum(XR_FACE_CONFIDENCE2_COUNT_FB);
|
|
CachedVisemeState.ExpressionVisemeWeights.SetNum(XR_FACE_TRACKING_VISEME_COUNT_METAX1);
|
|
}
|
|
|
|
FFaceTrackingXR::~FFaceTrackingXR()
|
|
{
|
|
}
|
|
|
|
void FFaceTrackingXR::RegisterAsOpenXRExtension()
|
|
{
|
|
#if defined(WITH_OCULUS_BRANCH)
|
|
// Feature not enabled on Marketplace build. Currently only for the meta fork
|
|
RegisterOpenXRExtensionModularFeature();
|
|
#endif
|
|
}
|
|
|
|
bool FFaceTrackingXR::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
OutExtensions.Add(XR_FB_FACE_TRACKING2_EXTENSION_NAME);
|
|
return true;
|
|
}
|
|
|
|
bool FFaceTrackingXR::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
OutExtensions.Add(XR_METAX1_FACE_TRACKING_VISEMES_EXTENSION_NAME);
|
|
return true;
|
|
}
|
|
|
|
const void* FFaceTrackingXR::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
|
{
|
|
if (InModule != nullptr)
|
|
{
|
|
bExtFaceTrackingSupported = InModule->IsExtensionEnabled(XR_FB_FACE_TRACKING2_EXTENSION_NAME);
|
|
bExtFaceTrackingVisemesSupported = InModule->IsExtensionEnabled(XR_METAX1_FACE_TRACKING_VISEMES_EXTENSION_NAME);
|
|
}
|
|
return InNext;
|
|
}
|
|
|
|
const void* FFaceTrackingXR::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
|
{
|
|
InitOpenXRFunctions(InInstance);
|
|
|
|
OpenXRHMD = (FOpenXRHMD*)GEngine->XRSystem.Get();
|
|
|
|
return InNext;
|
|
}
|
|
|
|
void FFaceTrackingXR::OnDestroySession(XrSession InSession)
|
|
{
|
|
OpenXRHMD = nullptr;
|
|
}
|
|
|
|
void* FFaceTrackingXR::OnWaitFrame(XrSession InSession, void* InNext)
|
|
{
|
|
Update_GameThread(InSession);
|
|
return InNext;
|
|
}
|
|
|
|
XrResult FFaceTrackingXR::StartFaceTracking()
|
|
{
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartFaceTracking] Cannot start face tracking, the instance or session is null."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (!IsFaceTrackingSupported())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartFaceTracking] Cannot start face tracking, face tracking is unsupported."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (FaceTracker != XR_NULL_HANDLE)
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartFaceTracking] Cannot start face tracking, face tracking is already started."));
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
XrFaceTrackerCreateInfo2FB createInfo = { XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB, nullptr };
|
|
|
|
auto result = XRMovement::xrCreateFaceTracker2FB(OpenXRHMD->GetSession(), &createInfo, &FaceTracker);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartFaceTracking] Failed to start face tracking. Result(%d)"), result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
XrResult FFaceTrackingXR::StopFaceTracking()
|
|
{
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopFaceTracking] Cannot stop face tracking, the instance or session is null."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (!IsFaceTrackingSupported())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopFaceTracking] Cannot stop face tracking, face tracking is unsupported."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
XrResult result = XR_SUCCESS;
|
|
if (IsFaceTrackingEnabled())
|
|
{
|
|
result = XRMovement::xrDestroyFaceTracker2FB(FaceTracker);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopFaceTracking] Failed to stop face tracking. Result(%d)"), result);
|
|
}
|
|
}
|
|
|
|
FaceTracker = XR_NULL_HANDLE;
|
|
|
|
return result;
|
|
}
|
|
|
|
XrResult FFaceTrackingXR::GetCachedFaceState(FOculusXRFaceState& OutState)
|
|
{
|
|
if (!IsFaceTrackingEnabled())
|
|
{
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
OutState = CachedFaceState;
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
XrResult FFaceTrackingXR::SetVisemesEnabled(bool enabled)
|
|
{
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SetVisemesEnabled] Cannot change viseme state, the instance of session is null."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
if (!IsFaceTrackingVisemesSupported())
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SetVisemesEnabled] Cannot change viseme state, visemes are not supported."));
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
UE_LOG(LogOculusXRMovement, Log, TEXT("[SetVisemesEnabled] Changing visemes enabled state: %hs"), enabled ? "TRUE" : "FALSE");
|
|
bVisemesEnabled = enabled;
|
|
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
XrResult FFaceTrackingXR::GetCachedVisemeState(FOculusXRFaceVisemesState& OutState)
|
|
{
|
|
if (!IsFaceTrackingEnabled() || !IsFaceTrackingVisemesEnabled())
|
|
{
|
|
return XR_ERROR_VALIDATION_FAILURE;
|
|
}
|
|
|
|
OutState = CachedVisemeState;
|
|
return XR_SUCCESS;
|
|
}
|
|
|
|
void FFaceTrackingXR::InitOpenXRFunctions(XrInstance InInstance)
|
|
{
|
|
// XR_FB_Eye_Tracking_Social
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrCreateFaceTracker2FB", &xrCreateFaceTracker2FB);
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrDestroyFaceTracker2FB", &xrDestroyFaceTracker2FB);
|
|
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetFaceExpressionWeights2FB", &xrGetFaceExpressionWeights2FB);
|
|
}
|
|
|
|
void FFaceTrackingXR::Update_GameThread(XrSession InSession)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession() || !IsFaceTrackingSupported() || !IsFaceTrackingEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
XrFaceExpressionInfo2FB info{ XR_TYPE_FACE_EXPRESSION_INFO2_FB, nullptr };
|
|
info.time = OpenXRHMD->GetDisplayTime();
|
|
|
|
float weightsArray[XR_FACE_EXPRESSION2_COUNT_FB];
|
|
float confidencesArray[XR_FACE_CONFIDENCE2_COUNT_FB];
|
|
|
|
XrFaceExpressionWeights2FB weights{ XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB, nullptr };
|
|
weights.weights = weightsArray;
|
|
weights.weightCount = XR_FACE_EXPRESSION2_COUNT_FB;
|
|
weights.confidences = confidencesArray;
|
|
weights.confidenceCount = XR_FACE_CONFIDENCE2_COUNT_FB;
|
|
|
|
bool useVisemes = IsFaceTrackingVisemesSupported() && IsFaceTrackingVisemesEnabled();
|
|
XrFaceTrackingVisemesMETAX1 faceTrackingVisemes{ XR_TYPE_FACE_TRACKING_VISEMES_METAX1 };
|
|
if (useVisemes)
|
|
{
|
|
weights.next = &faceTrackingVisemes;
|
|
}
|
|
|
|
auto result = XRMovement::xrGetFaceExpressionWeights2FB(FaceTracker, &info, &weights);
|
|
if (XR_FAILED(result))
|
|
{
|
|
UE_LOG(LogOculusXRMovement, Warning, TEXT("[FaceExpressionStateUpdate] Failed to get face tracking state. Result(%d)"), result);
|
|
return;
|
|
}
|
|
|
|
CachedFaceState.bIsValid = (weights.isValid == XR_TRUE);
|
|
CachedFaceState.bIsEyeFollowingBlendshapesValid = (weights.isEyeFollowingBlendshapesValid == XR_TRUE);
|
|
CachedFaceState.Time = OculusXR::FromXrTime(weights.time);
|
|
|
|
switch (weights.dataSource)
|
|
{
|
|
case XR_FACE_TRACKING_DATA_SOURCE2_AUDIO_FB:
|
|
CachedFaceState.DataSource = EFaceTrackingDataSource::Audio;
|
|
break;
|
|
case XR_FACE_TRACKING_DATA_SOURCE2_VISUAL_FB:
|
|
CachedFaceState.DataSource = EFaceTrackingDataSource::Visual;
|
|
break;
|
|
case XR_FACE_TRACKING_DATA_SOURCE_2FB_MAX_ENUM_FB:
|
|
CachedFaceState.DataSource = EFaceTrackingDataSource::MAX;
|
|
break;
|
|
}
|
|
|
|
FMemory::Memcpy(CachedFaceState.ExpressionWeights.GetData(), weights.weights, XR_FACE_EXPRESSION2_COUNT_FB);
|
|
FMemory::Memcpy(CachedFaceState.ExpressionWeightConfidences.GetData(), weights.confidences, XR_FACE_CONFIDENCE2_COUNT_FB);
|
|
|
|
if (useVisemes)
|
|
{
|
|
CachedVisemeState.bIsValid = (faceTrackingVisemes.isValid == XR_TRUE);
|
|
CachedVisemeState.Time = CachedFaceState.Time;
|
|
FMemory::Memcpy(CachedVisemeState.ExpressionVisemeWeights.GetData(), faceTrackingVisemes.visemes, XR_FACE_TRACKING_VISEME_COUNT_METAX1);
|
|
}
|
|
}
|
|
|
|
} // namespace XRMovement
|
|
|
|
#undef LOCTEXT_NAMESPACE
|