// @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 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 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 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& 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(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& 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 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(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 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(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(&HapticBuffer->RawData[DataIndex]); Samples[i] = static_cast(*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(&HapticBuffer->RawData[DataIndex]); Samples[i] = static_cast(*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>>& ResampledRawDataCache) { const uint8* OriginalRawData = HapticBuffer.RawData; TSharedPtr>* 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>& NewResampledRawDataSharedPtr = ResampledRawDataCache.Add(OriginalRawData); NewResampledRawDataSharedPtr = MakeShared>(); ResampledRawDataSharedPtrPtr = &NewResampledRawDataSharedPtr; TArray& 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