Android build settings + metaxr
This commit is contained in:
@@ -0,0 +1,474 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRBodytrackingXR.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "IOpenXRHMDModule.h"
|
||||
#include "OpenXRHMD.h"
|
||||
#include "OculusXRMovementLog.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
namespace XRMovement
|
||||
{
|
||||
PFN_xrCreateBodyTrackerFB xrCreateBodyTrackerFB = nullptr;
|
||||
PFN_xrDestroyBodyTrackerFB xrDestroyBodyTrackerFB = nullptr;
|
||||
PFN_xrLocateBodyJointsFB xrLocateBodyJointsFB = nullptr;
|
||||
PFN_xrGetBodySkeletonFB xrGetBodySkeletonFB = nullptr;
|
||||
PFN_xrRequestBodyTrackingFidelityMETA xrRequestBodyTrackingFidelityMETA = nullptr;
|
||||
PFN_xrSuggestBodyTrackingCalibrationOverrideMETA xrSuggestBodyTrackingCalibrationOverrideMETA = nullptr;
|
||||
PFN_xrResetBodyTrackingCalibrationMETA xrResetBodyTrackingCalibrationMETA = nullptr;
|
||||
|
||||
FBodyTrackingXR::FBodyTrackingXR()
|
||||
: bExtBodyTrackingEnabled(false)
|
||||
, bExtBodyTrackingFullBodyEnabled(false)
|
||||
, bExtBodyTrackingFidelityEnabled(false)
|
||||
, bExtBodyTrackingCalibrationEnabled(false)
|
||||
, OpenXRHMD(nullptr)
|
||||
, BodyTracker(nullptr)
|
||||
, FullBodyTracking(false)
|
||||
{
|
||||
}
|
||||
|
||||
FBodyTrackingXR::~FBodyTrackingXR()
|
||||
{
|
||||
}
|
||||
|
||||
void FBodyTrackingXR::RegisterAsOpenXRExtension()
|
||||
{
|
||||
#if defined(WITH_OCULUS_BRANCH)
|
||||
// Feature not enabled on Marketplace build. Currently only for the meta fork
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FBodyTrackingXR::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_FB_BODY_TRACKING_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FBodyTrackingXR::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_META_BODY_TRACKING_FIDELITY_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_META_BODY_TRACKING_CALIBRATION_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FBodyTrackingXR::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
||||
{
|
||||
if (InModule != nullptr)
|
||||
{
|
||||
bExtBodyTrackingEnabled = InModule->IsExtensionEnabled(XR_FB_BODY_TRACKING_EXTENSION_NAME);
|
||||
bExtBodyTrackingFullBodyEnabled = InModule->IsExtensionEnabled(XR_META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME);
|
||||
bExtBodyTrackingFidelityEnabled = InModule->IsExtensionEnabled(XR_META_BODY_TRACKING_FIDELITY_EXTENSION_NAME);
|
||||
bExtBodyTrackingCalibrationEnabled = InModule->IsExtensionEnabled(XR_META_BODY_TRACKING_CALIBRATION_EXTENSION_NAME);
|
||||
|
||||
UE_LOG(LogOculusXRMovement, Log, TEXT("[Body Tracking] Extensions available: Tracking: %hs -- Full Body: %hs -- Fidelity: %hs -- Calibration: %hs"),
|
||||
bExtBodyTrackingEnabled ? "ENABLED" : "DISABLED",
|
||||
bExtBodyTrackingFullBodyEnabled ? "ENABLED" : "DISABLED",
|
||||
bExtBodyTrackingFidelityEnabled ? "ENABLED" : "DISABLED",
|
||||
bExtBodyTrackingCalibrationEnabled ? "ENABLED" : "DISABLED");
|
||||
}
|
||||
return InNext;
|
||||
}
|
||||
|
||||
const void* FBodyTrackingXR::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
InitOpenXRFunctions(InInstance);
|
||||
|
||||
OpenXRHMD = (FOpenXRHMD*)GEngine->XRSystem.Get();
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FBodyTrackingXR::OnDestroySession(XrSession InSession)
|
||||
{
|
||||
OpenXRHMD = nullptr;
|
||||
}
|
||||
|
||||
void* FBodyTrackingXR::OnWaitFrame(XrSession InSession, void* InNext)
|
||||
{
|
||||
Update_GameThread(InSession);
|
||||
return InNext;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::StartBodyTracking()
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTracking] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsBodyTrackingSupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTracking] Body tracking is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (BodyTracker != XR_NULL_HANDLE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Log, TEXT("[StartBodyTracking] Body tracking is already started."));
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrBodyTrackerCreateInfoFB createInfo = { XR_TYPE_BODY_TRACKER_CREATE_INFO_FB };
|
||||
createInfo.next = nullptr;
|
||||
createInfo.bodyJointSet = XR_BODY_JOINT_SET_DEFAULT_FB;
|
||||
|
||||
auto result = XRMovement::xrCreateBodyTrackerFB(OpenXRHMD->GetSession(), &createInfo, &BodyTracker);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTracking] Body tracking failed to start. Result: %d"), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::StartBodyTrackingByJointSet(EOculusXRBodyJointSet jointSet)
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTrackingByJointSet] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsBodyTrackingSupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTrackingByJointSet] Body tracking is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (BodyTracker != XR_NULL_HANDLE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Log, TEXT("[StartBodyTrackingByJointSet] Body tracking is already started."));
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrBodyTrackerCreateInfoFB createInfo = { XR_TYPE_BODY_TRACKER_CREATE_INFO_FB };
|
||||
createInfo.next = nullptr;
|
||||
|
||||
switch (jointSet)
|
||||
{
|
||||
case EOculusXRBodyJointSet::UpperBody:
|
||||
createInfo.bodyJointSet = XR_BODY_JOINT_SET_DEFAULT_FB;
|
||||
break;
|
||||
case EOculusXRBodyJointSet::FullBody:
|
||||
createInfo.bodyJointSet = XR_BODY_JOINT_SET_FULL_BODY_META;
|
||||
if (!IsFullBodySupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTrackingByJointSet] Full body tracking is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTrackingByJointSet] Unknown body tracking joint set."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
auto result = XRMovement::xrCreateBodyTrackerFB(OpenXRHMD->GetSession(), &createInfo, &BodyTracker);
|
||||
if XR_FAILED (result)
|
||||
{
|
||||
BodyTracker = XR_NULL_HANDLE;
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StartBodyTrackingByJointSet] Body tracking failed to start. Result: %d"), result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
FullBodyTracking = (jointSet == EOculusXRBodyJointSet::FullBody);
|
||||
}
|
||||
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::StopBodyTracking()
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopBodyTracking] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsBodyTrackingSupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopBodyTracking] Body tracking is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
XrResult result = XR_SUCCESS;
|
||||
if (IsBodyTrackingEnabled())
|
||||
{
|
||||
result = XRMovement::xrDestroyBodyTrackerFB(BodyTracker);
|
||||
if XR_FAILED (result)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[StopBodyTracking] Body tracking failed to stop. Result: %d"), result);
|
||||
}
|
||||
}
|
||||
|
||||
BodyTracker = XR_NULL_HANDLE;
|
||||
FullBodyTracking = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::GetCachedBodyState(FOculusXRBodyState& OutState)
|
||||
{
|
||||
if (!IsBodyTrackingEnabled())
|
||||
{
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
OutState = CachedBodyState;
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::GetBodySkeleton(FOculusXRBodySkeleton& OutSkeleton)
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[GetBodySkeleton] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
int jointCount = IsFullBodyTrackingEnabled() ? (int)XR_FULL_BODY_JOINT_COUNT_META : (int)XR_BODY_JOINT_COUNT_FB;
|
||||
|
||||
// Allocate enough memory for the larger joint set
|
||||
static_assert((int)XR_FULL_BODY_JOINT_COUNT_META >= (int)XR_BODY_JOINT_COUNT_FB);
|
||||
XrBodySkeletonJointFB joints[XR_FULL_BODY_JOINT_COUNT_META];
|
||||
|
||||
XrBodySkeletonFB bodySkeleton = { XR_TYPE_BODY_SKELETON_FB };
|
||||
bodySkeleton.jointCount = jointCount;
|
||||
bodySkeleton.joints = joints;
|
||||
|
||||
auto result = XRMovement::xrGetBodySkeletonFB(BodyTracker, &bodySkeleton);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
OutSkeleton.NumBones = bodySkeleton.jointCount;
|
||||
for (uint32 i = 0; i < bodySkeleton.jointCount; ++i)
|
||||
{
|
||||
XrBodySkeletonJointFB bone = bodySkeleton.joints[i];
|
||||
XrPosef bonePose = bone.pose;
|
||||
|
||||
FOculusXRBodySkeletonBone& OculusXRBone = OutSkeleton.Bones[i];
|
||||
|
||||
OculusXRBone.Orientation = FRotator(ToFQuat(bonePose.orientation));
|
||||
OculusXRBone.Position = ToFVector(bonePose.position) * OpenXRHMD->GetWorldToMetersScale();
|
||||
|
||||
if (bone.parentJoint == XR_BODY_JOINT_NONE_FB)
|
||||
{
|
||||
OculusXRBone.ParentBoneIndex = EOculusXRBoneID::None;
|
||||
}
|
||||
else
|
||||
{
|
||||
OculusXRBone.ParentBoneIndex = static_cast<EOculusXRBoneID>(bone.parentJoint);
|
||||
}
|
||||
|
||||
OculusXRBone.BoneId = static_cast<EOculusXRBoneID>(bone.joint);
|
||||
}
|
||||
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::RequestBodyTrackingFidelity(EOculusXRBodyTrackingFidelity Fidelity)
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[RequestBodyTrackingFidelity] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsFidelitySupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[RequestBodyTrackingFidelity] Fidelity is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (BodyTracker == XR_NULL_HANDLE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[RequestBodyTrackingFidelity] Body tracking is not started."));
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrBodyTrackingFidelityMETA fidelity;
|
||||
switch (Fidelity)
|
||||
{
|
||||
case EOculusXRBodyTrackingFidelity::High:
|
||||
fidelity = XR_BODY_TRACKING_FIDELITY_HIGH_META;
|
||||
break;
|
||||
case EOculusXRBodyTrackingFidelity::Low:
|
||||
fidelity = XR_BODY_TRACKING_FIDELITY_LOW_META;
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[RequestBodyTrackingFidelity] Invalid fidelity level."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
XrResult result = xrRequestBodyTrackingFidelityMETA(BodyTracker, fidelity);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[RequestBodyTrackingFidelity] Failed to request fidelity level. Result: %d"), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::ResetBodyTrackingFidelity()
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[ResetBodyTrackingFidelity] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsFidelitySupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[ResetBodyTrackingFidelity] Fidelity is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (BodyTracker == XR_NULL_HANDLE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[ResetBodyTrackingFidelity] Body tracking is not started."));
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrResult result = xrResetBodyTrackingCalibrationMETA(BodyTracker);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[ResetBodyTrackingFidelity] Failed to request fidelity level. Result: %d"), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XrResult FBodyTrackingXR::SuggestBodyTrackingCalibrationOverride(float height)
|
||||
{
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SuggestBodyTrackingCalibrationOverride] XR state is invalid."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsCalibrationSupported())
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SuggestBodyTrackingCalibrationOverride] Calibration is unsupported."));
|
||||
return XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
if (BodyTracker == XR_NULL_HANDLE)
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SuggestBodyTrackingCalibrationOverride] Body tracking is not started."));
|
||||
return XR_SUCCESS;
|
||||
}
|
||||
|
||||
XrBodyTrackingCalibrationInfoMETA xrCalibrationInfo = { XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META };
|
||||
xrCalibrationInfo.bodyHeight = height;
|
||||
|
||||
XrResult result = xrSuggestBodyTrackingCalibrationOverrideMETA(BodyTracker, &xrCalibrationInfo);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[SuggestBodyTrackingCalibrationOverride] failed to suggest calibration override! Result: %d"), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void FBodyTrackingXR::InitOpenXRFunctions(XrInstance InInstance)
|
||||
{
|
||||
// XR_FB_Body_Tracking
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrCreateBodyTrackerFB", &xrCreateBodyTrackerFB);
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrDestroyBodyTrackerFB", &xrDestroyBodyTrackerFB);
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrLocateBodyJointsFB", &xrLocateBodyJointsFB);
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetBodySkeletonFB", &xrGetBodySkeletonFB);
|
||||
|
||||
// XR_META_body_tracking_fidelity
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrRequestBodyTrackingFidelityMETA", &xrRequestBodyTrackingFidelityMETA);
|
||||
|
||||
// XR_META_body_tracking_calibration
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrSuggestBodyTrackingCalibrationOverrideMETA", &xrSuggestBodyTrackingCalibrationOverrideMETA);
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrResetBodyTrackingCalibrationMETA", &xrResetBodyTrackingCalibrationMETA);
|
||||
}
|
||||
|
||||
void FBodyTrackingXR::Update_GameThread(XrSession InSession)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
if (!OpenXRHMD || !OpenXRHMD->GetInstance() || !OpenXRHMD->GetSession() || !IsBodyTrackingSupported() || !IsBodyTrackingEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(XR_FULL_BODY_JOINT_COUNT_META == static_cast<int>(EOculusXRBoneID::COUNT), "The size of the XR Bone ID enum should be the same as the EOculusXRBoneID count.");
|
||||
|
||||
int jointCount = IsFullBodyTrackingEnabled() ? (int)XR_FULL_BODY_JOINT_COUNT_META : (int)XR_BODY_JOINT_COUNT_FB;
|
||||
CachedBodyState.Joints.SetNum(static_cast<int>(XR_FULL_BODY_JOINT_COUNT_META));
|
||||
|
||||
XrBodyJointsLocateInfoFB info = { XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB };
|
||||
info.baseSpace = OpenXRHMD->GetTrackingSpace();
|
||||
info.time = OpenXRHMD->GetDisplayTime();
|
||||
|
||||
XrBodyJointLocationsFB locations = { XR_TYPE_BODY_JOINT_LOCATIONS_FB };
|
||||
XrBodyJointLocationFB jointLocations[XR_FULL_BODY_JOINT_COUNT_META];
|
||||
|
||||
locations.jointCount = jointCount;
|
||||
locations.jointLocations = jointLocations;
|
||||
|
||||
XrBodyTrackingCalibrationStatusMETA calibrationStatus = { XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META };
|
||||
calibrationStatus.next = XR_NULL_HANDLE;
|
||||
if (IsCalibrationSupported())
|
||||
{
|
||||
OculusXR::XRAppendToChain(
|
||||
reinterpret_cast<XrBaseOutStructure*>(&calibrationStatus), reinterpret_cast<XrBaseOutStructure*>(&locations));
|
||||
}
|
||||
|
||||
XrBodyTrackingFidelityStatusMETA fidelityStatus = { XR_TYPE_BODY_TRACKING_FIDELITY_STATUS_META };
|
||||
fidelityStatus.next = XR_NULL_HANDLE;
|
||||
if (IsFidelitySupported())
|
||||
{
|
||||
OculusXR::XRAppendToChain(
|
||||
reinterpret_cast<XrBaseOutStructure*>(&fidelityStatus), reinterpret_cast<XrBaseOutStructure*>(&locations));
|
||||
}
|
||||
|
||||
auto result = XRMovement::xrLocateBodyJointsFB(BodyTracker, &info, &locations);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOculusXRMovement, Warning, TEXT("[LocateBodyJoints] Failed to locate joints! Result: %d"), result);
|
||||
return;
|
||||
}
|
||||
|
||||
CachedBodyState.IsActive = (bool)locations.isActive;
|
||||
CachedBodyState.Confidence = locations.confidence;
|
||||
CachedBodyState.SkeletonChangedCount = locations.skeletonChangedCount;
|
||||
CachedBodyState.Time = locations.time * 1e-9; // FromXrTime
|
||||
|
||||
for (int i = 0; i < jointCount; ++i)
|
||||
{
|
||||
XrBodyJointLocationFB jointLocation = locations.jointLocations[i];
|
||||
XrPosef jointPose = jointLocation.pose;
|
||||
|
||||
FOculusXRBodyJoint& OculusXRBodyJoint = CachedBodyState.Joints[i];
|
||||
OculusXRBodyJoint.LocationFlags = jointLocation.locationFlags;
|
||||
OculusXRBodyJoint.bIsValid = jointLocation.locationFlags & (XRSpaceFlags::XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XRSpaceFlags::XR_SPACE_LOCATION_POSITION_VALID_BIT);
|
||||
OculusXRBodyJoint.Orientation = FRotator(ToFQuat(jointPose.orientation));
|
||||
OculusXRBodyJoint.Position = ToFVector(jointPose.position) * OpenXRHMD->GetWorldToMetersScale();
|
||||
}
|
||||
|
||||
// If using less joints than the max count we can just set the remaining joints to null
|
||||
if (jointCount < CachedBodyState.Joints.Num())
|
||||
{
|
||||
for (int i = jointCount; i < CachedBodyState.Joints.Num(); ++i)
|
||||
{
|
||||
CachedBodyState.Joints[i].bIsValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace XRMovement
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRMovementXRIncludes.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OculusXRMovementTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
class FOpenXRHMD;
|
||||
|
||||
namespace XRMovement
|
||||
{
|
||||
extern PFN_xrCreateBodyTrackerFB xrCreateBodyTrackerFB;
|
||||
extern PFN_xrDestroyBodyTrackerFB xrDestroyBodyTrackerFB;
|
||||
extern PFN_xrLocateBodyJointsFB xrLocateBodyJointsFB;
|
||||
extern PFN_xrGetBodySkeletonFB xrGetBodySkeletonFB;
|
||||
extern PFN_xrRequestBodyTrackingFidelityMETA xrRequestBodyTrackingFidelityMETA;
|
||||
extern PFN_xrSuggestBodyTrackingCalibrationOverrideMETA xrSuggestBodyTrackingCalibrationOverrideMETA;
|
||||
extern PFN_xrResetBodyTrackingCalibrationMETA xrResetBodyTrackingCalibrationMETA;
|
||||
|
||||
class FBodyTrackingXR : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
// IOculusXROpenXRHMDPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual void OnDestroySession(XrSession InSession) override;
|
||||
virtual void* OnWaitFrame(XrSession InSession, void* InNext) override;
|
||||
|
||||
public:
|
||||
FBodyTrackingXR();
|
||||
virtual ~FBodyTrackingXR();
|
||||
void RegisterAsOpenXRExtension();
|
||||
|
||||
bool IsBodyTrackingSupported() const { return bExtBodyTrackingEnabled; }
|
||||
bool IsFullBodySupported() const { return bExtBodyTrackingFullBodyEnabled; }
|
||||
bool IsFidelitySupported() const { return bExtBodyTrackingFidelityEnabled; }
|
||||
bool IsCalibrationSupported() const { return bExtBodyTrackingCalibrationEnabled; }
|
||||
|
||||
bool IsBodyTrackingEnabled() const { return BodyTracker != XR_NULL_HANDLE; }
|
||||
bool IsFullBodyTrackingEnabled() const { return FullBodyTracking; }
|
||||
|
||||
XrResult StartBodyTracking();
|
||||
XrResult StartBodyTrackingByJointSet(EOculusXRBodyJointSet jointSet);
|
||||
XrResult StopBodyTracking();
|
||||
XrResult GetCachedBodyState(FOculusXRBodyState& OutState);
|
||||
XrResult GetBodySkeleton(FOculusXRBodySkeleton& OutSkeleton);
|
||||
|
||||
XrResult RequestBodyTrackingFidelity(EOculusXRBodyTrackingFidelity Fidelity);
|
||||
XrResult ResetBodyTrackingFidelity();
|
||||
XrResult SuggestBodyTrackingCalibrationOverride(float height);
|
||||
|
||||
private:
|
||||
void InitOpenXRFunctions(XrInstance InInstance);
|
||||
void Update_GameThread(XrSession InSession);
|
||||
|
||||
bool bExtBodyTrackingEnabled;
|
||||
bool bExtBodyTrackingFullBodyEnabled;
|
||||
bool bExtBodyTrackingFidelityEnabled;
|
||||
bool bExtBodyTrackingCalibrationEnabled;
|
||||
|
||||
FOpenXRHMD* OpenXRHMD;
|
||||
FOculusXRBodyState CachedBodyState;
|
||||
XrBodyTrackerFB BodyTracker = XR_NULL_HANDLE;
|
||||
bool FullBodyTracking{ false };
|
||||
};
|
||||
|
||||
} // namespace XRMovement
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,192 @@
|
||||
// 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
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRMovementXRIncludes.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OculusXRMovementTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
class FOpenXRHMD;
|
||||
|
||||
namespace XRMovement
|
||||
{
|
||||
extern PFN_xrCreateEyeTrackerFB xrCreateEyeTrackerFB;
|
||||
extern PFN_xrDestroyEyeTrackerFB xrDestroyEyeTrackerFB;
|
||||
extern PFN_xrGetEyeGazesFB xrGetEyeGazesFB;
|
||||
|
||||
class FEyeTrackingXR : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
// IOculusXROpenXRHMDPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual void OnDestroySession(XrSession InSession) override;
|
||||
virtual void* OnWaitFrame(XrSession InSession, void* InNext) override;
|
||||
|
||||
public:
|
||||
FEyeTrackingXR();
|
||||
virtual ~FEyeTrackingXR();
|
||||
void RegisterAsOpenXRExtension();
|
||||
|
||||
bool IsEyeTrackingSupported() const { return bExtEyeTrackingEnabled; }
|
||||
bool IsEyeTrackingEnabled() const { return EyeTracker != XR_NULL_HANDLE; }
|
||||
|
||||
XrResult StartEyeTracking();
|
||||
XrResult StopEyeTracking();
|
||||
XrResult GetCachedEyeState(FOculusXREyeGazesState& OutState);
|
||||
|
||||
private:
|
||||
void InitOpenXRFunctions(XrInstance InInstance);
|
||||
void Update_GameThread(XrSession InSession);
|
||||
|
||||
bool bExtEyeTrackingEnabled;
|
||||
|
||||
FOpenXRHMD* OpenXRHMD;
|
||||
FOculusXREyeGazesState CachedEyeState;
|
||||
XrEyeTrackerFB EyeTracker = XR_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace XRMovement
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,259 @@
|
||||
// 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
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRMovementXRIncludes.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OculusXRMovementTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRMovement"
|
||||
|
||||
class FOpenXRHMD;
|
||||
|
||||
namespace XRMovement
|
||||
{
|
||||
extern PFN_xrCreateFaceTracker2FB xrCreateEyeTracker2FB;
|
||||
extern PFN_xrDestroyFaceTracker2FB xrDestroyEyeTracker2FB;
|
||||
extern PFN_xrGetFaceExpressionWeights2FB xrGetFaceExpressionWeights2FB;
|
||||
|
||||
class FFaceTrackingXR : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
// IOculusXROpenXRHMDPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual void OnDestroySession(XrSession InSession) override;
|
||||
virtual void* OnWaitFrame(XrSession InSession, void* InNext) override;
|
||||
|
||||
public:
|
||||
FFaceTrackingXR();
|
||||
virtual ~FFaceTrackingXR();
|
||||
void RegisterAsOpenXRExtension();
|
||||
|
||||
bool IsFaceTrackingSupported() const { return bExtFaceTrackingSupported; }
|
||||
bool IsFaceTrackingEnabled() const { return FaceTracker != XR_NULL_HANDLE; }
|
||||
bool IsFaceTrackingVisemesSupported() const { return bExtFaceTrackingVisemesSupported; }
|
||||
bool IsFaceTrackingVisemesEnabled() const { return bVisemesEnabled; }
|
||||
|
||||
XrResult StartFaceTracking();
|
||||
XrResult StopFaceTracking();
|
||||
XrResult GetCachedFaceState(FOculusXRFaceState& OutState);
|
||||
XrResult SetVisemesEnabled(bool enabled);
|
||||
XrResult GetCachedVisemeState(FOculusXRFaceVisemesState& OutState);
|
||||
|
||||
private:
|
||||
void InitOpenXRFunctions(XrInstance InInstance);
|
||||
void Update_GameThread(XrSession InSession);
|
||||
|
||||
bool bExtFaceTrackingSupported;
|
||||
bool bExtFaceTrackingVisemesSupported;
|
||||
bool bVisemesEnabled;
|
||||
|
||||
FOpenXRHMD* OpenXRHMD;
|
||||
FOculusXRFaceState CachedFaceState;
|
||||
FOculusXRFaceVisemesState CachedVisemeState;
|
||||
|
||||
XrFaceTracker2FB FaceTracker = XR_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace XRMovement
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <khronos/openxr/openxr.h>
|
||||
#include <khronos/openxr/meta_openxr_preview/meta_body_tracking_calibration.h>
|
||||
#include <khronos/openxr/meta_openxr_preview/meta_body_tracking_fidelity.h>
|
||||
#include <khronos/openxr/meta_openxr_preview/meta_body_tracking_full_body.h>
|
||||
#include <khronos/openxr/meta_openxr_preview/metax1_face_tracking_visemes.h>
|
||||
Reference in New Issue
Block a user