VRTowerDef/Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInput.cpp

1876 lines
91 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRInput.h"
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "OculusXRHandTracking.h"
#include "OculusXRMRFunctionLibrary.h"
#include "Misc/CoreDelegates.h"
#include "Features/IModularFeatures.h"
#include "Misc/ConfigCacheIni.h"
#include "Haptics/HapticFeedbackEffect_Base.h"
#include "GenericPlatform/GenericPlatformInputDeviceMapper.h"
#define OVR_DEBUG_LOGGING 0
#define OVR_HAP_LOGGING 0
#define LOCTEXT_NAMESPACE "OculusXRInput"
static TAutoConsoleVariable<int32> CVarOculusPCMBatchDuration(
TEXT("r.Mobile.Oculus.PCMBatchDuration"),
36,
TEXT("The duration that each PCM haptic batch lasts in ms. Default is 36ms.\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOculusControllerPose(
TEXT("r.Oculus.ControllerPose"),
0,
TEXT("0 Default controller pose.\n")
TEXT("1 Legacy controller pose.\n")
TEXT("2 Grip controller pose.\n")
TEXT("3 Aim controller pose.\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOculusResetUntrackedInputStates(
TEXT("r.Mobile.Oculus.ResetUntrackedInputStates"),
0,
TEXT("If true, reset input states of input devices if they are untracked (for example, controllers or hands after Oculus button is held to pause the app).\n"),
ECVF_Scalability | ECVF_RenderThreadSafe);
namespace OculusXRInput
{
const FKey FOculusKey::OculusTouch_Left_Thumbstick("OculusTouch_Left_Thumbstick");
const FKey FOculusKey::OculusTouch_Left_Trigger("OculusTouch_Left_Trigger");
const FKey FOculusKey::OculusTouch_Left_FaceButton1("OculusTouch_Left_FaceButton1");
const FKey FOculusKey::OculusTouch_Left_FaceButton2("OculusTouch_Left_FaceButton2");
const FKey FOculusKey::OculusTouch_Left_IndexPointing("OculusTouch_Left_IndexPointing");
const FKey FOculusKey::OculusTouch_Left_ThumbUp("OculusTouch_Left_ThumbUp");
const FKey FOculusKey::OculusTouch_Left_ThumbRest("OculusTouch_Left_ThumbRest");
const FKey FOculusKey::OculusTouch_Left_ThumbRest_Force("OculusTouch_Left_ThumbRest_Force");
const FKey FOculusKey::OculusTouch_Left_Stylus_Force("OculusTouch_Left_Stylus_Force");
const FKey FOculusKey::OculusTouch_Left_IndexTrigger_Curl("OculusTouch_Left_IndexTrigger_Curl");
const FKey FOculusKey::OculusTouch_Left_IndexTrigger_Slide("OculusTouch_Left_IndexTrigger_Slide");
const FKey FOculusKey::OculusTouch_Left_IndexTrigger_Force("OculusTouch_Left_IndexTrigger_Force");
const FKey FOculusKey::OculusTouch_Right_Thumbstick("OculusTouch_Right_Thumbstick");
const FKey FOculusKey::OculusTouch_Right_Trigger("OculusTouch_Right_Trigger");
const FKey FOculusKey::OculusTouch_Right_FaceButton1("OculusTouch_Right_FaceButton1");
const FKey FOculusKey::OculusTouch_Right_FaceButton2("OculusTouch_Right_FaceButton2");
const FKey FOculusKey::OculusTouch_Right_IndexPointing("OculusTouch_Right_IndexPointing");
const FKey FOculusKey::OculusTouch_Right_ThumbUp("OculusTouch_Right_ThumbUp");
const FKey FOculusKey::OculusTouch_Right_ThumbRest("OculusTouch_Right_ThumbRest");
const FKey FOculusKey::OculusTouch_Right_ThumbRest_Force("OculusTouch_Right_ThumbRest_Force");
const FKey FOculusKey::OculusTouch_Right_Stylus_Force("OculusTouch_Right_Stylus_Force");
const FKey FOculusKey::OculusTouch_Right_IndexTrigger_Curl("OculusTouch_Right_IndexTrigger_Curl");
const FKey FOculusKey::OculusTouch_Right_IndexTrigger_Slide("OculusTouch_Right_IndexTrigger_Slide");
const FKey FOculusKey::OculusTouch_Right_IndexTrigger_Force("OculusTouch_Right_IndexTrigger_Force");
const FKey FOculusKey::OculusRemote_DPad_Down("OculusRemote_DPad_Down");
const FKey FOculusKey::OculusRemote_DPad_Up("OculusRemote_DPad_Up");
const FKey FOculusKey::OculusRemote_DPad_Left("OculusRemote_DPad_Left");
const FKey FOculusKey::OculusRemote_DPad_Right("OculusRemote_DPad_Right");
const FKey FOculusKey::OculusRemote_Enter("OculusRemote_Enter");
const FKey FOculusKey::OculusRemote_Back("OculusRemote_Back");
const FKey FOculusKey::OculusRemote_VolumeUp("OculusRemote_VolumeUp");
const FKey FOculusKey::OculusRemote_VolumeDown("OculusRemote_VolumeDown");
const FKey FOculusKey::OculusRemote_Home("OculusRemote_Home");
const FKey FOculusKey::OculusHand_Left_ThumbPinch("OculusHand_Left_ThumbPinch");
const FKey FOculusKey::OculusHand_Left_IndexPinch("OculusHand_Left_IndexPinch");
const FKey FOculusKey::OculusHand_Left_MiddlePinch("OculusHand_Left_MiddlePinch");
const FKey FOculusKey::OculusHand_Left_RingPinch("OculusHand_Left_RingPinch");
const FKey FOculusKey::OculusHand_Left_PinkyPinch("OculusHand_Left_PinkPinch");
const FKey FOculusKey::OculusHand_Right_ThumbPinch("OculusHand_Right_ThumbPinch");
const FKey FOculusKey::OculusHand_Right_IndexPinch("OculusHand_Right_IndexPinch");
const FKey FOculusKey::OculusHand_Right_MiddlePinch("OculusHand_Right_MiddlePinch");
const FKey FOculusKey::OculusHand_Right_RingPinch("OculusHand_Right_RingPinch");
const FKey FOculusKey::OculusHand_Right_PinkyPinch("OculusHand_Right_PinkPinch");
const FKey FOculusKey::OculusHand_Left_SystemGesture("OculusHand_Left_SystemGesture");
const FKey FOculusKey::OculusHand_Right_SystemGesture("OculusHand_Right_SystemGesture");
const FKey FOculusKey::OculusHand_Left_ThumbPinchStrength("OculusHand_Left_ThumbPinchStrength");
const FKey FOculusKey::OculusHand_Left_IndexPinchStrength("OculusHand_Left_IndexPinchStrength");
const FKey FOculusKey::OculusHand_Left_MiddlePinchStrength("OculusHand_Left_MiddlePinchStrength");
const FKey FOculusKey::OculusHand_Left_RingPinchStrength("OculusHand_Left_RingPinchStrength");
const FKey FOculusKey::OculusHand_Left_PinkyPinchStrength("OculusHand_Left_PinkPinchStrength");
const FKey FOculusKey::OculusHand_Right_ThumbPinchStrength("OculusHand_Right_ThumbPinchStrength");
const FKey FOculusKey::OculusHand_Right_IndexPinchStrength("OculusHand_Right_IndexPinchStrength");
const FKey FOculusKey::OculusHand_Right_MiddlePinchStrength("OculusHand_Right_MiddlePinchStrength");
const FKey FOculusKey::OculusHand_Right_RingPinchStrength("OculusHand_Right_RingPinchStrength");
const FKey FOculusKey::OculusHand_Right_PinkyPinchStrength("OculusHand_Right_PinkPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_Thumbstick("OculusTouch_Left_Thumbstick");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_Trigger("OculusTouch_Left_Trigger");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_FaceButton1("OculusTouch_Left_FaceButton1");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_FaceButton2("OculusTouch_Left_FaceButton2");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_IndexPointing("OculusTouch_Left_IndexPointing");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_ThumbUp("OculusTouch_Left_ThumbUp");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Left_ThumbRest("OculusTouch_Left_ThumbRest");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_Thumbstick("OculusTouch_Right_Thumbstick");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_Trigger("OculusTouch_Right_Trigger");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_FaceButton1("OculusTouch_Right_FaceButton1");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_FaceButton2("OculusTouch_Right_FaceButton2");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_IndexPointing("OculusTouch_Right_IndexPointing");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_ThumbUp("OculusTouch_Right_ThumbUp");
const FOculusKeyNames::Type FOculusKeyNames::OculusTouch_Right_ThumbRest("OculusTouch_Right_ThumbRest");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_DPad_Down("OculusRemote_DPad_Down");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_DPad_Up("OculusRemote_DPad_Up");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_DPad_Left("OculusRemote_DPad_Left");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_DPad_Right("OculusRemote_DPad_Right");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_Enter("OculusRemote_Enter");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_Back("OculusRemote_Back");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_VolumeUp("OculusRemote_VolumeUp");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_VolumeDown("OculusRemote_VolumeDown");
const FOculusKeyNames::Type FOculusKeyNames::OculusRemote_Home("OculusRemote_Home");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_ThumbPinch("OculusHand_Left_ThumbPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_IndexPinch("OculusHand_Left_IndexPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_MiddlePinch("OculusHand_Left_MiddlePinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_RingPinch("OculusHand_Left_RingPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_PinkyPinch("OculusHand_Left_PinkPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_ThumbPinch("OculusHand_Right_ThumbPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_IndexPinch("OculusHand_Right_IndexPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_MiddlePinch("OculusHand_Right_MiddlePinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_RingPinch("OculusHand_Right_RingPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_PinkyPinch("OculusHand_Right_PinkPinch");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_SystemGesture("OculusHand_Left_SystemGesture");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_SystemGesture("OculusHand_Right_SystemGesture");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_ThumbPinchStrength("OculusHand_Left_ThumbPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_IndexPinchStrength("OculusHand_Left_IndexPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_MiddlePinchStrength("OculusHand_Left_MiddlePinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_RingPinchStrength("OculusHand_Left_RingPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Left_PinkyPinchStrength("OculusHand_Left_PinkPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_ThumbPinchStrength("OculusHand_Right_ThumbPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_IndexPinchStrength("OculusHand_Right_IndexPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_MiddlePinchStrength("OculusHand_Right_MiddlePinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_RingPinchStrength("OculusHand_Right_RingPinchStrength");
const FOculusKeyNames::Type FOculusKeyNames::OculusHand_Right_PinkyPinchStrength("OculusHand_Right_PinkPinchStrength");
/** Threshold for treating trigger pulls as button presses, from 0.0 to 1.0 */
float FOculusXRInput::TriggerThreshold = 0.8f;
/** Are Remote keys mapped to gamepad or not. */
bool FOculusXRInput::bRemoteKeysMappedToGamepad = true;
float FOculusXRInput::InitialButtonRepeatDelay = 0.2f;
float FOculusXRInput::ButtonRepeatDelay = 0.1f;
bool FOculusXRInput::bPulledHapticsDesc = false;
FOculusXRInput::FOculusXRInput(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
: MessageHandler(InMessageHandler)
, ControllerPairs()
{
// take care of backward compatibility of Remote with Gamepad
if (bRemoteKeysMappedToGamepad)
{
Remote.MapKeysToGamepad();
}
FOculusControllerPair& ControllerPair = *new (ControllerPairs) FOculusControllerPair();
// TODO: Map the oculus controllers uniquely instead of using the default
ControllerPair.DeviceId = IPlatformInputDeviceMapper::Get().GetDefaultInputDevice();
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
LocalTrackingSpaceRecenterCount = 0;
UE_LOG(LogOcInput, Log, TEXT("OculusXRInput is initialized"));
}
FOculusXRInput::~FOculusXRInput()
{
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
}
void FOculusXRInput::PreInit()
{
// Load the config, even if we failed to initialize a controller
LoadConfig();
// Register the FKeys
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_Thumbstick, LOCTEXT("OculusTouch_Left_Thumbstick", "Oculus Touch (L) Thumbstick CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_FaceButton1, LOCTEXT("OculusTouch_Left_FaceButton1", "Oculus Touch (L) X Button CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_Trigger, LOCTEXT("OculusTouch_Left_Trigger", "Oculus Touch (L) Trigger CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_FaceButton2, LOCTEXT("OculusTouch_Left_FaceButton2", "Oculus Touch (L) Y Button CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_IndexPointing, LOCTEXT("OculusTouch_Left_IndexPointing", "Oculus Touch (L) Pointing CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_ThumbUp, LOCTEXT("OculusTouch_Left_ThumbUp", "Oculus Touch (L) Thumb Up CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_ThumbRest, LOCTEXT("OculusTouch_Left_ThumbRest", "Oculus Touch (L) Thumb Rest CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_ThumbRest_Force, LOCTEXT("OculusTouch_Left_ThumbRest_Force", "Oculus Touch (L) Thumb Rest Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_Stylus_Force, LOCTEXT("OculusTouch_Left_Stylus_Force", "Oculus Touch (L) Stylus Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_IndexTrigger_Curl, LOCTEXT("OculusTouch_Left_IndexTrigger_Curl", "Oculus Touch (L) Trigger Curl CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_IndexTrigger_Slide, LOCTEXT("OculusTouch_Left_IndexTrigger_Slide", "Oculus Touch (L) Trigger Slide CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Left_IndexTrigger_Force, LOCTEXT("OculusTouch_Left_IndexTrigger_Force", "Oculus Touch (L) Trigger Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_Thumbstick, LOCTEXT("OculusTouch_Right_Thumbstick", "Oculus Touch (R) Thumbstick CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_FaceButton1, LOCTEXT("OculusTouch_Right_FaceButton1", "Oculus Touch (R) A Button CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_Trigger, LOCTEXT("OculusTouch_Right_Trigger", "Oculus Touch (R) Trigger CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_FaceButton2, LOCTEXT("OculusTouch_Right_FaceButton2", "Oculus Touch (R) B Button CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_IndexPointing, LOCTEXT("OculusTouch_Right_IndexPointing", "Oculus Touch (R) Pointing CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_ThumbUp, LOCTEXT("OculusTouch_Right_ThumbUp", "Oculus Touch (R) Thumb Up CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_ThumbRest, LOCTEXT("OculusTouch_Right_ThumbRest", "Oculus Touch (R) Thumb Rest CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_ThumbRest_Force, LOCTEXT("OculusTouch_Right_ThumbRest_Force", "Oculus Touch (R) Thumb Rest Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_Stylus_Force, LOCTEXT("OculusTouch_Right_Stylus_Force", "Oculus Touch (R) Stylus Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_IndexTrigger_Curl, LOCTEXT("OculusTouch_Right_IndexTrigger_Curl", "Oculus Touch (R) Trigger Curl CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_IndexTrigger_Slide, LOCTEXT("OculusTouch_Right_IndexTrigger_Slide", "Oculus Touch (R) Trigger Slide CapTouch"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusTouch_Right_IndexTrigger_Force, LOCTEXT("OculusTouch_Right_IndexTrigger_Force", "Oculus Touch (R) Trigger Force"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D | FKeyDetails::NotBlueprintBindableKey, "OculusTouch"));
EKeys::AddMenuCategoryDisplayInfo("OculusRemote", LOCTEXT("OculusRemoteSubCategory", "Oculus Remote"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_DPad_Up, LOCTEXT("OculusRemote_DPad_Up", "Oculus Remote D-pad Up"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_DPad_Down, LOCTEXT("OculusRemote_DPad_Down", "Oculus Remote D-pad Down"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_DPad_Left, LOCTEXT("OculusRemote_DPad_Left", "Oculus Remote D-pad Left"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_DPad_Right, LOCTEXT("OculusRemote_DPad_Right", "Oculus Remote D-pad Right"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_Enter, LOCTEXT("OculusRemote_Enter", "Oculus Remote Enter"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_Back, LOCTEXT("OculusRemote_Back", "Oculus Remote Back"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_VolumeUp, LOCTEXT("OculusRemote_VolumeUp", "Oculus Remote Volume Up"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_VolumeDown, LOCTEXT("OculusRemote_VolumeDown", "Oculus Remote Volume Down"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusRemote_Home, LOCTEXT("OculusRemote_Home", "Oculus Remote Home"), FKeyDetails::GamepadKey, "OculusRemote"));
EKeys::AddMenuCategoryDisplayInfo("OculusHand", LOCTEXT("OculusHandSubCategory", "Oculus Hand"), TEXT("GraphEditor.PadEvent_16x"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_ThumbPinch, LOCTEXT("OculusHand_Left_ThumbPinch", "Oculus Hand (L) Thumb Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_IndexPinch, LOCTEXT("OculusHand_Left_IndexPinch", "Oculus Hand (L) Index Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_MiddlePinch, LOCTEXT("OculusHand_Left_MiddlePinch", "Oculus Hand (L) Middle Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_RingPinch, LOCTEXT("OculusHand_Left_RingPinch", "Oculus Hand (L) Ring Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_PinkyPinch, LOCTEXT("OculusHand_Left_PinkyPinch", "Oculus Hand (L) Pinky Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_ThumbPinch, LOCTEXT("OculusHand_Right_ThumbPinch", "Oculus Hand (R) Thumb Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_IndexPinch, LOCTEXT("OculusHand_Right_IndexPinch", "Oculus Hand (R) Index Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_MiddlePinch, LOCTEXT("OculusHand_Right_MiddlePinch", "Oculus Hand (R) Middle Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_RingPinch, LOCTEXT("OculusHand_Right_RingPinch", "Oculus Hand (R) Ring Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_PinkyPinch, LOCTEXT("OculusHand_Right_PinkyPinch", "Oculus Hand (R) Pinky Pinch"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_SystemGesture, LOCTEXT("OculusHand_Left_SystemGesture", "Oculus Hand (L) System Gesture"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_SystemGesture, LOCTEXT("OculusHand_Right_SystemGesture", "Oculus Hand (R) System Gesture"), FKeyDetails::GamepadKey | FKeyDetails::NotBlueprintBindableKey, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_ThumbPinchStrength, LOCTEXT("OculusHand_Left_ThumbPinchStrength", "Oculus Hand (L) Thumb Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_IndexPinchStrength, LOCTEXT("OculusHand_Left_IndexPinchStrength", "Oculus Hand (L) Index Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_MiddlePinchStrength, LOCTEXT("OculusHand_Left_MiddlePinchStrength", "Oculus Hand (L) Middle Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_RingPinchStrength, LOCTEXT("OculusHand_Left_RingPinchStrength", "Oculus Hand (L) Ring Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Left_PinkyPinchStrength, LOCTEXT("OculusHand_Left_PinkyPinchStrength", "Oculus Hand (L) Pinky Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_ThumbPinchStrength, LOCTEXT("OculusHand_Right_ThumbPinchStrength", "Oculus Hand (R) Thumb Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_IndexPinchStrength, LOCTEXT("OculusHand_Right_IndexPinchStrength", "Oculus Hand (R) Index Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_MiddlePinchStrength, LOCTEXT("OculusHand_Right_MiddlePinchStrength", "Oculus Hand (R) Middle Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_RingPinchStrength, LOCTEXT("OculusHand_Right_RingPinchStrength", "Oculus Hand (R) Ring Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
EKeys::AddKey(FKeyDetails(FOculusKey::OculusHand_Right_PinkyPinchStrength, LOCTEXT("OculusHand_Right_PinkyPinchStrength", "Oculus Hand (R) Pinky Pinch Strength"), FKeyDetails::GamepadKey | FKeyDetails::Axis1D, "OculusHand"));
UE_LOG(LogOcInput, Log, TEXT("OculusXRInput pre-init called"));
}
void FOculusXRInput::LoadConfig()
{
const TCHAR* OculusTouchSettings = TEXT("OculusTouch.Settings");
float ConfigThreshold = TriggerThreshold;
if (GConfig->GetFloat(OculusTouchSettings, TEXT("TriggerThreshold"), ConfigThreshold, GEngineIni))
{
TriggerThreshold = ConfigThreshold;
}
const TCHAR* OculusRemoteSettings = TEXT("OculusRemote.Settings");
bool bConfigRemoteKeysMappedToGamepad;
if (GConfig->GetBool(OculusRemoteSettings, TEXT("bRemoteKeysMappedToGamepad"), bConfigRemoteKeysMappedToGamepad, GEngineIni))
{
bRemoteKeysMappedToGamepad = bConfigRemoteKeysMappedToGamepad;
}
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
}
void FOculusXRInput::Tick(float DeltaTime)
{
// Nothing to do when ticking, for now. SendControllerEvents() handles everything.
}
void FOculusXRInput::SendControllerEvents()
{
const double CurrentTime = FPlatformTime::Seconds();
const float AnalogButtonPressThreshold = TriggerThreshold;
float DeltaTime = 0.0;
if (StartTime < CurrentTime)
{
DeltaTime = (float)(CurrentTime - StartTime);
StartTime = CurrentTime;
}
if (IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && FApp::HasVRFocus())
{
if (MessageHandler.IsValid() && GEngine->XRSystem->GetHMDDevice())
{
FPlatformUserId PlatUser = IPlatformInputDeviceMapper::Get().GetPrimaryPlatformUser();
FInputDeviceId DeviceId = IPlatformInputDeviceMapper::Get().GetDefaultInputDevice();
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(GEngine->XRSystem->GetHMDDevice());
OculusXRHMD->StartGameFrame_GameThread();
ovrpControllerState6 OvrpControllerState;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetControllerState6(ovrpController_Remote, &OvrpControllerState)) && (OvrpControllerState.ConnectedControllerTypes & ovrpController_Remote))
{
for (int32 ButtonIndex = 0; ButtonIndex < (int32)EOculusRemoteControllerButton::TotalButtonCount; ++ButtonIndex)
{
FOculusButtonState& ButtonState = Remote.Buttons[ButtonIndex];
check(!ButtonState.Key.IsNone()); // is button's name initialized?
// Determine if the button is pressed down
bool bButtonPressed = false;
switch ((EOculusRemoteControllerButton)ButtonIndex)
{
case EOculusRemoteControllerButton::DPad_Up:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Up) != 0;
break;
case EOculusRemoteControllerButton::DPad_Down:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Down) != 0;
break;
case EOculusRemoteControllerButton::DPad_Left:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Left) != 0;
break;
case EOculusRemoteControllerButton::DPad_Right:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Right) != 0;
break;
case EOculusRemoteControllerButton::Enter:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Start) != 0;
break;
case EOculusRemoteControllerButton::Back:
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Back) != 0;
break;
case EOculusRemoteControllerButton::VolumeUp:
#ifdef SUPPORT_INTERNAL_BUTTONS
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_VolUp) != 0;
#endif
break;
case EOculusRemoteControllerButton::VolumeDown:
#ifdef SUPPORT_INTERNAL_BUTTONS
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_VolDown) != 0;
#endif
break;
case EOculusRemoteControllerButton::Home:
#ifdef SUPPORT_INTERNAL_BUTTONS
bButtonPressed = (OvrpControllerState.Buttons & ovrpButton_Home) != 0;
#endif
break;
default:
check(0); // unhandled button, shouldn't happen
break;
}
// Update button state
if (bButtonPressed != ButtonState.bIsPressed)
{
ButtonState.bIsPressed = bButtonPressed;
if (ButtonState.bIsPressed)
{
OnControllerButtonPressed(ButtonState, PlatUser, DeviceId, false);
// Set the timer for the first repeat
ButtonState.NextRepeatTime = CurrentTime + InitialButtonRepeatDelay;
}
else
{
OnControllerButtonReleased(ButtonState, PlatUser, DeviceId, false);
}
}
// Apply key repeat, if its time for that
if (ButtonState.bIsPressed && ButtonState.NextRepeatTime <= CurrentTime)
{
OnControllerButtonPressed(ButtonState, PlatUser, DeviceId, true);
// Set the timer for the next repeat
ButtonState.NextRepeatTime = CurrentTime + ButtonRepeatDelay;
}
}
}
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetControllerState6((ovrpController)(ovrpController_LTrackedRemote | ovrpController_RTrackedRemote | ovrpController_Touch), &OvrpControllerState)))
{
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: ButtonState = 0x%X"), OvrpControllerState.Buttons);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: Touches = 0x%X"), OvrpControllerState.Touches);
// If using touch controllers (Quest) use the local tracking space recentering as a signal for recenter
if ((OvrpControllerState.ConnectedControllerTypes & ovrpController_LTouch) != 0 || (OvrpControllerState.ConnectedControllerTypes & ovrpController_RTouch) != 0)
{
int32 recenterCount = 0;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetLocalTrackingSpaceRecenterCount(&recenterCount)))
{
if (LocalTrackingSpaceRecenterCount != recenterCount)
{
FCoreDelegates::VRControllerRecentered.Broadcast();
LocalTrackingSpaceRecenterCount = recenterCount;
}
}
}
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
FPlatformUserId PlatformUser = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(ControllerPair.DeviceId);
for (int32 HandIndex = 0; HandIndex < UE_ARRAY_COUNT(ControllerPair.TouchControllerStates); ++HandIndex)
{
FOculusTouchControllerState& State = ControllerPair.TouchControllerStates[HandIndex];
bool bIsLeft = (HandIndex == (int32)EControllerHand::Left);
bool bIsMobileController = bIsLeft ? (OvrpControllerState.ConnectedControllerTypes & ovrpController_LTrackedRemote) != 0 : (OvrpControllerState.ConnectedControllerTypes & ovrpController_RTrackedRemote) != 0;
bool bIsTouchController = bIsLeft ? (OvrpControllerState.ConnectedControllerTypes & ovrpController_LTouch) != 0 : (OvrpControllerState.ConnectedControllerTypes & ovrpController_RTouch) != 0;
bool bIsCurrentlyTracked = bIsMobileController || bIsTouchController;
if (bIsCurrentlyTracked)
{
ovrpNode OvrpNode = (HandIndex == (int32)EControllerHand::Left) ? ovrpNode_HandLeft : ovrpNode_HandRight;
State.bIsConnected = true;
ovrpBool bResult = true;
State.bIsPositionTracked = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePositionTracked2(OvrpNode, &bResult)) && bResult;
State.bIsPositionValid = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePositionValid(OvrpNode, &bResult)) && bResult;
State.bIsOrientationTracked = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodeOrientationTracked2(OvrpNode, &bResult)) && bResult;
State.bIsOrientationValid = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodeOrientationValid(OvrpNode, &bResult)) && bResult;
const float OvrTriggerAxis = OvrpControllerState.IndexTrigger[HandIndex];
const float OvrGripAxis = OvrpControllerState.HandTrigger[HandIndex];
const float OvrThumbRestForce = OvrpControllerState.ThumbRestForce[HandIndex];
const float OvrStylusForce = OvrpControllerState.StylusForce[HandIndex];
const float OvrIndexTriggerCurl = OvrpControllerState.IndexTriggerCurl[HandIndex];
const float OvrIndexTriggerSlide = OvrpControllerState.IndexTriggerSlide[HandIndex];
const float OvrIndexTriggerForce = OvrpControllerState.IndexTriggerForce[HandIndex];
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: IndexTrigger[%d] = %f"), int(HandIndex), OvrTriggerAxis);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: HandTrigger[%d] = %f"), int(HandIndex), OvrGripAxis);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: ThumbStick[%d] = { %f, %f }"), int(HandIndex), OvrpControllerState.Thumbstick[HandIndex].x, OvrpControllerState.Thumbstick[HandIndex].y);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: ThumbRestForce[%d] = %f"), int(HandIndex), OvrThumbRestForce);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: StylusForce[%d] = %f"), int(HandIndex), OvrStylusForce);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: IndexTriggerCurl[%d] = %f"), int(HandIndex), OvrIndexTriggerCurl);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: IndexTriggerSlide[%d] = %f"), int(HandIndex), OvrIndexTriggerSlide);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: IndexTriggerForce[%d] = %f"), int(HandIndex), OvrIndexTriggerForce);
if (bIsMobileController)
{
if (OvrpControllerState.RecenterCount[HandIndex] != State.RecenterCount)
{
State.RecenterCount = OvrpControllerState.RecenterCount[HandIndex];
FCoreDelegates::VRControllerRecentered.Broadcast();
}
}
if (OvrTriggerAxis != State.TriggerAxis)
{
State.TriggerAxis = OvrTriggerAxis;
MessageHandler->OnControllerAnalog(bIsLeft ? EKeys::OculusTouch_Left_Trigger_Axis.GetFName() : EKeys::OculusTouch_Right_Trigger_Axis.GetFName(), PlatformUser, ControllerPair.DeviceId, State.TriggerAxis);
}
if (OvrGripAxis != State.GripAxis)
{
State.GripAxis = OvrGripAxis;
MessageHandler->OnControllerAnalog(bIsLeft ? EKeys::OculusTouch_Left_Grip_Axis.GetFName() : EKeys::OculusTouch_Right_Grip_Axis.GetFName(), PlatformUser, ControllerPair.DeviceId, State.GripAxis);
}
ovrpVector2f ThumbstickValue = OvrpControllerState.Thumbstick[HandIndex];
ovrpVector2f TouchpadValue = OvrpControllerState.Touchpad[HandIndex];
if (ThumbstickValue.x != State.ThumbstickAxes.X)
{
State.ThumbstickAxes.X = ThumbstickValue.x;
MessageHandler->OnControllerAnalog(bIsLeft ? EKeys::OculusTouch_Left_Thumbstick_X.GetFName() : EKeys::OculusTouch_Right_Thumbstick_X.GetFName(), PlatformUser, ControllerPair.DeviceId, State.ThumbstickAxes.X);
}
if (ThumbstickValue.y != State.ThumbstickAxes.Y)
{
State.ThumbstickAxes.Y = ThumbstickValue.y;
MessageHandler->OnControllerAnalog(bIsLeft ? EKeys::OculusTouch_Left_Thumbstick_Y.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Y.GetFName(), PlatformUser, ControllerPair.DeviceId, State.ThumbstickAxes.Y);
}
if (TouchpadValue.x != State.TouchpadAxes.X)
{
State.TouchpadAxes.X = TouchpadValue.x;
}
if (TouchpadValue.y != State.TouchpadAxes.Y)
{
State.TouchpadAxes.Y = TouchpadValue.y;
}
if (OvrThumbRestForce != State.ThumbRestForce)
{
State.ThumbRestForce = OvrThumbRestForce;
MessageHandler->OnControllerAnalog(bIsLeft ? FOculusKey::OculusTouch_Left_ThumbRest_Force.GetFName() : FOculusKey::OculusTouch_Right_ThumbRest_Force.GetFName(), PlatformUser, ControllerPair.DeviceId, State.ThumbRestForce);
}
if (OvrStylusForce != State.StylusForce)
{
State.StylusForce = OvrStylusForce;
MessageHandler->OnControllerAnalog(bIsLeft ? FOculusKey::OculusTouch_Left_Stylus_Force.GetFName() : FOculusKey::OculusTouch_Right_Stylus_Force.GetFName(), PlatformUser, ControllerPair.DeviceId, State.StylusForce);
}
if (OvrIndexTriggerCurl != State.IndexTriggerCurl)
{
State.IndexTriggerCurl = OvrIndexTriggerCurl;
MessageHandler->OnControllerAnalog(bIsLeft ? FOculusKey::OculusTouch_Left_IndexTrigger_Curl.GetFName() : FOculusKey::OculusTouch_Right_IndexTrigger_Curl.GetFName(), PlatformUser, ControllerPair.DeviceId, State.IndexTriggerCurl);
}
if (OvrIndexTriggerSlide != State.IndexTriggerSlide)
{
State.IndexTriggerSlide = OvrIndexTriggerSlide;
MessageHandler->OnControllerAnalog(bIsLeft ? FOculusKey::OculusTouch_Left_IndexTrigger_Slide.GetFName() : FOculusKey::OculusTouch_Right_IndexTrigger_Slide.GetFName(), PlatformUser, ControllerPair.DeviceId, State.IndexTriggerSlide);
}
if (OvrIndexTriggerForce != State.IndexTriggerForce)
{
State.IndexTriggerForce = OvrIndexTriggerForce;
MessageHandler->OnControllerAnalog(bIsLeft ? FOculusKey::OculusTouch_Left_IndexTrigger_Force.GetFName() : FOculusKey::OculusTouch_Right_IndexTrigger_Force.GetFName(), PlatformUser, ControllerPair.DeviceId, State.IndexTriggerForce);
}
for (int32 ButtonIndex = 0; ButtonIndex < (int32)EOculusTouchControllerButton::TotalButtonCount; ++ButtonIndex)
{
FOculusButtonState& ButtonState = State.Buttons[ButtonIndex];
check(!ButtonState.Key.IsNone()); // is button's name initialized?
// Determine if the button is pressed down
bool bButtonPressed = false;
switch ((EOculusTouchControllerButton)ButtonIndex)
{
case EOculusTouchControllerButton::Trigger:
bButtonPressed = State.TriggerAxis >= AnalogButtonPressThreshold;
break;
case EOculusTouchControllerButton::Grip:
bButtonPressed = State.GripAxis >= AnalogButtonPressThreshold;
break;
case EOculusTouchControllerButton::XA:
bButtonPressed = bIsLeft ? (OvrpControllerState.Buttons & ovrpButton_X) != 0 : (OvrpControllerState.Buttons & ovrpButton_A) != 0;
break;
case EOculusTouchControllerButton::YB:
bButtonPressed = bIsLeft ? (OvrpControllerState.Buttons & ovrpButton_Y) != 0 : (OvrpControllerState.Buttons & ovrpButton_B) != 0;
break;
case EOculusTouchControllerButton::Thumbstick:
bButtonPressed = bIsLeft ? (OvrpControllerState.Buttons & ovrpButton_LThumb) != 0 : (OvrpControllerState.Buttons & ovrpButton_RThumb) != 0;
break;
case EOculusTouchControllerButton::Thumbstick_Up:
if (bIsTouchController && State.ThumbstickAxes.Size() > 0.7f || bIsMobileController && State.Buttons[(int)EOculusTouchControllerButton::Thumbstick].bIsPressed && State.ThumbstickAxes.Size() > 0.5f)
{
float Angle = FMath::Atan2(State.ThumbstickAxes.Y, State.ThumbstickAxes.X);
bButtonPressed = Angle >= (1.0f / 8.0f) * PI && Angle <= (7.0f / 8.0f) * PI;
}
break;
case EOculusTouchControllerButton::Thumbstick_Down:
if (bIsTouchController && State.ThumbstickAxes.Size() > 0.7f || bIsMobileController && State.Buttons[(int)EOculusTouchControllerButton::Thumbstick].bIsPressed && State.ThumbstickAxes.Size() > 0.5f)
{
float Angle = FMath::Atan2(State.ThumbstickAxes.Y, State.ThumbstickAxes.X);
bButtonPressed = Angle >= (-7.0f / 8.0f) * PI && Angle <= (-1.0f / 8.0f) * PI;
}
break;
case EOculusTouchControllerButton::Thumbstick_Left:
if (bIsTouchController && State.ThumbstickAxes.Size() > 0.7f || bIsMobileController && State.Buttons[(int)EOculusTouchControllerButton::Thumbstick].bIsPressed && State.ThumbstickAxes.Size() > 0.5f)
{
float Angle = FMath::Atan2(State.ThumbstickAxes.Y, State.ThumbstickAxes.X);
bButtonPressed = Angle <= (-5.0f / 8.0f) * PI || Angle >= (5.0f / 8.0f) * PI;
}
break;
case EOculusTouchControllerButton::Thumbstick_Right:
if (bIsTouchController && State.ThumbstickAxes.Size() > 0.7f || bIsMobileController && State.Buttons[(int)EOculusTouchControllerButton::Thumbstick].bIsPressed && State.ThumbstickAxes.Size() > 0.5f)
{
float Angle = FMath::Atan2(State.ThumbstickAxes.Y, State.ThumbstickAxes.X);
bButtonPressed = Angle >= (-3.0f / 8.0f) * PI && Angle <= (3.0f / 8.0f) * PI;
}
break;
case EOculusTouchControllerButton::Menu:
bButtonPressed = bIsLeft && (OvrpControllerState.Buttons & ovrpButton_Start);
break;
case EOculusTouchControllerButton::Thumbstick_Touch:
bButtonPressed = bIsLeft ? (OvrpControllerState.Touches & ovrpTouch_LThumb) != 0 : (OvrpControllerState.Touches & ovrpTouch_RThumb) != 0;
break;
case EOculusTouchControllerButton::Trigger_Touch:
bButtonPressed = bIsLeft ? (OvrpControllerState.Touches & ovrpTouch_LIndexTrigger) != 0 : (OvrpControllerState.Touches & ovrpTouch_RIndexTrigger) != 0;
break;
case EOculusTouchControllerButton::XA_Touch:
bButtonPressed = bIsLeft ? (OvrpControllerState.Touches & ovrpTouch_X) != 0 : (OvrpControllerState.Touches & ovrpTouch_A) != 0;
break;
case EOculusTouchControllerButton::YB_Touch:
bButtonPressed = bIsLeft ? (OvrpControllerState.Touches & ovrpTouch_Y) != 0 : (OvrpControllerState.Touches & ovrpTouch_B) != 0;
break;
default:
check(0);
break;
}
// Update button state
if (bButtonPressed != ButtonState.bIsPressed)
{
ButtonState.bIsPressed = bButtonPressed;
if (ButtonState.bIsPressed)
{
OnControllerButtonPressed(ButtonState, PlatformUser, ControllerPair.DeviceId, false);
// Set the timer for the first repeat
ButtonState.NextRepeatTime = CurrentTime + InitialButtonRepeatDelay;
}
else
{
OnControllerButtonReleased(ButtonState, PlatformUser, ControllerPair.DeviceId, false);
}
}
// Apply key repeat, if its time for that
if (ButtonState.bIsPressed && ButtonState.NextRepeatTime <= CurrentTime)
{
OnControllerButtonPressed(ButtonState, PlatformUser, ControllerPair.DeviceId, true);
// Set the timer for the next repeat
ButtonState.NextRepeatTime = CurrentTime + ButtonRepeatDelay;
}
}
// Handle Capacitive States
for (int32 CapTouchIndex = 0; CapTouchIndex < (int32)EOculusTouchCapacitiveAxes::TotalAxisCount; ++CapTouchIndex)
{
FOculusAxisState& CapState = State.CapacitiveAxes[CapTouchIndex];
float CurrentAxisVal = 0.f;
switch ((EOculusTouchCapacitiveAxes)CapTouchIndex)
{
case EOculusTouchCapacitiveAxes::XA:
{
const uint32 mask = (bIsLeft) ? ovrpTouch_X : ovrpTouch_A;
CurrentAxisVal = (OvrpControllerState.Touches & mask) != 0 ? 1.f : 0.f;
break;
}
case EOculusTouchCapacitiveAxes::YB:
{
const uint32 mask = (bIsLeft) ? ovrpTouch_Y : ovrpTouch_B;
CurrentAxisVal = (OvrpControllerState.Touches & mask) != 0 ? 1.f : 0.f;
break;
}
case EOculusTouchCapacitiveAxes::Thumbstick:
{
const uint32 mask = bIsMobileController ? ((bIsLeft) ? ovrpTouch_LTouchpad : ovrpTouch_RTouchpad) : ((bIsLeft) ? ovrpTouch_LThumb : ovrpTouch_RThumb);
CurrentAxisVal = (OvrpControllerState.Touches & mask) != 0 ? 1.f : 0.f;
break;
}
case EOculusTouchCapacitiveAxes::Trigger:
{
const uint32 mask = (bIsLeft) ? ovrpTouch_LIndexTrigger : ovrpTouch_RIndexTrigger;
CurrentAxisVal = (OvrpControllerState.Touches & mask) != 0 ? 1.f : 0.f;
break;
}
case EOculusTouchCapacitiveAxes::IndexPointing:
{
const uint32 mask = (bIsLeft) ? ovrpNearTouch_LIndexTrigger : ovrpNearTouch_RIndexTrigger;
CurrentAxisVal = (OvrpControllerState.NearTouches & mask) != 0 ? 0.f : 1.f;
break;
}
case EOculusTouchCapacitiveAxes::ThumbUp:
{
const uint32 mask = (bIsLeft) ? ovrpNearTouch_LThumbButtons : ovrpNearTouch_RThumbButtons;
CurrentAxisVal = (OvrpControllerState.NearTouches & mask) != 0 ? 0.f : 1.f;
break;
}
case EOculusTouchCapacitiveAxes::ThumbRest:
{
const uint32 mask = (bIsLeft) ? ovrpTouch_LThumbRest : ovrpTouch_RThumbRest;
CurrentAxisVal = (OvrpControllerState.Touches & mask) != 0 ? 1.f : 0.f;
break;
}
default:
check(0);
}
if (CurrentAxisVal != CapState.State)
{
MessageHandler->OnControllerAnalog(CapState.Axis, PlatformUser, ControllerPair.DeviceId, CurrentAxisVal);
CapState.State = CurrentAxisVal;
}
}
ProcessHaptics(DeltaTime);
}
else
{
// Controller isn't available right now.
if (CVarOculusResetUntrackedInputStates.GetValueOnAnyThread())
{
//Zero out input state, so that if controller comes back it will send fresh event deltas
State = FOculusTouchControllerState((EControllerHand)HandIndex);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: Controller for the hand %d is not tracked and input states are reset"), int(HandIndex));
}
else
{
//Cache input state, so that if controller comes back it will send event deltas
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: Controller for the hand %d is not tracked and input states are saved"), int(HandIndex));
}
}
}
}
}
else
{
// Controller isn't available right now. Zero out input state, so that if it comes back it will send fresh event deltas
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
for (int32 HandIndex = 0; HandIndex < UE_ARRAY_COUNT(ControllerPair.TouchControllerStates); ++HandIndex)
{
FOculusTouchControllerState& State = ControllerPair.TouchControllerStates[HandIndex];
State = FOculusTouchControllerState((EControllerHand)HandIndex);
}
}
}
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetControllerState6((ovrpController)(ovrpController_LHand | ovrpController_RHand | ovrpController_Hands), &OvrpControllerState)))
{
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
FPlatformUserId PlatformUser = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(ControllerPair.DeviceId);
for (int32 HandIndex = 0; HandIndex < UE_ARRAY_COUNT(ControllerPair.HandControllerStates); ++HandIndex)
{
FOculusHandControllerState& State = ControllerPair.HandControllerStates[HandIndex];
bool bIsLeft = (HandIndex == (int32)EControllerHand::Left);
bool bIsCurrentlyTracked = bIsLeft ? (OvrpControllerState.ConnectedControllerTypes & ovrpController_LHand) != 0 : (OvrpControllerState.ConnectedControllerTypes & ovrpController_RHand) != 0;
if (bIsCurrentlyTracked)
{
State.bIsConnected = true;
ovrpBool bResult = true;
// Hand Tracking requires the frame number for accurate results
OculusXRHMD::FGameFrame* CurrentFrame;
if (IsInGameThread())
{
CurrentFrame = OculusXRHMD->GetNextFrameToRender();
}
else
{
CurrentFrame = OculusXRHMD->GetFrame_RenderThread();
}
// Poll for Hand Tracking State
ovrpHandState HandState;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetHandState2(ovrpStep_Render, CurrentFrame ? CurrentFrame->FrameNumber : OVRP_CURRENT_FRAMEINDEX, (ovrpHand)HandIndex, &HandState)))
{
// Update various data about hands
State.HandScale = HandState.HandScale;
// Update Bone Rotations
for (uint32 BoneIndex = 0; BoneIndex < UE_ARRAY_COUNT(State.BoneRotations); BoneIndex++)
{
ovrpQuatf RawRotation = HandState.BoneRotations[BoneIndex];
FQuat BoneRotation = FOculusHandTracking::OvrBoneQuatToFQuat(RawRotation);
BoneRotation.Normalize();
State.BoneRotations[BoneIndex] = BoneRotation;
}
// Update Pinch State and Pinch Strength
bool bTracked = (HandState.Status & ovrpHandStatus_HandTracked) != 0;
State.TrackingConfidence = FOculusHandTracking::ToEOculusXRTrackingConfidence(HandState.HandConfidence);
State.bIsPositionTracked = bTracked && State.TrackingConfidence == EOculusXRTrackingConfidence::High;
State.bIsPositionValid = bTracked;
State.bIsOrientationTracked = bTracked && State.TrackingConfidence == EOculusXRTrackingConfidence::High;
State.bIsOrientationValid = bTracked;
State.bIsPointerPoseValid = (HandState.Status & ovrpHandStatus_InputValid) != 0;
ovrpPosef PointerPose = HandState.PointerPose;
State.PointerPose.SetTranslation(OculusXRHMD::ToFVector(PointerPose.Position));
State.PointerPose.SetRotation(OculusXRHMD::ToFQuat(PointerPose.Orientation));
State.bIsDominantHand = (HandState.Status & ovrpHandStatus_DominantHand) != 0;
// Poll for finger confidence
for (uint32 FingerIndex = 0; FingerIndex < (int32)EOculusHandAxes::TotalAxisCount; FingerIndex++)
{
State.FingerConfidences[FingerIndex] = FOculusHandTracking::ToEOculusXRTrackingConfidence(HandState.FingerConfidences[FingerIndex]);
}
// Poll for finger pinches
for (uint32 FingerIndex = 0; FingerIndex < (uint32)EOculusHandButton::TotalButtonCount; FingerIndex++)
{
FOculusButtonState& PinchState = State.HandButtons[FingerIndex];
check(!PinchState.Key.IsNone());
bool bPressed = false;
if (FingerIndex < (uint32)EOculusHandButton::System)
{
bPressed = (((uint32)HandState.Pinches & (1 << FingerIndex)) != 0);
bPressed &= (HandState.HandConfidence == ovrpTrackingConfidence_High) && (HandState.FingerConfidences[FingerIndex] == ovrpTrackingConfidence_High);
}
else if (FingerIndex == (uint32)EOculusHandButton::System)
{
bPressed = (HandState.Status & ovrpHandStatus_SystemGestureInProgress) != 0;
}
else
{
bPressed = (OvrpControllerState.Buttons & ovrpButton_Start) != 0 && !State.bIsDominantHand;
}
if (bPressed != PinchState.bIsPressed)
{
PinchState.bIsPressed = bPressed;
if (PinchState.bIsPressed)
{
OnControllerButtonPressed(PinchState, PlatformUser, ControllerPair.DeviceId, false);
}
else
{
OnControllerButtonReleased(PinchState, PlatformUser, ControllerPair.DeviceId, false);
}
}
}
// Poll for finger strength
for (uint32 FingerIndex = 0; FingerIndex < (uint32)EOculusHandAxes::TotalAxisCount; FingerIndex++)
{
FOculusAxisState& PinchStrength = State.HandAxes[FingerIndex];
check(!PinchStrength.Axis.IsNone());
float PinchValue = 0.0f;
if (HandState.HandConfidence == ovrpTrackingConfidence_High)
{
PinchValue = HandState.PinchStrength[FingerIndex];
}
if (PinchValue != PinchStrength.State)
{
MessageHandler->OnControllerAnalog(PinchStrength.Axis, PlatformUser, ControllerPair.DeviceId, PinchValue);
PinchStrength.State = PinchValue;
}
}
}
}
else
{
// Hand isn't available right now.
if (CVarOculusResetUntrackedInputStates.GetValueOnAnyThread())
{
//Zero out input state, so that if hand comes back it will send fresh event deltas
State = FOculusHandControllerState((EControllerHand)HandIndex);
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: Hand for the hand %d is not tracked and input states are reset"), int32(HandIndex));
}
else
{
//Cache input state, so that if hand comes back it will send event deltas
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT("SendControllerEvents: Hand for the hand %d is not tracked and input states are saved"), int(HandIndex));
}
}
}
}
}
else
{
// Hands are not availble right now, zero out the hand state
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
for (int32 HandIndex = 0; HandIndex < UE_ARRAY_COUNT(ControllerPair.HandControllerStates); ++HandIndex)
{
FOculusHandControllerState& State = ControllerPair.HandControllerStates[HandIndex];
State = FOculusHandControllerState((EControllerHand)HandIndex);
}
}
}
}
}
UE_CLOG(OVR_DEBUG_LOGGING, LogOcInput, Log, TEXT(""));
}
void FOculusXRInput::SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
MessageHandler = InMessageHandler;
}
bool FOculusXRInput::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
// No exec commands supported, for now.
return false;
}
void FOculusXRInput::SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value)
{
const EControllerHand Hand = (ChannelType == FForceFeedbackChannelType::LEFT_LARGE || ChannelType == FForceFeedbackChannelType::LEFT_SMALL) ? EControllerHand::Left : EControllerHand::Right;
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
FOculusTouchControllerState& ControllerState = ControllerPair.TouchControllerStates[(int32)Hand];
if (ControllerState.bPlayingHapticEffect)
{
continue;
}
// @todo: The SMALL channel controls frequency, the LARGE channel controls amplitude. This is a bit of a weird fit.
if (ChannelType == FForceFeedbackChannelType::LEFT_SMALL || ChannelType == FForceFeedbackChannelType::RIGHT_SMALL)
{
ControllerState.ForceFeedbackHapticFrequency = Value;
}
else
{
ControllerState.ForceFeedbackHapticAmplitude = Value;
}
UpdateForceFeedback(ControllerPair, Hand);
break;
}
}
}
void FOculusXRInput::SetChannelValues(int32 ControllerId, const FForceFeedbackValues& Values)
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
// @todo: The SMALL channel controls frequency, the LARGE channel controls amplitude. This is a bit of a weird fit.
FOculusTouchControllerState& LeftControllerState = ControllerPair.TouchControllerStates[(int32)EControllerHand::Left];
if (!LeftControllerState.bPlayingHapticEffect)
{
LeftControllerState.ForceFeedbackHapticFrequency = Values.LeftSmall;
LeftControllerState.ForceFeedbackHapticAmplitude = Values.LeftLarge;
UpdateForceFeedback(ControllerPair, EControllerHand::Left);
}
FOculusTouchControllerState& RightControllerState = ControllerPair.TouchControllerStates[(int32)EControllerHand::Right];
if (!RightControllerState.bPlayingHapticEffect)
{
RightControllerState.ForceFeedbackHapticFrequency = Values.RightSmall;
RightControllerState.ForceFeedbackHapticAmplitude = Values.RightLarge;
UpdateForceFeedback(ControllerPair, EControllerHand::Right);
}
}
}
}
bool FOculusXRInput::SupportsForceFeedback(int32 ControllerId)
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
const FOculusTouchControllerState& ControllerStateLeft = ControllerPair.TouchControllerStates[(int32)EControllerHand::Left];
const FOculusTouchControllerState& ControllerStateRight = ControllerPair.TouchControllerStates[(int32)EControllerHand::Right];
if (!(ControllerStateLeft.bIsConnected || ControllerStateRight.bIsConnected))
{
// neither hand connected, won't be receiving force feedback
continue;
}
if (IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
// available so could receive feedback
return true;
}
}
}
// not handling force feedback
return false;
}
void FOculusXRInput::UpdateForceFeedback(const FOculusControllerPair& ControllerPair, const EControllerHand Hand)
{
const FOculusTouchControllerState& ControllerState = ControllerPair.TouchControllerStates[(int32)Hand];
if (ControllerState.bIsConnected && !ControllerState.bPlayingHapticEffect)
{
if (IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && FApp::HasVRFocus())
{
ovrpControllerState6 OvrpControllerState;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetControllerState6((ovrpController)(ovrpController_Active | ovrpController_LTrackedRemote | ovrpController_RTrackedRemote), &OvrpControllerState)) && (OvrpControllerState.ConnectedControllerTypes & (ovrpController_Touch | ovrpController_LTrackedRemote | ovrpController_RTrackedRemote)))
{
float FreqMin, FreqMax = 0.f;
GetHapticFrequencyRange(FreqMin, FreqMax);
// Map the [0.0 - 1.0] range to a useful range of frequencies for the Oculus controllers
const float ActualFrequency = FMath::Lerp(FreqMin, FreqMax, FMath::Clamp(ControllerState.ForceFeedbackHapticFrequency, 0.0f, 1.0f));
// Oculus SDK wants amplitude values between 0.0 and 1.0
const float ActualAmplitude = ControllerState.ForceFeedbackHapticAmplitude * GetHapticAmplitudeScale();
ovrpController OvrController = ovrpController_None;
if (OvrpControllerState.ConnectedControllerTypes & (ovrpController_Touch))
{
OvrController = (Hand == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
}
else if (OvrpControllerState.ConnectedControllerTypes & (ovrpController_LTrackedRemote | ovrpController_RTrackedRemote))
{
OvrController = (Hand == EControllerHand::Left) ? ovrpController_LTrackedRemote : ovrpController_RTrackedRemote;
}
static float LastAmplitudeSent = -1;
if (ActualAmplitude != LastAmplitudeSent)
{
ovrpHapticsLocation hapticsLocationMask = ovrpHapticsLocation::ovrpHapticsLocation_Hand;
FOculusXRHMDModule::GetPluginWrapper().SetControllerLocalizedVibration(OvrController, hapticsLocationMask, ActualFrequency, ActualAmplitude);
LastAmplitudeSent = ActualAmplitude;
}
}
}
}
}
bool FOculusXRInput::OnControllerButtonPressed(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat)
{
bool result = MessageHandler->OnControllerButtonPressed(ButtonState.Key, UserId, DeviceId, IsRepeat);
if (!ButtonState.EmulatedKey.IsNone())
{
MessageHandler->OnControllerButtonPressed(ButtonState.EmulatedKey, UserId, DeviceId, IsRepeat);
}
return result;
}
bool FOculusXRInput::OnControllerButtonReleased(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat)
{
bool result = MessageHandler->OnControllerButtonReleased(ButtonState.Key, UserId, DeviceId, IsRepeat);
if (!ButtonState.EmulatedKey.IsNone())
{
MessageHandler->OnControllerButtonReleased(ButtonState.EmulatedKey, UserId, DeviceId, IsRepeat);
}
return result;
}
FName FOculusXRInput::GetMotionControllerDeviceTypeName() const
{
const static FName DefaultName(TEXT("OculusXRInputDevice"));
return DefaultName;
}
// Supported motion sources
const TMap<FName, ovrpNode> MotionSourceMap{
{ FName("Left"), ovrpNode_HandLeft },
{ FName("Right"), ovrpNode_HandRight },
{ FName("LeftGrip"), ovrpNode_HandLeft },
{ FName("RightGrip"), ovrpNode_HandRight },
{ FName("LeftAim"), ovrpNode_HandLeft },
{ FName("RightAim"), ovrpNode_HandRight },
// Sometimes we can get an enum as the motion source name
{ FName("EControllerHand::Left"), ovrpNode_HandLeft },
{ FName("EControllerHand::Right"), ovrpNode_HandRight }
};
#if UE_VERSION_OLDER_THAN(5, 3, 0)
bool FOculusXRInput::GetControllerOrientationAndPosition(const int32 ControllerIndex, const EControllerHand DeviceHand, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const
{
FName MotionSource;
switch (DeviceHand)
{
case EControllerHand::Left:
MotionSource = FName("Left");
break;
case EControllerHand::Right:
MotionSource = FName("Right");
break;
default:
MotionSource = FName("Unknown");
break;
}
return GetControllerOrientationAndPosition(ControllerIndex, MotionSource, OutOrientation, OutPosition, WorldToMetersScale);
}
ETrackingStatus FOculusXRInput::GetControllerTrackingStatus(const int32 ControllerIndex, const EControllerHand DeviceHand) const
{
ETrackingStatus TrackingStatus = ETrackingStatus::NotTracked;
if (DeviceHand != EControllerHand::Left && DeviceHand != EControllerHand::Right)
{
return TrackingStatus;
}
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerIndex);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerIndex, InPlatformUser, InDeviceId);
for (const FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
const FOculusTouchControllerState& ControllerState = ControllerPair.TouchControllerStates[(int32)DeviceHand];
if (ControllerState.bIsConnected)
{
if (ControllerState.bIsPositionTracked && ControllerState.bIsOrientationTracked)
{
TrackingStatus = ETrackingStatus::Tracked;
}
else if (ControllerState.bIsPositionValid && ControllerState.bIsOrientationValid)
{
TrackingStatus = ETrackingStatus::InertialOnly;
}
break;
}
const FOculusHandControllerState& HandState = ControllerPair.HandControllerStates[(int32)DeviceHand];
if (HandState.bIsConnected)
{
if (HandState.bIsPositionTracked && HandState.bIsOrientationTracked)
{
TrackingStatus = ETrackingStatus::Tracked;
}
break;
}
}
}
return TrackingStatus;
}
#endif
bool FOculusXRInput::GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerIndex);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerIndex, InPlatformUser, InDeviceId);
// Don't do renderthread pose update if MRC is active due to controller jitter issues with SceneCaptures
if (IsInGameThread() || !UOculusXRMRFunctionLibrary::IsMrcActive())
{
for (const FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
if (MotionSourceMap.Contains(MotionSource))
{
if (IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(GEngine->XRSystem->GetHMDDevice());
ovrpNode Node = MotionSourceMap[MotionSource];
ovrpBool bResult = true;
bool bIsPositionValid = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePositionValid(Node, &bResult)) && bResult;
bool bIsOrientationValid = OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodeOrientationValid(Node, &bResult)) && bResult;
if (bIsPositionValid || bIsOrientationValid)
{
OculusXRHMD::FSettings* Settings;
OculusXRHMD::FGameFrame* CurrentFrame;
if (IsInGameThread())
{
Settings = OculusXRHMD->GetSettings();
CurrentFrame = OculusXRHMD->GetNextFrameToRender();
}
else
{
Settings = OculusXRHMD->GetSettings_RenderThread();
CurrentFrame = OculusXRHMD->GetFrame_RenderThread();
}
if (Settings)
{
ovrpPoseStatef InPoseState;
OculusXRHMD::FPose OutPose;
EOculusXRControllerPoseAlignment ControllerPoseAlignment = Settings->ControllerPoseAlignment;
switch (CVarOculusControllerPose.GetValueOnAnyThread())
{
case 1:
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Default;
break;
case 2:
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Grip;
break;
case 3:
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Aim;
break;
default:
break;
}
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePoseState3(ovrpStep_Render, CurrentFrame ? CurrentFrame->FrameNumber : OVRP_CURRENT_FRAMEINDEX, Node, &InPoseState)) && OculusXRHMD->ConvertPose_Internal(InPoseState.Pose, OutPose, Settings, WorldToMetersScale))
{
FName FinalMotionSource = MotionSource;
FString MotionSourceStr = MotionSource.ToString();
// Converting controller hand enum to motion source can leave behind the enum name in the resulting motion source, so just remove that before handling it
MotionSourceStr.RemoveFromStart("EControllerHand::");
if (MotionSourceStr.Equals("Left") || MotionSourceStr.Equals("Right"))
{
switch (ControllerPoseAlignment)
{
case EOculusXRControllerPoseAlignment::Grip:
FinalMotionSource = FName(MotionSourceStr.Append(FString("Grip")));
break;
case EOculusXRControllerPoseAlignment::Aim:
FinalMotionSource = FName(MotionSourceStr.Append(FString("Aim")));
break;
case EOculusXRControllerPoseAlignment::Default:
default:
break;
}
}
// TODO: Just pass the pose info to OVRPlugin instead of doing the conversion between poses here
if (FinalMotionSource == FName("LeftGrip") || FinalMotionSource == FName("RightGrip"))
{
OutPose = OutPose * OculusXRHMD::FPose(FQuat(FVector(0, 1, 0), -FMath::DegreesToRadians(double(60))), FVector(-0.04, 0, -0.03) * WorldToMetersScale);
}
else if (FinalMotionSource == FName("LeftAim") || FinalMotionSource == FName("RightAim"))
{
OutPose = OutPose * OculusXRHMD::FPose(FQuat::Identity, FVector(0.055, 0, 0) * WorldToMetersScale);
}
if (bIsPositionValid)
{
OutPosition = OutPose.Position;
}
if (bIsOrientationValid)
{
OutOrientation = OutPose.Orientation.Rotator();
}
// Avoid any broadcasting in other threads than the game thread because that is undefined behavior
if (IsInGameThread())
{
auto bSuccess = true;
EControllerHand ControllerHand;
if (GetHandEnumForSourceName(MotionSource, ControllerHand))
{
// TODO: Just use the motion source name here instead of the legacy enum
UOculusXRInputFunctionLibrary::HandMovementFilter.Broadcast(
ControllerHand,
&OutPosition,
&OutOrientation,
&bSuccess);
}
return bSuccess;
}
return true;
}
}
}
}
}
break;
}
}
}
// Avoid any broadcasting in other threads than the game thread because that is undefined behavior
auto bSuccess = false;
if (IsInGameThread())
{
EControllerHand ControllerHand;
if (GetHandEnumForSourceName(MotionSource, ControllerHand))
{
// TODO: Just use the motion source name here instead of the legacy enum
UOculusXRInputFunctionLibrary::HandMovementFilter.Broadcast(
ControllerHand,
&OutPosition,
&OutOrientation,
&bSuccess);
}
}
return bSuccess;
}
ETrackingStatus FOculusXRInput::GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const
{
ETrackingStatus TrackingStatus = ETrackingStatus::NotTracked;
bool IsLeftHand = MotionSource == FName("Left") || MotionSource == FName("EControllerHand::Left") || MotionSource == FName("LeftGrip") || MotionSource == FName("LeftAim");
bool IsRightHand = MotionSource == FName("Right") || MotionSource == FName("EControllerHand::Right") || MotionSource == FName("RightGrip") || MotionSource == FName("RightAim");
if (!IsLeftHand && !IsRightHand)
{
return TrackingStatus;
}
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerIndex);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerIndex, InPlatformUser, InDeviceId);
for (const FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
const FOculusTouchControllerState& ControllerState = ControllerPair.TouchControllerStates[IsLeftHand ? 0 : 1];
if (ControllerState.bIsConnected)
{
if (ControllerState.bIsPositionTracked && ControllerState.bIsOrientationTracked)
{
TrackingStatus = ETrackingStatus::Tracked;
}
else if (ControllerState.bIsPositionValid && ControllerState.bIsOrientationValid)
{
TrackingStatus = ETrackingStatus::InertialOnly;
}
break;
}
const FOculusHandControllerState& HandState = ControllerPair.HandControllerStates[IsLeftHand ? 0 : 1];
if (HandState.bIsConnected)
{
if (HandState.bIsPositionTracked && HandState.bIsOrientationTracked)
{
TrackingStatus = ETrackingStatus::Tracked;
}
break;
}
}
}
return TrackingStatus;
}
void FOculusXRInput::SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values)
{
SetHapticFeedbackValues(ControllerId, Hand, Values, nullptr);
}
void FOculusXRInput::SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values, TSharedPtr<FOculusXRHapticsDesc> HapticsDesc)
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
for (FOculusControllerPair& ControllerPair : ControllerPairs)
{
if (ControllerPair.DeviceId == InDeviceId)
{
FOculusTouchControllerState& ControllerState = ControllerPair.TouchControllerStates[Hand];
if (ControllerState.bIsConnected)
{
if (IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && FApp::HasVRFocus())
{
ovrpController ControllerTypes = (ovrpController)(ovrpController_Active | ovrpController_LTrackedRemote | ovrpController_RTrackedRemote);
#ifdef USE_ANDROID_INPUT
ControllerTypes = (ovrpController)(ControllerTypes | ovrpController_Touch);
#endif
ovrpControllerState6 OvrpControllerState;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetControllerState6(ControllerTypes, &OvrpControllerState)))
{
UE_LOG(LogOcInput, Error, TEXT("GetControllerState6 failed."));
return;
}
if (OvrpControllerState.ConnectedControllerTypes & (ovrpController_Touch | ovrpController_LTrackedRemote | ovrpController_RTrackedRemote))
{
// Buffered haptics is currently only supported on Touch
FHapticFeedbackBuffer* HapticBuffer = Values.HapticBuffer;
bool bHapticBuffer = (HapticBuffer && HapticBuffer->BufferLength > 0);
if ((OvrpControllerState.ConnectedControllerTypes & (ovrpController_Touch)) && bHapticBuffer)
{
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
ovrpHapticsState OvrpHapticsState;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetControllerHapticsState2(OvrpController, &OvrpHapticsState)))
{
UE_LOG(LogOcInput, Error, TEXT("ControllerHapticsState2 failed."));
return;
}
double StartTimePCM = FPlatformTime::Seconds();
float TimeToSend = GetMaxHapticDuration(EControllerHand(Hand));
int WantToSend = (int)(TimeToSend * HapticBuffer->SamplingRate);
if (WantToSend == 0)
return;
WantToSend = FMath::Min(WantToSend, OvrpHapticsDesc.MaximumBufferSamplesCount);
WantToSend = FMath::Max(WantToSend, OvrpHapticsDesc.MinimumBufferSamplesCount);
ovrpUInt32 SamplesSent = 0;
if (OvrpHapticsState.SamplesQueued < OvrpHapticsDesc.MinimumSafeSamplesQueued + WantToSend) //trying to minimize latency
{
WantToSend = (OvrpHapticsDesc.MinimumSafeSamplesQueued + WantToSend - OvrpHapticsState.SamplesQueued);
void* BufferToFree = nullptr;
ovrpHapticsBuffer OvrpHapticsBuffer;
WantToSend = FMath::Min(WantToSend, HapticBuffer->BufferLength - HapticBuffer->SamplesSent);
WantToSend = FMath::Min(WantToSend, (int)(0.001f * CVarOculusPCMBatchDuration.GetValueOnAnyThread() * HapticBuffer->SamplingRate));
TimeToSend = 1.f * WantToSend / HapticBuffer->SamplingRate;
OvrpHapticsBuffer.SamplesCount = WantToSend;
if (OvrpHapticsBuffer.SamplesCount == 0 && OvrpHapticsState.SamplesQueued == 0)
{
Values.HapticBuffer->bFinishedPlaying = HapticBuffer->bFinishedPlaying = true;
ControllerState.bPlayingHapticEffect = false;
}
else
{
if (OvrpHapticsDesc.SampleSizeInBytes == 1)
{
uint8* Samples = (uint8*)FMemory::Malloc(OvrpHapticsBuffer.SamplesCount * sizeof(*Samples));
for (int i = 0; i < OvrpHapticsBuffer.SamplesCount; i++)
{
Samples[i] = static_cast<uint8>(HapticBuffer->RawData[HapticBuffer->CurrentPtr + i] * HapticBuffer->ScaleFactor);
}
OvrpHapticsBuffer.Samples = BufferToFree = Samples;
}
else if (OvrpHapticsDesc.SampleSizeInBytes == 2)
{
uint16* Samples = (uint16*)FMemory::Malloc(OvrpHapticsBuffer.SamplesCount * sizeof(*Samples));
for (int i = 0; i < OvrpHapticsBuffer.SamplesCount; i++)
{
const uint32 DataIndex = HapticBuffer->CurrentPtr + (i * 2);
const uint16* const RawData = reinterpret_cast<const uint16*>(&HapticBuffer->RawData[DataIndex]);
Samples[i] = static_cast<uint16>(*RawData * HapticBuffer->ScaleFactor);
}
OvrpHapticsBuffer.Samples = BufferToFree = Samples;
}
else if (OvrpHapticsDesc.SampleSizeInBytes == 4)
{
uint32* Samples = (uint32*)FMemory::Malloc(OvrpHapticsBuffer.SamplesCount * sizeof(*Samples));
for (int i = 0; i < OvrpHapticsBuffer.SamplesCount; i++)
{
const uint32 DataIndex = HapticBuffer->CurrentPtr + (i * 4);
const uint32* const RawData = reinterpret_cast<const uint32*>(&HapticBuffer->RawData[DataIndex]);
Samples[i] = static_cast<uint32>(*RawData * HapticBuffer->ScaleFactor);
}
OvrpHapticsBuffer.Samples = BufferToFree = Samples;
}
else
{
UE_LOG(LogOcInput, Error, TEXT("Unsupported OvrpHapticsDesc.SampleSizeInBytes: %d."), OvrpHapticsDesc.SampleSizeInBytes);
return;
}
ovrpHapticsPcmVibration HapticsVibration;
bool bAppend = HapticsDesc ? HapticsDesc->bAppend : false;
HapticsVibration.Append = (bAppend || HapticBuffer->SamplesSent > 0);
float* PCMBuffer = (float*)FMemory::Malloc(OvrpHapticsBuffer.SamplesCount * sizeof(*PCMBuffer));
for (int i = 0; i < OvrpHapticsBuffer.SamplesCount; i++)
{
float Amplitude = ((uint8_t*)OvrpHapticsBuffer.Samples)[i] / 255.0f;
Amplitude = FMath::Min(1.0f, Amplitude);
Amplitude = FMath::Max(-1.0f, Amplitude);
PCMBuffer[i] = Amplitude;
}
HapticsVibration.Buffer = PCMBuffer;
HapticsVibration.BufferSize = (ovrpUInt32)OvrpHapticsBuffer.SamplesCount;
HapticsVibration.SampleRateHz = HapticBuffer->SamplingRate;
HapticsVibration.SamplesConsumed = &SamplesSent;
FOculusXRHMDModule::GetPluginWrapper().SetControllerHapticsPcm(
OvrpController,
HapticsVibration);
double EndTimePCM = FPlatformTime::Seconds();
if (PCMBuffer)
{
FMemory::Free(PCMBuffer);
}
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("PCMHaptics is finished: bAppend: %d, BufferSize: %d, SampleRate: %.3f, SamplesConsumed: %d, Total SamplesSent: %d, TimeSpent: %fms"),
(int)(HapticsVibration.Append),
HapticsVibration.BufferSize,
HapticsVibration.SampleRateHz,
SamplesSent,
HapticBuffer->SamplesSent + SamplesSent,
(EndTimePCM - StartTimePCM) * 1000.0);
if (BufferToFree)
{
FMemory::Free(BufferToFree);
}
HapticBuffer->CurrentPtr += (SamplesSent * OvrpHapticsDesc.SampleSizeInBytes);
HapticBuffer->SamplesSent += SamplesSent;
ControllerState.bPlayingHapticEffect = true;
}
}
}
else
{
float FreqMin, FreqMax = 0.f;
GetHapticFrequencyRange(FreqMin, FreqMax);
const float InitialFreq = (Values.Frequency > 0.0f) ? Values.Frequency : 1.0f;
const float Frequency = FMath::Lerp(FreqMin, FreqMax, FMath::Clamp(InitialFreq, 0.f, 1.f));
const float Amplitude = Values.Amplitude * GetHapticAmplitudeScale();
if (ControllerState.HapticAmplitude != Amplitude || ControllerState.HapticFrequency != Frequency)
{
ControllerState.HapticAmplitude = Amplitude;
ControllerState.HapticFrequency = Frequency;
ovrpController OvrController = ovrpController_None;
if (OvrpControllerState.ConnectedControllerTypes & (ovrpController_Touch))
{
OvrController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
}
else if (OvrpControllerState.ConnectedControllerTypes & (ovrpController_LTrackedRemote | ovrpController_RTrackedRemote))
{
OvrController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTrackedRemote : ovrpController_RTrackedRemote;
}
ovrpHapticsLocation Loc = (HapticsDesc ? GetOVRPHapticsLocation(HapticsDesc->Location) : ovrpHapticsLocation::ovrpHapticsLocation_Hand);
FOculusXRHMDModule::GetPluginWrapper().SetControllerLocalizedVibration(OvrController,
Loc,
Frequency,
Amplitude);
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("LocalizedVibration is finished: Location: %d, Frequency: %f, Amplitude: %f"), (int)(Loc), Frequency, Amplitude);
ControllerState.bPlayingHapticEffect = (Amplitude != 0.f) && (Frequency != 0.f);
}
}
}
}
}
break;
}
}
}
void FOculusXRInput::PlayHapticEffect(
UHapticFeedbackEffect_Base* HapticEffect,
EControllerHand Hand,
EOculusXRHandHapticsLocation Location,
bool bAppend,
float Scale,
bool bLoop)
{
if (HapticEffect)
{
switch (Hand)
{
case EControllerHand::Left:
ActiveHapticEffect_Left.Reset();
HapticsDesc_Left.Reset();
ActiveHapticEffect_Left = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
HapticsDesc_Left = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
break;
case EControllerHand::Right:
ActiveHapticEffect_Right.Reset();
HapticsDesc_Right.Reset();
ActiveHapticEffect_Right = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
HapticsDesc_Right = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
break;
default:
UE_LOG(LogOcInput, Warning, TEXT("Invalid hand specified (%d) for haptic feedback effect %s"), (int32)Hand, *HapticEffect->GetName());
break;
}
}
}
int FOculusXRInput::PlayHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int InSampleRate, bool bPCM, bool bAppend)
{
int TimeToSend = GetMaxHapticDuration(Hand);
if (TimeToSend == 0)
return 0;
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
int SampleRate = (InSampleRate > 0 ? InSampleRate : OvrpHapticsDesc.SampleRateHz);
int MaxSamplesCount = TimeToSend * SampleRate;
if (SamplesCount > MaxSamplesCount || SamplesCount < OvrpHapticsDesc.MinimumBufferSamplesCount)
{
UE_LOG(LogOcInput, Error, TEXT("Sample count should be between %d and %d which last %d time."),
OvrpHapticsDesc.MinimumBufferSamplesCount, MaxSamplesCount, TimeToSend);
}
int WantToSend = FMath::Min(SamplesCount, MaxSamplesCount);
WantToSend = FMath::Max(WantToSend, OvrpHapticsDesc.MinimumBufferSamplesCount);
float* BufferToSend = (float*)FMemory::Malloc(WantToSend * sizeof(*BufferToSend));
for (int i = 0; i < WantToSend; i++)
{
float Amplitude = ((uint8_t*)Samples)[i] / 255.0f;
Amplitude = FMath::Min(1.0f, Amplitude);
Amplitude = FMath::Max((bPCM ? -1.f : 0.0f), Amplitude);
BufferToSend[i] = Amplitude;
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("amplitude, %.3f"), Amplitude);
}
ovrpUInt32 SamplesSent = 0;
if (bPCM)
{ //PCM
ovrpHapticsPcmVibration HapticsVibration;
HapticsVibration.Buffer = BufferToSend;
HapticsVibration.BufferSize = (ovrpUInt32)WantToSend;
HapticsVibration.SampleRateHz = SampleRate;
HapticsVibration.SamplesConsumed = &SamplesSent;
FOculusXRHMDModule::GetPluginWrapper().SetControllerHapticsPcm(
OvrpController,
HapticsVibration);
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("PCMHaptics is finished: bAppend: %d, BufferSize: %d, SampleRate: %.3f, SamplesConsumed: %d"),
(int)(HapticsVibration.Append),
HapticsVibration.BufferSize,
HapticsVibration.SampleRateHz,
SamplesSent);
}
else
{ //HAE
ovrpHapticsAmplitudeEnvelopeVibration HapticsVibration;
HapticsVibration.Duration = WantToSend / SampleRate;
HapticsVibration.AmplitudeCount = WantToSend;
HapticsVibration.Amplitudes = BufferToSend;
FOculusXRHMDModule::GetPluginWrapper().SetControllerHapticsAmplitudeEnvelope(
OvrpController,
HapticsVibration);
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("HAEHaptics is finished: AmplitudeCount: %d, SampleRate: %d"),
HapticsVibration.AmplitudeCount,
SampleRate);
}
if (BufferToSend)
{
FMemory::Free(BufferToSend);
}
return (int)SamplesSent;
}
void FOculusXRInput::SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
{
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
FOculusXRHMDModule::GetPluginWrapper().SetControllerLocalizedVibration(OvrpController, GetOVRPHapticsLocation(Location), Frequency, Amplitude);
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("LocalizedVibration is finished: Location: %d, Frequency: %f, Amplitude: %f"), (int)(Location), Frequency, Amplitude);
}
void FOculusXRInput::ProcessHaptics(const float DeltaTime)
{
FHapticFeedbackValues LeftHaptics, RightHaptics;
bool bLeftHapticsNeedUpdate = false;
bool bRightHapticsNeedUpdate = false;
if (ActiveHapticEffect_Left.IsValid())
{
const bool bPlaying = ActiveHapticEffect_Left->Update(DeltaTime, LeftHaptics);
if (!bPlaying)
{
ActiveHapticEffect_Left->bLoop ? HapticsDesc_Left->Restart() : HapticsDesc_Left.Reset();
ActiveHapticEffect_Left->bLoop ? ActiveHapticEffect_Left->Restart() : ActiveHapticEffect_Left.Reset();
}
bLeftHapticsNeedUpdate = true;
}
if (ActiveHapticEffect_Right.IsValid())
{
const bool bPlaying = ActiveHapticEffect_Right->Update(DeltaTime, RightHaptics);
if (!bPlaying)
{
ActiveHapticEffect_Right->bLoop ? HapticsDesc_Right->Restart() : HapticsDesc_Right.Reset();
ActiveHapticEffect_Right->bLoop ? ActiveHapticEffect_Right->Restart() : ActiveHapticEffect_Right.Reset();
}
bRightHapticsNeedUpdate = true;
}
// Haptic Updates
if (bLeftHapticsNeedUpdate)
{
SetHapticFeedbackValues(0, (int32)(EControllerHand::Left), LeftHaptics, HapticsDesc_Left);
}
if (bRightHapticsNeedUpdate)
{
SetHapticFeedbackValues(0, (int32)(EControllerHand::Right), RightHaptics, HapticsDesc_Right);
}
}
void FOculusTouchControllerState::ResampleHapticBufferData(const FHapticFeedbackBuffer& HapticBuffer, TMap<const uint8*, TSharedPtr<TArray<uint8>>>& ResampledRawDataCache)
{
const uint8* OriginalRawData = HapticBuffer.RawData;
TSharedPtr<TArray<uint8>>* ResampledRawDataSharedPtrPtr = ResampledRawDataCache.Find(OriginalRawData);
if (ResampledRawDataSharedPtrPtr == nullptr)
{
// We need to resample and cache the resampled data.
ResampledHapticBuffer = HapticBuffer;
int32 SampleRate = HapticBuffer.SamplingRate;
int TargetFrequency = 320;
int TargetBufferSize = (HapticBuffer.BufferLength * TargetFrequency) / (SampleRate * 2) + 1; //2 because we're only using half of the 16bit source PCM buffer
ResampledHapticBuffer.BufferLength = TargetBufferSize;
ResampledHapticBuffer.CurrentPtr = 0;
ResampledHapticBuffer.SamplingRate = TargetFrequency;
TSharedPtr<TArray<uint8>>& NewResampledRawDataSharedPtr = ResampledRawDataCache.Add(OriginalRawData);
NewResampledRawDataSharedPtr = MakeShared<TArray<uint8>>();
ResampledRawDataSharedPtrPtr = &NewResampledRawDataSharedPtr;
TArray<uint8>& ResampledRawData = *NewResampledRawDataSharedPtr;
ResampledRawData.SetNum(TargetBufferSize);
const uint8* PCMData = HapticBuffer.RawData;
int previousTargetIndex = -1;
int currentMin = 0;
for (int i = 1; i < HapticBuffer.BufferLength; i += 2)
{
int targetIndex = i * TargetFrequency / (SampleRate * 2);
int val = PCMData[i];
if (val & 0x80)
{
val = ~val;
}
currentMin = FMath::Min(currentMin, val);
if (targetIndex != previousTargetIndex)
{
ResampledRawData[targetIndex] = val * 2; // *Scale;
previousTargetIndex = targetIndex;
currentMin = 0;
}
}
ResampledHapticBuffer.RawData = ResampledRawData.GetData();
}
else if (ResampledHapticBuffer.RawData != (*ResampledRawDataSharedPtrPtr)->GetData())
{
// If this a cached effect, but not the same one we played last so we need to copy the new one's buffer and reference its cached resampled data.
ResampledHapticBuffer = HapticBuffer;
ResampledHapticBuffer.RawData = (*ResampledRawDataSharedPtrPtr)->GetData();
}
}
void FOculusXRInput::GetHapticFrequencyRange(float& MinFrequency, float& MaxFrequency) const
{
MinFrequency = 0.f;
MaxFrequency = 1.f;
}
float FOculusXRInput::GetHapticAmplitudeScale() const
{
return 1.f;
}
uint32 FOculusXRInput::GetNumberOfTouchControllers() const
{
uint32 RetVal = 0;
for (FOculusControllerPair Pair : ControllerPairs)
{
RetVal += (Pair.TouchControllerStates[0].bIsConnected ? 1 : 0);
RetVal += (Pair.TouchControllerStates[1].bIsConnected ? 1 : 0);
}
return RetVal;
}
uint32 FOculusXRInput::GetNumberOfHandControllers() const
{
uint32 RetVal = 0;
for (FOculusControllerPair Pair : ControllerPairs)
{
RetVal += (Pair.HandControllerStates[0].bIsConnected ? 1 : 0);
RetVal += (Pair.HandControllerStates[1].bIsConnected ? 1 : 0);
}
return RetVal;
}
ovrpHapticsLocation FOculusXRInput::GetOVRPHapticsLocation(EOculusXRHandHapticsLocation Location)
{
switch (Location)
{
case EOculusXRHandHapticsLocation::Hand:
return ovrpHapticsLocation::ovrpHapticsLocation_Hand;
case EOculusXRHandHapticsLocation::Thumb:
return ovrpHapticsLocation::ovrpHapticsLocation_Thumb;
case EOculusXRHandHapticsLocation::Index:
return ovrpHapticsLocation::ovrpHapticsLocation_Index;
default:
UE_LOG(LogOcInput, Error, TEXT("Unsupported Haptics Location: %d"), Location);
return ovrpHapticsLocation::ovrpHapticsLocation_None;
}
}
bool FOculusXRInput::GetOvrpHapticsDesc(int Hand)
{
if (!bPulledHapticsDesc)
{
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
// Buffered haptics is currently only supported on Touch
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetControllerHapticsDesc2(OvrpController, &OvrpHapticsDesc)))
{
UE_LOG(LogOcInput, Error, TEXT("ControllerHapticsDesc2 failed."));
return false;
}
bPulledHapticsDesc = true;
if (OvrpHapticsDesc.SampleRateHz == 0)
{
UE_LOG(LogOcInput, Error, TEXT("GetControllerHapticsDesc2 returns OvrpHapticsDesc.SampleRateHz = %d"), OvrpHapticsDesc.SampleRateHz);
OvrpHapticsDesc.SampleRateHz = 2000;
return true;
}
else
{
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("GetControllerHapticsDesc2 returns OvrpHapticsDesc.SampleRateHz = %d"), OvrpHapticsDesc.SampleRateHz);
}
}
return true;
}
float FOculusXRInput::GetControllerSampleRateHz(EControllerHand Hand)
{
float sampleRateHz = 0.f;
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetControllerSampleRateHz(OvrpController, &sampleRateHz)))
{
UE_LOG(LogOcInput, Error, TEXT("GetControllerSampleRateHz failed."));
}
return sampleRateHz;
}
int FOculusXRInput::GetMaxHapticDuration(EControllerHand Hand)
{
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
if (!GetOvrpHapticsDesc((int32)Hand))
return 0;
return OvrpHapticsDesc.MaximumBufferSamplesCount / OvrpHapticsDesc.SampleRateHz;
}
} // namespace OculusXRInput
#undef LOCTEXT_NAMESPACE
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS