October3d55/Matain/ViveOpenXR/Source/ViveOpenXRTracker/ViveOpenXRXrTracker/Private/ViveOpenXRXrTracker.cpp

975 lines
42 KiB
C++

// Copyright HTC Corporation. All Rights Reserved.
#include "ViveOpenXRXrTracker.h"
#include "IXRTrackingSystem.h"
#include "OpenXRCore.h"
#include "UObject/UObjectIterator.h"
#include "Modules/ModuleManager.h"
#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"
#include "Misc/ConfigCacheIni.h"
#include "Modules/ModuleManager.h"
#include "GenericPlatform/GenericPlatformInputDeviceMapper.h"
#include "EnhancedInputDeveloperSettings.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystemInterface.h"
#include "EnhancedInputModule.h"
#include "PlayerMappableInputConfig.h"
#include "GenericPlatform/GenericPlatformInputDeviceMapper.h"
#if WITH_EDITOR
#include "Editor/EditorEngine.h"
#include "Editor.h"
#include "EnhancedInputEditorSubsystem.h"
#endif
DEFINE_LOG_CATEGORY(LogViveOpenXRXrTracker);
FViveOpenXRXrTracker* FViveOpenXRXrTracker::m_Instance = nullptr;
FViveOpenXRXrTracker::FViveXrTracker::FViveXrTracker()
: XrTrackerUserPath(XR_NULL_PATH)
, xrTrackerPosePath(XR_NULL_PATH)
, MenuClickPath(XR_NULL_PATH)
, TriggerClickPath(XR_NULL_PATH)
, SqueezeClickPath(XR_NULL_PATH)
, TrackpadClickPath(XR_NULL_PATH)
, xrTrackerPoseAction(XR_NULL_HANDLE)
, MenuClickAction(XR_NULL_HANDLE)
, TriggerClickAction(XR_NULL_HANDLE)
, SqueezeClickAction(XR_NULL_HANDLE)
, TrackpadClickAction(XR_NULL_HANDLE)
, XrTrackerSpaceLocation({ XR_TYPE_SPACE_LOCATION })
, XrTrackerStatePose({ XR_TYPE_ACTION_STATE_POSE })
, MenuClickActionState({ XR_TYPE_ACTION_STATE_BOOLEAN })
, TriggerClickActionState({ XR_TYPE_ACTION_STATE_BOOLEAN })
, SqueezeClickActionState({ XR_TYPE_ACTION_STATE_BOOLEAN })
, TrackpadClickActionState({ XR_TYPE_ACTION_STATE_BOOLEAN })
, DeviceId(0)
{
XrTrackerPaths.RemoveAll([](const int& num) {
return true;
});
}
void FViveOpenXRXrTracker::FViveXrTracker::AddAction(XrActionSet& InActionSet, const FName& InName, const TArray<XrPath>& InSubactionPaths, XrActionType InActionType, XrAction& InAction)
{
check(InActionSet != XR_NULL_HANDLE);
if (InAction != XR_NULL_HANDLE) {
xrDestroyAction(InAction);
InAction = XR_NULL_HANDLE;
}
char ActionName[NAME_SIZE];
InName.GetPlainANSIString(ActionName);
XrActionCreateInfo Info;
Info.type = XR_TYPE_ACTION_CREATE_INFO;
Info.next = nullptr;
Info.actionType = InActionType;
Info.countSubactionPaths = InSubactionPaths.Num();
Info.subactionPaths = InSubactionPaths.GetData();
FCStringAnsi::Strcpy(Info.actionName, XR_MAX_ACTION_NAME_SIZE, ActionName);
FCStringAnsi::Strcpy(Info.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE, ActionName);
XR_ENSURE(xrCreateAction(InActionSet, &Info, &InAction));
}
FViveOpenXRXrTracker::FViveOpenXRXrTracker(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
: MessageHandler(InMessageHandler)
{
// Register modular feature manually
IModularFeatures::Get().RegisterModularFeature(IMotionController::GetModularFeatureName(), static_cast<IMotionController*>(this));
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
m_Instance = this;
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("FViveOpenXRXrTracker() register extension feature Xr Tracker %p."), m_Instance);
}
FViveOpenXRXrTracker::~FViveOpenXRXrTracker()
{
// Unregister modular feature manually
IModularFeatures::Get().UnregisterModularFeature(IMotionController::GetModularFeatureName(), static_cast<IMotionController*>(this));
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
}
bool FViveOpenXRXrTracker::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
if (m_EnableXrTracker)
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("GetRequiredExtensions() XR_HTC_path_enumeration."));
OutExtensions.Add("XR_HTC_path_enumeration");
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("GetRequiredExtensions() XR_HTC_vive_xr_tracker_interaction."));
OutExtensions.Add("XR_HTC_vive_xr_tracker_interaction");
}
return true;
}
#define LOCTEXT_NAMESPACE "FViveOpenXRXrTrackerModule"
void FViveOpenXRXrTracker::PostCreateInstance(XrInstance InInstance)
{
if (!m_EnableXrTracker) return;
Instance = InInstance;
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrEnumeratePathsForInteractionProfileHTC", (PFN_xrVoidFunction*)&xrEnumeratePathsForInteractionProfileHTC));
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("Get xrEnumeratePathsForInteractionProfileHTC function pointer."));
if (!bPathEnumerated)
{
// Enumerate tracker user paths
uint32_t userCount = 0;
uint32_t userCountInput = 0;
// initialization arrays
XrTrackerUserPaths.Empty();
XrTrackerPaths.Empty();
// Create enumerateInfo
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
enumerateInfo.type = XR_TYPE_PATHS_FOR_INTERACTION_PROFILE_ENUMERATE_INFO_HTC;
// Get the user paths with the input interaction profile.
xrTrackerInteractionProfile = FOpenXRPath("/interaction_profiles/htc/vive_xr_tracker");
enumerateInfo.interactionProfile = xrTrackerInteractionProfile;
// Use XR_NULL_PATH pathenumerate to get supported userspath under interactionprofile
enumerateInfo.userPath = XR_NULL_PATH;
TArray<XrPath> XrTrackerEnumerateUserPaths;
XrTrackerEnumerateUserPaths.Empty();
// Enumerate user paths
XR_ENSURE(xrEnumeratePathsForInteractionProfileHTC(Instance, &enumerateInfo, 0, &userCount, XrTrackerEnumerateUserPaths.GetData()));
{
userCountInput = userCount;
bool enumerateUltimatePath = false;
if (userCountInput > 0)
{
// Get how meny user paths
for (uint32_t i = 0; i < userCount; i++)
{
XrTrackerEnumerateUserPaths.Emplace(XR_NULL_PATH);
}
// Get user paths names
XR_ENSURE(xrEnumeratePathsForInteractionProfileHTC(Instance, &enumerateInfo, userCountInput, &userCount, XrTrackerEnumerateUserPaths.GetData()));
for (uint32_t i = 0; i < userCountInput; i++)
{
FOpenXRPath path = XrTrackerEnumerateUserPaths[i];
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("XrPath : %s"), *path.ToString());
if (path.ToString().Contains("ultimate"))
XrTrackerUserPaths.Emplace(XrTrackerEnumerateUserPaths[i]);
}
if (XrTrackerUserPaths.Num() >= 1)
enumerateUltimatePath = true;
else
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("xrEnumeratePathsForInteractionProfileHTC no any path."));
}
if (!enumerateUltimatePath)
{
XrTrackerUserPaths.Empty();
for (int i = 0; i < 5; i++)
{
XrTrackerUserPaths.Emplace(FOpenXRPath(xrTrackerPaths[i]));
}
}
TArray<XrPath> XrTrackerSupportPaths;
// Get inputs for each user
for (int i = 0; i < XrTrackerUserPaths.Num(); i++)
{
FName XrTrackerName = (FName)("UltimateTracker" + FString::FormatAsNumber(i + 1));
FViveXrTracker viveXrTracker = FViveXrTracker();
viveXrTrackerMap.Add(XrTrackerName, viveXrTracker);
FOpenXRPath XrTrackerUser = XrTrackerUserPaths[i];
viveXrTrackerMap[XrTrackerName].XrTrackerUserPath = XrTrackerUser;
uint32_t pathCount = 0;
uint32_t pathCountInput = 0;
// Get the input and output source paths with user path.
enumerateInfo.userPath = viveXrTrackerMap[XrTrackerName].XrTrackerUserPath;
// Enumerate user input paths
XR_ENSURE(xrEnumeratePathsForInteractionProfileHTC(Instance, &enumerateInfo, 0, &pathCount, XrTrackerSupportPaths.GetData()));
{
// Get how meny input paths
pathCountInput = pathCount;
TArray<XrPath> XrTrackerSupportInputPaths;
XrTrackerSupportPaths.Empty();
if (pathCountInput >= 0)
{
for (uint32_t j = 0; j < pathCountInput; j++)
{
XrTrackerSupportPaths.Emplace(XR_NULL_PATH);
}
XR_ENSURE(xrEnumeratePathsForInteractionProfileHTC(Instance, &enumerateInfo, pathCountInput, &pathCount, XrTrackerSupportPaths.GetData()));
{
for (uint32_t j = 0; j < pathCountInput; j++)
{
uint32 PathCount = 0;
char PathChars[XR_MAX_PATH_LENGTH];
XrResult Result = xrPathToString(Instance, XrTrackerSupportPaths[j], XR_MAX_PATH_LENGTH, &PathCount, PathChars);
if (FString(PathCount, PathChars).Contains("pose"))
{
FString pathStr;
viveXrTrackerMap[XrTrackerName].PoseActionNameStr = "xrtracker_" + FString::FormatAsNumber(i + 1) + "pose";
PathCount = FString(PathChars).Len();
// Set up pose xrpath
pathStr = XrTrackerUser.ToString() + FString(PathCount, PathChars);
FTCHARToUTF8 Converter(*pathStr);
const char* utf8PathStr = Converter.Get();
XrPath posePath = static_cast<FOpenXRPath>(utf8PathStr);
viveXrTrackerMap[XrTrackerName].xrTrackerPosePath = posePath;
}
else if (FString(PathCount, PathChars).Contains("menu"))
{
FString pathStr;
viveXrTrackerMap[XrTrackerName].MenuActionNameStr = "xrtracker_" + FString::FormatAsNumber(i + 1) + "_menu" + "_click";
PathCount = FString(PathChars).Len();
// Set up manu xrpath
pathStr = XrTrackerUser.ToString() + FString(PathCount, PathChars);
FTCHARToUTF8 Converter(*pathStr);
const char* utf8PathStr = Converter.Get();
XrPath menuClickPathPath = static_cast<FOpenXRPath>(utf8PathStr);
viveXrTrackerMap[XrTrackerName].MenuClickPath = menuClickPathPath;
// Set up manu name
FName actionFName(*viveXrTrackerMap[XrTrackerName].MenuActionNameStr);
viveXrTrackerMap[XrTrackerName].MenuClickKey = FKey(actionFName);
}
else if (FString(PathCount, PathChars).Contains("squeeze"))
{
FString pathStr;
viveXrTrackerMap[XrTrackerName].SqueezeActionNameStr = "xrtracker_" + FString::FormatAsNumber(i + 1) + "_squeeze" + "_click";
PathCount = FString(PathChars).Len();
// Set up squeeze xrpath
pathStr = XrTrackerUser.ToString() + FString(PathCount, PathChars);
FTCHARToUTF8 Converter(*pathStr);
const char* utf8PathStr = Converter.Get();
XrPath squeezeClickPath = static_cast<FOpenXRPath>(utf8PathStr);
viveXrTrackerMap[XrTrackerName].SqueezeClickPath = squeezeClickPath;
// Set up squeeze name
FName actionFName(*viveXrTrackerMap[XrTrackerName].SqueezeActionNameStr);
viveXrTrackerMap[XrTrackerName].SqueezeClickKey = FKey(actionFName);
}
else if (FString(PathCount, PathChars).Contains("trigger"))
{
FString pathStr;
viveXrTrackerMap[XrTrackerName].TriggerActionNameStr = "xrtracker_" + FString::FormatAsNumber(i + 1) + "_trigger" + "_click";
PathCount = FString(PathChars).Len();
// Set up trigger xrpath
pathStr = XrTrackerUser.ToString() + FString(PathCount, PathChars);
FTCHARToUTF8 Converter(*pathStr);
const char* utf8PathStr = Converter.Get();
XrPath triggerClickPathPath = static_cast<FOpenXRPath>(utf8PathStr);
viveXrTrackerMap[XrTrackerName].TriggerClickPath = triggerClickPathPath;
// Set up trigger name
FName actionFName(*viveXrTrackerMap[XrTrackerName].TriggerActionNameStr);
viveXrTrackerMap[XrTrackerName].TriggerClickKey = FKey(actionFName);
}
else if (FString(PathCount, PathChars).Contains("trackpad"))
{
FString pathStr;
viveXrTrackerMap[XrTrackerName].TrackpadActionNameStr = "xrtracker_" + FString::FormatAsNumber(i + 1) + "_trackpad" + "_click";
PathCount = FString(PathChars).Len();
// Set up trackpad xrpath
pathStr = XrTrackerUser.ToString() + FString(PathCount, PathChars);
FTCHARToUTF8 Converter(*pathStr);
const char* utf8PathStr = Converter.Get();
XrPath trackpadClickPath = static_cast<FOpenXRPath>(utf8PathStr);
viveXrTrackerMap[XrTrackerName].TrackpadClickPath = trackpadClickPath;
// Set up trackpad name
FName actionFName(*viveXrTrackerMap[XrTrackerName].TrackpadActionNameStr);
viveXrTrackerMap[XrTrackerName].TrackpadClickKey = FKey(actionFName);
}
}
}
}
}
}
}
}
bPathEnumerated = true;
}
}
void FViveOpenXRXrTracker::PostCreateSession(XrSession InSession)
{
m_Session = InSession;
}
void FViveOpenXRXrTracker::AttachActionSets(TSet<XrActionSet>& OutActionSets)
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("AttachActionSets() Entry."));
if (!m_EnableXrTracker || !bPathEnumerated) return;
check(Instance != XR_NULL_HANDLE);
OutActionSets.Add(XrTrackerActionSet);
for (auto& viveXrTracker : viveXrTrackerMap)
{
viveXrTracker.Value.AddTrackedDevices(OpenXRHMD); //Wait for subacitonPath
}
if (m_EnableXrTrackerInputs)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const UEnhancedInputDeveloperSettings* InputSettings = GetDefault<UEnhancedInputDeveloperSettings>();
if (InputSettings)
{
for (const auto& Context : InputSettings->DefaultMappingContexts)
{
if (Context.InputMappingContext)
{
TStrongObjectPtr<const UInputMappingContext> Obj(Context.InputMappingContext.LoadSynchronous());
InputMappingContextToPriorityMap.Add(Obj, Context.Priority);
}
else
{
UE_LOG(LogHMD, Warning, TEXT("Default Mapping Contexts contains an Input Mapping Context set to \"None\", ignoring while building OpenXR actions."));
}
}
}
if (!InputMappingContextToPriorityMap.IsEmpty())
{
for (const auto& MappingContext : InputMappingContextToPriorityMap)
{
for (const FEnhancedActionKeyMapping& Mapping : MappingContext.Key->GetMappings())
{
for (auto& viveXrTracker : viveXrTrackerMap)
{
viveXrTracker.Value.CheckAndAddEnhancedInputAction(Mapping);
}
}
}
}
else
{
for (auto& viveXrTracker : viveXrTrackerMap)
{
KeyActionStates.Emplace(viveXrTracker.Value.MenuClickKey.GetFName(), &viveXrTracker.Value.MenuClickActionState);
KeyActionStates.Emplace(viveXrTracker.Value.TriggerClickKey.GetFName(), &viveXrTracker.Value.TriggerClickActionState);
KeyActionStates.Emplace(viveXrTracker.Value.SqueezeClickKey.GetFName(), &viveXrTracker.Value.SqueezeClickActionState);
KeyActionStates.Emplace(viveXrTracker.Value.TrackpadClickKey.GetFName(), &viveXrTracker.Value.TrackpadClickActionState);
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
}
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("AttachActionSets() finished."));
bActionsAttached = true;
}
void FViveOpenXRXrTracker::GetActiveActionSetsForSync(TArray<XrActiveActionSet>& OutActiveSets)
{
if (!m_EnableXrTracker || !bPathEnumerated) return;
check(XrTrackerActionSet != XR_NULL_HANDLE);
OutActiveSets.Add(XrActiveActionSet{ XrTrackerActionSet, XR_NULL_PATH });
}
const void* FViveOpenXRXrTracker::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
{
static FName SystemName(TEXT("OpenXR"));
Instance = InInstance;
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
OpenXRHMD = (FOpenXRHMD*)GEngine->XRSystem.Get();
}
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("OnCreateSession() entry."));
if (!m_EnableXrTracker || !bPathEnumerated) return InNext;
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("Entry XrTracker OnCreateSession."));
// Create ActionSet
if (XrTrackerActionSet != XR_NULL_HANDLE)
{
xrDestroyActionSet(XrTrackerActionSet);
XrTrackerActionSet = XR_NULL_HANDLE;
}
{
XrActionSetCreateInfo xrTrackerActionInfo;
xrTrackerActionInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
xrTrackerActionInfo.next = nullptr;
FCStringAnsi::Strcpy(xrTrackerActionInfo.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE, "xrtracker");
FCStringAnsi::Strcpy(xrTrackerActionInfo.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE, "VIVE OpenXR Xr Tracker Action Set");
xrTrackerActionInfo.priority = 0;
XR_ENSURE(xrCreateActionSet(Instance, &xrTrackerActionInfo, &XrTrackerActionSet));
}
TArray<XrActionSuggestedBinding> Bindings;
for (auto& viveXrTracker : viveXrTrackerMap)
{
FName poseActionFName(*viveXrTracker.Value.PoseActionNameStr);
viveXrTracker.Value.AddAction(XrTrackerActionSet, poseActionFName, XrTrackerUserPaths, XR_ACTION_TYPE_POSE_INPUT, viveXrTracker.Value.xrTrackerPoseAction);
if (m_EnableXrTrackerInputs)
{
FName manuActionFName(*viveXrTracker.Value.MenuActionNameStr);
viveXrTracker.Value.AddAction(XrTrackerActionSet, manuActionFName, XrTrackerUserPaths, XR_ACTION_TYPE_BOOLEAN_INPUT, viveXrTracker.Value.MenuClickAction);
FName triggerActionFName(*viveXrTracker.Value.TriggerActionNameStr);
viveXrTracker.Value.AddAction(XrTrackerActionSet, triggerActionFName, XrTrackerUserPaths, XR_ACTION_TYPE_BOOLEAN_INPUT, viveXrTracker.Value.TriggerClickAction);
FName squeezeActionFName(*viveXrTracker.Value.SqueezeActionNameStr);
viveXrTracker.Value.AddAction(XrTrackerActionSet, squeezeActionFName, XrTrackerUserPaths, XR_ACTION_TYPE_BOOLEAN_INPUT, viveXrTracker.Value.SqueezeClickAction);
FName trackpadActionFName(*viveXrTracker.Value.TrackpadActionNameStr);
viveXrTracker.Value.AddAction(XrTrackerActionSet, trackpadActionFName, XrTrackerUserPaths, XR_ACTION_TYPE_BOOLEAN_INPUT, viveXrTracker.Value.TrackpadClickAction);
}
viveXrTracker.Value.GetSuggestedBindings(Bindings);
viveXrTracker.Value.XrTrackerPaths = XrTrackerPaths;
}
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("xrSuggestInteractionProfileBindings()"));
XrInteractionProfileSuggestedBinding SuggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
SuggestedBindings.interactionProfile = xrTrackerInteractionProfile;
SuggestedBindings.suggestedBindings = Bindings.GetData();
SuggestedBindings.countSuggestedBindings = (uint32)Bindings.Num();
XR_ENSURE(xrSuggestInteractionProfileBindings(Instance, &SuggestedBindings));
return InNext;
}
const void* FViveOpenXRXrTracker::OnBeginSession(XrSession InSession, const void* InNext)
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("OnBeginSession() entry."));
if (!m_EnableXrTracker || !bPathEnumerated) return InNext;
bSessionStarted = true;
return InNext;
}
void FViveOpenXRXrTracker::OnDestroySession(XrSession InSession)
{
if (bActionsAttached)
{
bActionsAttached = false;
bSessionStarted = false;
KeyActionStates.Reset();
}
}
void FViveOpenXRXrTracker::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
{
}
/************************************************************************/
/* IMotionController */
/************************************************************************/
bool FViveOpenXRXrTracker::GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const
{
if (!bActionsAttached || OpenXRHMD == nullptr)
{
return false;
}
if (!MotionSource.ToString().Contains("UltimateTracker"))
return false;
if (viveXrTrackerMap.Num() == 0)
return false;
if (!viveXrTrackerMap.Contains(MotionSource)) {
return false;
}
FString motionSource = MotionSource.ToString();
if (ControllerIndex == DeviceIndex && motionSource.Contains("XrTracker"))
{
int32 DeviceId = viveXrTrackerMap[MotionSource].DeviceId;
FQuat Orientation;
bool Success = OpenXRHMD->GetCurrentPose(DeviceId, Orientation, OutPosition);
OutOrientation = FRotator(Orientation);
return Success;
}
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("GetControllerOrientationAndPosition return false."));
return false;
}
bool FViveOpenXRXrTracker::GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, bool& OutbProvidedLinearVelocity, FVector& OutLinearVelocity, bool& OutbProvidedAngularVelocity, FVector& OutAngularVelocityAsAxisAndLength, bool& OutbProvidedLinearAcceleration, FVector& OutLinearAcceleration, float WorldToMetersScale) const
{
// FTimespan initializes to 0 and GetControllerOrientationAndPositionForTime with time 0 will return the latest data.
FTimespan Time;
bool OutTimeWasUsed = false;
return GetControllerOrientationAndPositionForTime(ControllerIndex, MotionSource, Time, OutTimeWasUsed, OutOrientation, OutPosition, OutbProvidedLinearVelocity, OutLinearVelocity, OutbProvidedAngularVelocity, OutAngularVelocityAsAxisAndLength, OutbProvidedLinearAcceleration, OutLinearAcceleration, WorldToMetersScale);
}
bool FViveOpenXRXrTracker::GetControllerOrientationAndPositionForTime(const int32 ControllerIndex, const FName MotionSource, FTimespan Time, bool& OutTimeWasUsed, FRotator& OutOrientation, FVector& OutPosition, bool& OutbProvidedLinearVelocity, FVector& OutLinearVelocity, bool& OutbProvidedAngularVelocity, FVector& OutAngularVelocityRadPerSec, bool& OutbProvidedLinearAcceleration, FVector& OutLinearAcceleration, float WorldToMetersScale) const
{
if (!bActionsAttached || OpenXRHMD == nullptr)
{
return false;
}
FString motionSource = MotionSource.ToString();
if (ControllerIndex == DeviceIndex && motionSource.Contains("UltimateTracker"))
{
int32 DeviceId = viveXrTrackerMap[MotionSource].DeviceId;
FQuat Orientation;
bool Success = OpenXRHMD->GetPoseForTime(DeviceId, Time, OutTimeWasUsed, Orientation, OutPosition, OutbProvidedLinearVelocity, OutLinearVelocity, OutbProvidedAngularVelocity, OutAngularVelocityRadPerSec, OutbProvidedLinearAcceleration, OutLinearAcceleration, WorldToMetersScale);
OutOrientation = FRotator(Orientation);
return Success;
}
return false;
}
ETrackingStatus FViveOpenXRXrTracker::GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const
{
if (!bActionsAttached || OpenXRHMD == nullptr)
{
return ETrackingStatus::NotTracked;
}
XrSession Session = OpenXRHMD->GetSession();
if (Session == XR_NULL_HANDLE)
{
return ETrackingStatus::NotTracked;
}
XrActionStateGetInfo PoseActionStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
if (!MotionSource.ToString().Contains("UltimateTracker"))
return ETrackingStatus::NotTracked;
if (viveXrTrackerMap.Num() == 0)
return ETrackingStatus::NotTracked;
if (!viveXrTrackerMap.Contains(MotionSource)) {
return ETrackingStatus::NotTracked;
}
FViveXrTracker xrTracker = viveXrTrackerMap[MotionSource];
if (xrTracker.xrTrackerPoseAction == nullptr)
return ETrackingStatus::NotTracked;
PoseActionStateInfo.action = xrTracker.xrTrackerPoseAction;
PoseActionStateInfo.subactionPath = xrTracker.xrTrackerPosePath;
//UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("State.isActive"));
return ETrackingStatus::Tracked;
}
FName FViveOpenXRXrTracker::GetMotionControllerDeviceTypeName() const
{
const static FName DefaultName(TEXT("OpenXRXrTracker"));
return DefaultName;
}
void FViveOpenXRXrTracker::PostSyncActions(XrSession InSession)
{
if (!m_EnableXrTracker || !bPathEnumerated) return;
WorldToMetersScale_ = OpenXRHMD->GetWorldToMetersScale();
for (TPair<FName, FViveXrTracker>& viveXrTracker : viveXrTrackerMap)
{
viveXrTracker.Value.SyncActionStates(InSession);
}
if (m_EnableXrTrackerInputs)
{
SendInputEvent_Legacy();
SendInputEvent_EnhancedInput();
}
}
void FViveOpenXRXrTracker::EnumerateSources(TArray<FMotionControllerSource>& SourcesOut) const
{
check(IsInGameThread());
// PC suppots 20 trackers, Android supports 5 trackers
#if PLATFORM_ANDROID
for (int i = 1; i <= 5; i++)
{
FString name = "UltimateTracker" + FString::FormatAsNumber(i);
SourcesOut.Add(FMotionControllerSource(FName(*name)));
}
#endif
#if PLATFORM_WINDOWS
for (int i = 1; i <= 20; i++)
{
FString name = "UltimateTracker" + FString::FormatAsNumber(i);
SourcesOut.Add(FMotionControllerSource(FName(*name)));
}
#endif
}
int32 FViveOpenXRXrTracker::FViveXrTracker::AddTrackedDevices(FOpenXRHMD* HMD)
{
if (HMD)
{
DeviceId = HMD->AddTrackedDevice(xrTrackerPoseAction, xrTrackerPosePath, XrTrackerUserPath);
}
return DeviceId;
}
void FViveOpenXRXrTracker::FViveXrTracker::GetSuggestedBindings(TArray<XrActionSuggestedBinding>& OutSuggestedBindings)
{
OutSuggestedBindings.Add(XrActionSuggestedBinding{ xrTrackerPoseAction, xrTrackerPosePath });
if (m_Instance->m_EnableXrTrackerInputs)
{
OutSuggestedBindings.Add(XrActionSuggestedBinding{ MenuClickAction, MenuClickPath });
OutSuggestedBindings.Add(XrActionSuggestedBinding{ TriggerClickAction, TriggerClickPath });
OutSuggestedBindings.Add(XrActionSuggestedBinding{ SqueezeClickAction, SqueezeClickPath });
OutSuggestedBindings.Add(XrActionSuggestedBinding{ TrackpadClickAction, TrackpadClickPath });
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
bool FViveOpenXRXrTracker::SetPlayerMappableInputConfig(TObjectPtr<class UPlayerMappableInputConfig> InputConfig)
{
if (bActionsAttached)
{
UE_LOG(LogHMD, Error, TEXT("Attempted to attach an input config while one is already attached for the current session."));
return false;
}
TSet<TObjectPtr<UInputMappingContext>> MappingContexts;
InputConfig->GetMappingContexts().GetKeys(MappingContexts);
return AttachInputMappingContexts(MappingContexts);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
void FViveOpenXRXrTracker::FViveXrTracker::SyncActionStates(XrSession InSession)
{
XrActionStateGetInfo GetXrTrackerStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
GetXrTrackerStateInfo.action = xrTrackerPoseAction;
GetXrTrackerStateInfo.subactionPath = XrTrackerUserPath;
XR_ENSURE(xrGetActionStatePose(InSession, &GetXrTrackerStateInfo, &XrTrackerStatePose));
if (m_Instance->m_EnableXrTrackerInputs)
{
XrActionStateGetInfo MenuClickActionStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
MenuClickActionStateInfo.action = MenuClickAction;
MenuClickActionStateInfo.subactionPath = XrTrackerUserPath;
XR_ENSURE(xrGetActionStateBoolean(InSession, &MenuClickActionStateInfo, &MenuClickActionState));
XrActionStateGetInfo TriggerClickActionStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
TriggerClickActionStateInfo.action = TriggerClickAction;
TriggerClickActionStateInfo.subactionPath = XrTrackerUserPath;
XR_ENSURE(xrGetActionStateBoolean(InSession, &TriggerClickActionStateInfo, &TriggerClickActionState));
XrActionStateGetInfo SqueezeClickActionStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
SqueezeClickActionStateInfo.action = SqueezeClickAction;
SqueezeClickActionStateInfo.subactionPath = XrTrackerUserPath;
XR_ENSURE(xrGetActionStateBoolean(InSession, &SqueezeClickActionStateInfo, &SqueezeClickActionState));
XrActionStateGetInfo TrackpadClickActionStateInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
TrackpadClickActionStateInfo.action = TrackpadClickAction;
TrackpadClickActionStateInfo.subactionPath = XrTrackerUserPath;
XR_ENSURE(xrGetActionStateBoolean(InSession, &TrackpadClickActionStateInfo, &TrackpadClickActionState));
}
}
void FViveOpenXRXrTracker::FViveXrTracker::CheckAndAddEnhancedInputAction(FEnhancedActionKeyMapping EnhancedActionKeyMapping)
{
if (EnhancedActionKeyMapping.Key == MenuClickKey)
{
MenuClickActions.Emplace(EnhancedActionKeyMapping.Action);
}
if (EnhancedActionKeyMapping.Key == TriggerClickKey)
{
TriggerClickActions.Emplace(EnhancedActionKeyMapping.Action);
}
if (EnhancedActionKeyMapping.Key == SqueezeClickKey)
{
SqueezeClickActions.Emplace(EnhancedActionKeyMapping.Action);
}
if (EnhancedActionKeyMapping.Key == TrackpadClickKey)
{
TrackpadClickActions.Emplace(EnhancedActionKeyMapping.Action);
}
}
void FViveOpenXRXrTracker::SendInputEvent_Legacy()
{
if (!m_EnableXrTracker || !bPathEnumerated) return;
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
for (auto& KeyActionState : KeyActionStates)
{
XrActionStateBoolean state = *KeyActionState.Value;
if (state.changedSinceLastSync)
{
FName keyName = KeyActionState.Key;
if (state.isActive && state.currentState)
{
MessageHandler->OnControllerButtonPressed(keyName, DeviceMapper.GetPrimaryPlatformUser(), DeviceMapper.GetDefaultInputDevice(), /*IsRepeat =*/false);
}
else
{
MessageHandler->OnControllerButtonReleased(keyName, DeviceMapper.GetPrimaryPlatformUser(), DeviceMapper.GetDefaultInputDevice(), /*IsRepeat =*/false);
}
}
}
}
void FViveOpenXRXrTracker::SendInputEvent_EnhancedInput()
{
if (!m_EnableXrTracker || !bPathEnumerated) return;
auto InjectEnhancedInput = [](XrActionStateBoolean& State, TArray<TObjectPtr<const UInputAction>>& Actions)
{
FInputActionValue InputValue;
TArray<TObjectPtr<UInputTrigger>> Triggers = {};
TArray<TObjectPtr<UInputModifier>> Modifiers = {};
InputValue = FInputActionValue(State.isActive ? (bool)State.currentState : false);
//UE_LOG(LogViveOpenXRWristTracker, Log, TEXT("SendInputEvent_EnhancedInput()."));
for (auto InputAction : Actions)
{
//if(State.isActive) UE_LOG(LogViveOpenXRWristTracker, Log, TEXT("SendInputEvent_EnhancedInput() Action: %s, Value: %d"), *InputAction.GetFName().ToString(), (uint8_t)State.currentState);
auto InjectSubsystemInput = [InputAction, InputValue, Triggers, Modifiers](IEnhancedInputSubsystemInterface* Subsystem)
{
if (Subsystem)
{
Subsystem->InjectInputForAction(InputAction, InputValue, Modifiers, Triggers);
}
};
IEnhancedInputModule::Get().GetLibrary()->ForEachSubsystem(InjectSubsystemInput);
#if WITH_EDITOR
if (GEditor)
{
// UEnhancedInputLibrary::ForEachSubsystem only enumerates runtime subsystems.
InjectSubsystemInput(GEditor->GetEditorSubsystem<UEnhancedInputEditorSubsystem>());
}
#endif
}
};
for (TPair<FName, FViveXrTracker>& viveXrTracker : viveXrTrackerMap)
{
InjectEnhancedInput(viveXrTracker.Value.MenuClickActionState, viveXrTracker.Value.MenuClickActions);
InjectEnhancedInput(viveXrTracker.Value.TriggerClickActionState, viveXrTracker.Value.TriggerClickActions);
InjectEnhancedInput(viveXrTracker.Value.SqueezeClickActionState, viveXrTracker.Value.SqueezeClickActions);
InjectEnhancedInput(viveXrTracker.Value.TrackpadClickActionState, viveXrTracker.Value.TrackpadClickActions);
}
}
#undef LOCTEXT_NAMESPACE
#define LOCTEXT_NAMESPACE "FViveOpenXRXrTrackerModule"
FName IViveOpenXRXrTrackerModule::ViveOpenXRXrTrackerModularKeyName = FName(TEXT("ViveOpenXRXrTracker"));
FViveOpenXRXrTrackerModule::FViveOpenXRXrTrackerModule()
{
}
void FViveOpenXRXrTrackerModule::StartupModule()
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("StartupModule() Entry."));
IViveOpenXRXrTrackerModule::StartupModule();
TSharedPtr<FGenericApplicationMessageHandler> DummyMessageHandler(new FGenericApplicationMessageHandler());
CreateInputDevice(DummyMessageHandler.ToSharedRef());
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 1", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 1"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 2", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 2"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 3", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 3"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 4", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 4"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 5", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 5"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 6", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 6"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 7", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 7"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 8", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 8"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 9", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 9"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 10", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 10"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 11", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 11"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 12", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 12"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 13", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 13"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 14", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 14"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 15", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 15"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 16", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 16"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 17", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 17"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 18", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 18"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 19", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 19"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("Xr Tracker 20", LOCTEXT("XrTrackerSubCategory", "HTC Xr Tracker 20"), TEXT("GraphEditor.PadEvent_16x"));
// Check if the modeule is enabled
check(GConfig && GConfig->IsReadyForUse());
FString modeName;
if (GConfig->GetString(TEXT("/Script/ViveOpenXRRuntimeSettings.ViveOpenXRRuntimeSettings"), TEXT("bEnableXrTracker"), modeName, GEngineIni))
{
if (modeName.Equals("False"))
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("StartupModule() Entry."));
GetXrTracker()->m_EnableXrTracker = false;
}
else if (modeName.Equals("True"))
{
GetXrTracker()->m_EnableXrTracker = true;
}
}
if (GConfig->GetString(TEXT("/Script/ViveOpenXRRuntimeSettings.ViveOpenXRRuntimeSettings"), TEXT("bEnableXrTrackerInputs"), modeName, GEngineIni))
{
if (modeName.Equals("False"))
{
GetXrTracker()->m_EnableXrTrackerInputs = false;
}
else if (modeName.Equals("True"))
{
GetXrTracker()->m_EnableXrTrackerInputs = true;
}
}
if (GetXrTracker()->m_EnableXrTrackerInputs)
{
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 1", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 1"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 2", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 2"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 3", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 3"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 4", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 4"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 5", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 5"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 6", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 6"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 7", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 7"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 8", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 8"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 9", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 9"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 10", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 10"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 11", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 11"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 12", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 12"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 13", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 13"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 14", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 14"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 15", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 15"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 16", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 16"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 17", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 17"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 18", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 18"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 19", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 19"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddMenuCategoryDisplayInfo("UltimateTracker 20", LOCTEXT("XrTrackerSubCategory", "HTC UltimateTracker 20"), TEXT("GraphEditor.PadEvent_16x"));
#if PLATFORM_ANDROID
for (int i = 1; i <= 5; i++)
{
// UltimateTracker name
FName TrackerName("UltimateTracker " + FString::FormatAsNumber(i));
// Menu action input
FString MenuActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_menu" + "_click";
FName MenuActionName(*MenuActionString);
FText MenuText = FText::Format(LOCTEXT("XrTracker_{0}_Menu_Click", "UltimateTracker ({0}) Menu Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(MenuActionName), MenuText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Squeeze action input
FString SqueezeActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_squeeze" + "_click";
FName SqueezeActionName(*SqueezeActionString);
FText SqueezeText = FText::Format(LOCTEXT("XrTracker_{0}_Squeeze_Click", "UltimateTracker ({0}) Squeeze Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(SqueezeActionName), SqueezeText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Trigger action input
FString TriggerActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_trigger" + "_click";
FName TriggerActionName(*TriggerActionString);
FText TriggerText = FText::Format(LOCTEXT("XrTracker_{0}_Trigger_Click", "UltimateTracker ({0}) Trigger Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(TriggerActionName), TriggerText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Trackpad action input
FString TrackpadActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_trackpad" + "_click";
FName TrackpadActionName(*TrackpadActionString);
FText TrackpadText = FText::Format(LOCTEXT("XrTracker_{0}_Trackpad_Click", "UltimateTracker ({0}) Trackpad Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(TrackpadActionName), TrackpadText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
}
#endif
#if PLATFORM_WINDOWS
for (int i = 1; i <= 20; i++)
{
// UltimateTracker name
FName TrackerName("UltimateTracker " + FString::FormatAsNumber(i));
// Menu action input
FString MenuActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_menu" + "_click";
FName MenuActionName(*MenuActionString);
FText MenuText = FText::Format(LOCTEXT("XrTracker_{0}_Menu_Click", "UltimateTracker ({0}) Menu Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(MenuActionName), MenuText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Squeeze action input
FString SqueezeActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_squeeze" + "_click";
FName SqueezeActionName(*SqueezeActionString);
FText SqueezeText = FText::Format(LOCTEXT("XrTracker_{0}_Squeeze_Click", "UltimateTracker ({0}) Squeeze Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(SqueezeActionName), SqueezeText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Trigger action input
FString TriggerActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_trigger" + "_click";
FName TriggerActionName(*TriggerActionString);
FText TriggerText = FText::Format(LOCTEXT("XrTracker_{0}_Trigger_Click", "UltimateTracker ({0}) Trigger Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(TriggerActionName), TriggerText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
// Trackpad action input
FString TrackpadActionString = "xrtracker_" + FString::FormatAsNumber(i) + "_trackpad" + "_click";
FName TrackpadActionName(*TrackpadActionString);
FText TrackpadText = FText::Format(LOCTEXT("XrTracker_{0}_Trackpad_Click", "UltimateTracker ({0}) Trackpad Click"), FText::AsNumber(i));
EKeys::AddKey(FKeyDetails(FKey(TrackpadActionName), TrackpadText, FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, TrackerName));
}
#endif
}
if (GetXrTracker()->m_EnableXrTracker)
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("Enable Xr Tracker."));
}
else
{
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("Disable Xr Tracker."));
}
UE_LOG(LogViveOpenXRXrTracker, Log, TEXT("StartupModule() Finished."));
}
void FViveOpenXRXrTrackerModule::ShutdownModule()
{
}
TSharedPtr<class IInputDevice> FViveOpenXRXrTrackerModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
if (!XrTracker.IsValid())
{
auto InputDevice = new FViveOpenXRXrTracker(InMessageHandler);
XrTracker = TSharedPtr<FViveOpenXRXrTracker>(InputDevice);
return XrTracker;
}
else
{
XrTracker.Get()->SetMessageHandler(InMessageHandler);
return XrTracker;
}
return nullptr;
}
FViveOpenXRXrTracker* FViveOpenXRXrTrackerModule::GetXrTracker()
{
return FViveOpenXRXrTracker::GetInstance();
}
IMPLEMENT_MODULE(FViveOpenXRXrTrackerModule, ViveOpenXRXrTracker)