October3d55/M/PICOOpen7991a2a23d57V5/Source/PICOOpenXRHandTracking/Private/PICO_HandTracking.cpp

703 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PICO_HandTracking.h"
#include "PICO_IHandTrackingModule.h"
#include "IXRTrackingSystem.h"
#include "Engine/Engine.h"
#include "OpenXRCore.h"
#include "Framework/Application/SlateApplication.h"
//#include "DrawDebugHelpers.h"
#include "ILiveLinkClient.h"
#include "IOpenXRHMDModule.h"
#include "PICOOpenXRRuntimeSettings.h"
#include "UObject/UObjectGlobals.h"
#define LOCTEXT_NAMESPACE "PICOOpenXRHandTracking"
// These enum's must match.
static_assert(EHandKeypoint::Palm == static_cast<EHandKeypoint>(XR_HAND_JOINT_PALM_EXT), "EHandKeypoint enum does not match XrHandJointEXT.");
static_assert(EHandKeypoint::Wrist == static_cast<EHandKeypoint>(XR_HAND_JOINT_WRIST_EXT), "EHandKeypoint enum does not match XrHandJointEXT.");
static_assert(EHandKeypoint::ThumbMetacarpal == static_cast<EHandKeypoint>(XR_HAND_JOINT_THUMB_METACARPAL_EXT), "EHandKeypoint enum does not match XrHandJointEXT.");
static_assert(EHandKeypoint::IndexTip == static_cast<EHandKeypoint>(XR_HAND_JOINT_INDEX_TIP_EXT), "EHandKeypoint enum does not match XrHandJointEXT.");
static_assert(EHandKeypoint::LittleTip == static_cast<EHandKeypoint>(XR_HAND_JOINT_LITTLE_TIP_EXT), "EHandKeypoint enum does not match XrHandJointEXT.");
class FPICOOpenXRHandTrackingModule :
public IPICOOpenXRHandTrackingModule
{
public:
FPICOOpenXRHandTrackingModule()
: InputDevice(nullptr)
, bLiveLinkSourceRegistered(false)
{}
virtual void StartupModule() override
{
IPICOOpenXRHandTrackingModule::StartupModule();
// HACK: Generic Application might not be instantiated at this point so we create the input device with a
// dummy message handler. When the Generic Application creates the input device it passes a valid message
// handler to it which is further on used for all the controller events. This hack fixes issues caused by
// using a custom input device before the Generic Application has instantiated it. Eg. within BeginPlay()
//
// This also fixes the warnings that pop up on the custom input keys when the blueprint loads. Those
// warnings are caused because Unreal loads the bluerints before the input device has been instantiated
// and has added its keys, thus leading Unreal to believe that those keys don't exist. This hack causes
// an earlier instantiation of the input device, and consequently, the custom keys.
TSharedPtr<FGenericApplicationMessageHandler> DummyMessageHandler(new FGenericApplicationMessageHandler());
CreateInputDevice(DummyMessageHandler.ToSharedRef());
}
virtual void ShutdownModule() override
{
IPICOOpenXRHandTrackingModule::ShutdownModule();
}
virtual TSharedPtr<class IInputDevice> CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override
{
if (!InputDevice.IsValid())
{
TSharedPtr<FHandTrackingPICO> HandTrackingInputDevice(new FHandTrackingPICO(InMessageHandler));
InputDevice = HandTrackingInputDevice;
return InputDevice;
}
else
{
InputDevice.Get()->SetMessageHandler(InMessageHandler);
return InputDevice;
}
return nullptr;
}
virtual TSharedPtr<IInputDevice> GetInputDevice() override
{
if (!InputDevice.IsValid())
{
CreateInputDevice(FSlateApplication::Get().GetPlatformApplication()->GetMessageHandler());
}
return InputDevice;
}
virtual TSharedPtr<ILiveLinkSource> GetLiveLinkSource() override
{
if (!InputDevice.IsValid())
{
CreateInputDevice(FSlateApplication::Get().GetPlatformApplication()->GetMessageHandler());
}
return InputDevice;
}
virtual bool IsLiveLinkSourceValid() const override
{
return InputDevice.IsValid();
}
virtual void AddLiveLinkSource() override
{
if (bLiveLinkSourceRegistered)
{
return;
}
// Auto register with LiveLink
ensureMsgf(FModuleManager::Get().LoadModule("LiveLink"), TEXT("PICOOpenXRHandTracking depends on the LiveLink module."));
IModularFeatures& ModularFeatures = IModularFeatures::Get();
if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
{
ILiveLinkClient* LiveLinkClient = &IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
LiveLinkClient->AddSource(GetLiveLinkSource());
bLiveLinkSourceRegistered = true;
}
}
virtual void RemoveLiveLinkSource() override
{
IModularFeatures& ModularFeatures = IModularFeatures::Get();
if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
{
ILiveLinkClient* LiveLinkClient = &IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName);
LiveLinkClient->RemoveSource(GetLiveLinkSource());
}
bLiveLinkSourceRegistered = false;
}
private:
TSharedPtr<FHandTrackingPICO> InputDevice;
bool bLiveLinkSourceRegistered;
};
IMPLEMENT_MODULE(FPICOOpenXRHandTrackingModule, PICOOpenXRHandTracking);
FLiveLinkSubjectName FHandTrackingPICO::LiveLinkLeftHandTrackingSubjectName(TEXT("LeftHand"));
FLiveLinkSubjectName FHandTrackingPICO::LiveLinkRightHandTrackingSubjectName(TEXT("RightHand"));
FHandTrackingPICO::FHandTrackingPICO(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
: MessageHandler(InMessageHandler)
, DeviceIndex(0)
{
// Register modular feature manually
IModularFeatures::Get().RegisterModularFeature(IMotionController::GetModularFeatureName(), static_cast<IMotionController*>(this));
IModularFeatures::Get().RegisterModularFeature(IHandTracker::GetModularFeatureName(), static_cast<IHandTracker*>(this));
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
AddKeys();
// We're implicitly requiring that the OpenXRPlugin has been loaded and
// initialized at this point.
if (!IOpenXRHMDModule::Get().IsAvailable())
{
UE_LOG(PICOOpenXRHandTracking, Error, TEXT("Error - OpenXRHMDPlugin isn't available"));
}
}
FHandTrackingPICO::~FHandTrackingPICO()
{
// Unregister modular feature manually
IModularFeatures::Get().UnregisterModularFeature(IMotionController::GetModularFeatureName(), static_cast<IMotionController*>(this));
IModularFeatures::Get().UnregisterModularFeature(IHandTracker::GetModularFeatureName(), static_cast<IHandTracker*>(this));
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
}
bool FHandTrackingPICO::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
TArray<XrApiLayerProperties> Properties;
EnumerateOpenXRApiLayers(Properties);
// Some API layers can crash the loader when enabled, if they're present we shouldn't enable the extension
for (const XrApiLayerProperties& Layer : Properties)
{
if (FCStringAnsi::Strstr(Layer.layerName, "XR_APILAYER_VIVE_hand_tracking") &&
Layer.layerVersion <= 1)
{
return false;
}
}
OutExtensions.Add("XR_EXT_hand_tracking");
return true;
}
bool FHandTrackingPICO::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
OutExtensions.Add("XR_FB_hand_tracking_mesh");
return true;
}
const void* FHandTrackingPICO::OnGetSystem(XrInstance InInstance, const void* InNext)
{
// Store extension open xr calls to member function pointers for convenient use.
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)&xrCreateHandTrackerEXT));
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)&xrDestroyHandTrackerEXT));
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)&xrLocateHandJointsEXT));
return InNext;
}
const void* FHandTrackingPICO::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
{
// Need to wait until the EHandKeypoint enum has been loaded, so we do this here rather than in the constructor which runs too early.
BuildMotionSourceToKeypointMap();
XrSystemHandTrackingPropertiesEXT HandTrackingSystemProperties{
XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT };
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES,
&HandTrackingSystemProperties };
XR_ENSURE(xrGetSystemProperties(InInstance, InSystem, &systemProperties));
bHandTrackingAvailable = HandTrackingSystemProperties.supportsHandTracking == XR_TRUE;
return InNext;
}
const void* FHandTrackingPICO::OnBeginSession(XrSession InSession, const void* InNext)
{
static FName SystemName(TEXT("OpenXR"));
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
XRTrackingSystem = GEngine->XRSystem.Get();
}
Session = InSession;
return InNext;
}
void FHandTrackingPICO::UpdateDeviceLocations(XrSession InSession, XrTime InDisplayTime, XrSpace InTrackingSpace)
{
TrackingSpace = InTrackingSpace;
DisplayTime = InDisplayTime;
}
FHandTrackingPICO::FHandState::FHandState()
{
Velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
Velocities.jointVelocities = JointVelocities;
Scale.next = &Velocities;
Scale.currentOutput = 1.0f;
Locations.next = &Scale;
Locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
Locations.jointLocations = JointLocations;
}
bool FHandTrackingPICO::FHandState::GetTransform(EHandKeypoint Keypoint, FTransform& OutTransform) const
{
check((int32)Keypoint < EHandKeypointCount);
OutTransform = KeypointTransforms[(uint32)Keypoint];
return ReceivedJointPoses;
}
const FTransform& FHandTrackingPICO::FHandState::GetTransform(EHandKeypoint Keypoint) const
{
check((int32)Keypoint < EHandKeypointCount);
return KeypointTransforms[(uint32)Keypoint];
}
void FHandTrackingPICO::BuildMotionSourceToKeypointMap()
{
if (!MotionSourceToKeypointMap.IsEmpty())
{
return;
}
bool bUseMoreSpecificMotionSourceNames = false;
UPICOOpenXRRuntimeSettings* Settings = GetMutableDefault<UPICOOpenXRRuntimeSettings>();
ensure(Settings);
if (Settings)
{
bUseMoreSpecificMotionSourceNames = Settings->bUseMoreSpecificMotionSourceNames;
bSupportLegacyControllerMotionSources = Settings->bSupportLegacyControllerMotionSources;
}
// There is a motionsource that corresponds to each hand keypoint of the form [Left|Right][Keypoint].
// Build a map so we can quickly translate from motion source FName to hand bone EHandKeypoint value.
// We also have the option of using more specific motion sources of the form HandTracking[Left|Right][Keypoint]
// this is useful if one wishes to use hand tracking and controllers simultaneously.
// We also may support more generic legacy motion sources, by default we do support this.
const UEnum* EnumPtr = FindObject<UEnum>(nullptr, TEXT("/Script/HeadMountedDisplay.EHandKeypoint"), true);
check(EnumPtr != nullptr);
check(IsInGameThread());
const FString Left(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left"));
const FString Right(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingRight") : TEXT("Right"));
for (int64 E = 0; E < EHandKeypointCount; ++E)
{
const EHandKeypoint EnumValue = static_cast<EHandKeypoint>(E);
const FString EnumName = EnumPtr->GetNameStringByValue(E);
const FName LeftName(Left + EnumName);
const FName RightName(Right + EnumName);
MotionSourceToKeypointMap.Add(LeftName, MotionSourceInfo(EnumValue, true));
MotionSourceToKeypointMap.Add(RightName, MotionSourceInfo(EnumValue, false));
}
if (bSupportLegacyControllerMotionSources)
{
MotionSourceToKeypointMap.Add(FName("Left"), MotionSourceInfo(EHandKeypoint::Palm, true));
MotionSourceToKeypointMap.Add(FName("Right"), MotionSourceInfo(EHandKeypoint::Palm, false));
}
}
bool FHandTrackingPICO::GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const
{
if (!bHandTrackingAvailable)
{
return false;
}
// Hand tracking currently does not support late update. Current hand tracking systems have latency that make it pointless.
if (!IsInGameThread())
{
return false;
}
bool bTracked = false;
if (ControllerIndex == DeviceIndex)
{
FTransform ControllerTransform = FTransform::Identity;
const MotionSourceInfo* KeyPointInfoPtr = MotionSourceToKeypointMap.Find(MotionSource);
if (KeyPointInfoPtr)
{
const EHandKeypoint KeyPoint = KeyPointInfoPtr->Key;
const bool bIsLeft = KeyPointInfoPtr->Value;
if (bIsLeft)
{
ControllerTransform = GetLeftHandState().GetTransform(KeyPoint);
bTracked = GetLeftHandState().ReceivedJointPoses;
}
else
{
ControllerTransform = GetRightHandState().GetTransform(KeyPoint);
bTracked = GetRightHandState().ReceivedJointPoses;
}
}
OutPosition = ControllerTransform.GetLocation();
OutOrientation = ControllerTransform.GetRotation().Rotator();
}
return bTracked;
}
ETrackingStatus FHandTrackingPICO::GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const
{
if (!bHandTrackingAvailable)
{
return ETrackingStatus::NotTracked;
}
const MotionSourceInfo* KeyPointInfoPtr = MotionSourceToKeypointMap.Find(MotionSource);
if (KeyPointInfoPtr)
{
const bool bIsLeft = KeyPointInfoPtr->Value;
const FHandTrackingPICO::FHandState& HandState = bIsLeft ? GetLeftHandState() : GetRightHandState();
return HandState.ReceivedJointPoses ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked;
}
return ETrackingStatus::NotTracked;
}
FName FHandTrackingPICO::GetMotionControllerDeviceTypeName() const
{
if (FModuleManager::Get().IsModuleLoaded("OpenXRHandTracking"))
{
return TEXT("PICOOpenXRHandTracking");
}
else
{
return TEXT("OpenXRHandTracking");
}
}
void FHandTrackingPICO::EnumerateSources(TArray<FMotionControllerSource>& SourcesOut) const
{
check(IsInGameThread());
bool bUseMoreSpecificMotionSourceNames = false;
UPICOOpenXRRuntimeSettings* Settings = GetMutableDefault<UPICOOpenXRRuntimeSettings>();
ensure(Settings);
if (Settings)
{
bUseMoreSpecificMotionSourceNames = Settings->bUseMoreSpecificMotionSourceNames;
}
SourcesOut.Reserve(SourcesOut.Num() + (EHandKeypointCount * 2));
const UEnum* EnumPtr = FindObject<UEnum>(nullptr, TEXT("/Script/HeadMountedDisplay.EHandKeypoint"), true);
check(EnumPtr != nullptr);
const FString Left(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left"));
const FString Right(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingRight") : TEXT("Right"));
for (int32 Keypoint = 0; Keypoint < EHandKeypointCount; Keypoint++)
{
static int32 EnumNameLength = FString(TEXT("EHandKeypoint::")).Len();
const FString EnumString = EnumPtr->GetNameByValue(Keypoint).ToString();
const TCHAR* EnumChars = *EnumString;
const TCHAR* EnumValue = EnumChars + EnumNameLength;
FString StringLeft(Left);
StringLeft.AppendChars(EnumValue, EnumString.Len() - EnumNameLength);
FString StringRight(Right);
StringRight.AppendChars(EnumValue, EnumString.Len() - EnumNameLength);
FName SourceL(*(StringLeft));
FName SourceR(*(StringRight));
SourcesOut.Add(SourceL);
SourcesOut.Add(SourceR);
}
}
void FHandTrackingPICO::Tick(float DeltaTime)
{
UpdateLiveLink();
}
void FHandTrackingPICO::SendControllerEvents()
{
}
void FHandTrackingPICO::SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
MessageHandler = InMessageHandler;
}
bool FHandTrackingPICO::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
return false;
}
bool FHandTrackingPICO::IsGamepadAttached() const
{
return false;
}
FHandTrackingPICO::FHandState& FHandTrackingPICO::GetLeftHandState()
{
return HandStates[0];
}
FHandTrackingPICO::FHandState& FHandTrackingPICO::GetRightHandState()
{
return HandStates[1];
}
bool FHandTrackingPICO::StartHandTracking()
{
if (bHandTrackingAvailable && Session != XR_NULL_HANDLE)
{
// Create a hand tracker for left hand that tracks default set of hand joints.
FHandState& LeftHandState = GetLeftHandState();
{
XrHandTrackerCreateInfoEXT CreateInfo{ XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT };
CreateInfo.hand = XR_HAND_LEFT_EXT;
CreateInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
XR_ENSURE(xrCreateHandTrackerEXT(Session, &CreateInfo, &LeftHandState.HandTracker));
}
// Create a hand tracker for left hand that tracks default set of hand joints.
FHandState& RightHandState = GetRightHandState();
{
XrHandTrackerCreateInfoEXT CreateInfo{ XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT };
CreateInfo.hand = XR_HAND_RIGHT_EXT;
CreateInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
XR_ENSURE(xrCreateHandTrackerEXT(Session, &CreateInfo, &RightHandState.HandTracker));
}
bHandTrackingRunning = true;
return true;
}
return false;
}
void FHandTrackingPICO::StopHandTracking()
{
// Create a hand tracker for left hand that tracks default set of hand joints.
FHandState& LeftHandState = GetLeftHandState();
if (LeftHandState.HandTracker != XR_NULL_HANDLE)
{
XR_ENSURE(xrDestroyHandTrackerEXT(LeftHandState.HandTracker));
}
// Create a hand tracker for left hand that tracks default set of hand joints.
FHandState& RightHandState = GetRightHandState();
if (RightHandState.HandTracker != XR_NULL_HANDLE)
{
XR_ENSURE(xrDestroyHandTrackerEXT(RightHandState.HandTracker));
}
bHandTrackingRunning = false;
}
bool FHandTrackingPICO::UpdateHandTrackingData()
{
if (!bHandTrackingAvailable || !bHandTrackingRunning)
{
return false;
}
XrHandJointsLocateInfoEXT LocateInfo{ XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT };
LocateInfo.baseSpace = TrackingSpace;
LocateInfo.time = DisplayTime;
const float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale();
for (int i = 0; i < 2; ++i)
{
FHandState& HandState = HandStates[i];
XR_ENSURE(xrLocateHandJointsEXT(HandState.HandTracker, &LocateInfo, &HandState.Locations));
HandState.ReceivedJointPoses = HandState.Locations.isActive == XR_TRUE;
if (HandState.ReceivedJointPoses) {
static_assert(XR_HAND_JOINT_PALM_EXT == 0 && XR_HAND_JOINT_LITTLE_TIP_EXT == XR_HAND_JOINT_COUNT_EXT - 1, "XrHandJointEXT enum is not as assumed for the following loop!");
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j)
{
const XrHandJointLocationEXT& JoinLocation = HandState.JointLocations[j];
const XrPosef& Pose = JoinLocation.pose;
const FTransform Transform = ToFTransform(Pose, WorldToMetersScale);
HandState.KeypointTransforms[j] = Transform;
HandState.Radii[j] = JoinLocation.radius * WorldToMetersScale;
HandState.LinearVelocity[j] = ToFVector(HandState.JointVelocities[j].linearVelocity, WorldToMetersScale);
HandState.AngularVelocity[j] = ToFVector(HandState.JointVelocities[j].angularVelocity, WorldToMetersScale);
// velocities would go here
}
HandState.HandScale = HandState.Scale.currentOutput;
}
}
return true;
}
bool FHandTrackingPICO::GetHandTrackingData(EControllerHand Hand, TArray<FVector>& OutPositions, TArray<FQuat>& OutRotations, TArray<float>& OutRadii, TArray<FVector>& LinearVelocity, TArray<FVector>& AngularVelocity, float& Scale) const
{
if (!bHandTrackingAvailable)
{
return false;
}
if (Hand != EControllerHand::Left && Hand != EControllerHand::Right)
{
return false;
}
const FHandTrackingPICO::FHandState& HandState = (Hand == EControllerHand::Left) ? GetLeftHandState() : GetRightHandState();
if (!HandState.ReceivedJointPoses)
{
return false;
}
OutPositions.Empty(EHandKeypointCount);
OutRotations.Empty(EHandKeypointCount);
const FTransform& TrackingToWoldTransform = XRTrackingSystem->GetTrackingToWorldTransform();
for (int i = 0; i < EHandKeypointCount; ++i)
{
FTransform KeypointWorldTransform = HandState.KeypointTransforms[i] * TrackingToWoldTransform;
OutPositions.Add(KeypointWorldTransform.GetLocation());
OutRotations.Add(KeypointWorldTransform.GetRotation());
}
OutRadii.Empty(EHandKeypointCount);
for (int i = 0; i < EHandKeypointCount; ++i)
{
OutRadii.Add(HandState.Radii[i]);
}
LinearVelocity.Empty(EHandKeypointCount);
AngularVelocity.Empty(EHandKeypointCount);
for (int i = 0; i < EHandKeypointCount; ++i)
{
LinearVelocity.Add(HandState.LinearVelocity[i]);
AngularVelocity.Add(HandState.AngularVelocity[i]);
}
Scale = HandState.HandScale;
return true;
}
bool FHandTrackingPICO::GetHandTrackingMeshScale(EControllerHand Hand, float& Scale)
{
Scale = 1.0f;
if (!bHandTrackingAvailable)
{
return false;
}
if (Hand != EControllerHand::Left && Hand != EControllerHand::Right)
{
return false;
}
const FHandTrackingPICO::FHandState& HandState = (Hand == EControllerHand::Left) ? GetLeftHandState() : GetRightHandState();
if (!HandState.ReceivedJointPoses)
{
return false;
}
Scale = HandState.HandScale;
return true;
}
const FHandTrackingPICO::FHandState& FHandTrackingPICO::GetLeftHandState() const
{
return HandStates[0];
}
const FHandTrackingPICO::FHandState& FHandTrackingPICO::GetRightHandState() const
{
return HandStates[1];
}
bool FHandTrackingPICO::IsHandTrackingSupportedByDevice() const
{
return bHandTrackingAvailable;
}
FName FHandTrackingPICO::GetHandTrackerDeviceTypeName() const
{
return GetMotionControllerDeviceTypeName();
}
bool FHandTrackingPICO::IsHandTrackingStateValid() const
{
return bHandTrackingAvailable;
}
bool FHandTrackingPICO::GetKeypointState(EControllerHand Hand, EHandKeypoint Keypoint, FTransform& OutTransform, float& OutRadius) const
{
if (!bHandTrackingAvailable)
{
return false;
}
bool gotTransform = false;
// NOTE: currently there is no openxr input simulation implementation. Maybe we will do that soon though? Leaving this for reference for now.
//#if WITH_INPUT_SIMULATION
// if (auto* InputSim = UOpenXRInputSimulationEngineSubsystem::GetInputSimulationIfEnabled())
// {
// gotTransform = InputSim->GetHandJointTransform(Hand, Keypoint, OutTransform);
// OutRadius = HandState.Radii[(uint32)Keypoint];
// }
// else
//#endif
{
const FHandTrackingPICO::FHandState& HandState = (Hand == EControllerHand::Left) ? GetLeftHandState() : GetRightHandState();
gotTransform = HandState.GetTransform(Keypoint, OutTransform);
OutRadius = HandState.Radii[(uint32)Keypoint];
}
if (gotTransform)
{
// Convert to UE world space
const FTransform& TrackingToWoldTransform = XRTrackingSystem->GetTrackingToWorldTransform();
OutTransform *= TrackingToWoldTransform;
}
return gotTransform;
}
bool FHandTrackingPICO::GetAllKeypointStates(EControllerHand Hand, TArray<FVector>& OutPositions, TArray<FQuat>& OutRotations, TArray<float>& OutRadii) const
{
if (!bHandTrackingAvailable)
{
return false;
}
if (Hand != EControllerHand::Left && Hand != EControllerHand::Right)
{
return false;
}
const FHandTrackingPICO::FHandState& HandState = (Hand == EControllerHand::Left) ? GetLeftHandState() : GetRightHandState();
if (!HandState.ReceivedJointPoses)
{
return false;
}
OutPositions.Empty(EHandKeypointCount);
OutRotations.Empty(EHandKeypointCount);
const FTransform& TrackingToWoldTransform = XRTrackingSystem->GetTrackingToWorldTransform();
for (int i = 0; i < EHandKeypointCount; ++i)
{
FTransform KeypointWorldTransform = HandState.KeypointTransforms[i] * TrackingToWoldTransform;
OutPositions.Add(KeypointWorldTransform.GetLocation());
OutRotations.Add(KeypointWorldTransform.GetRotation());
}
OutRadii.Empty(EHandKeypointCount);
for (int i = 0; i < EHandKeypointCount; ++i)
{
OutRadii.Add(HandState.Radii[i]);
}
return true;
}
void FHandTrackingPICO::AddKeys()
{
}
#undef LOCTEXT_NAMESPACE