215 lines
7.3 KiB
C++
215 lines
7.3 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
#include "OculusXRInputHapticsExtensionPlugin.h"
|
|
|
|
#include "IOpenXRHMDModule.h"
|
|
#include "OculusXRInputExtensionPlugin.h"
|
|
#include "OculusXRInputXRFunctions.h"
|
|
#include "OpenXRCore.h"
|
|
|
|
namespace OculusXRInput
|
|
{
|
|
|
|
XrInstance FInputHapticsExtensionPlugin::GetOpenXRInstance() const
|
|
{
|
|
return Instance;
|
|
}
|
|
|
|
XrSession FInputHapticsExtensionPlugin::GetOpenXRSession() const
|
|
{
|
|
return Session;
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::IsPCMExtensionAvailable() const
|
|
{
|
|
return bExtFBHapticsPcmAvailable;
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::IsAmplitudeEnvelopeExtensionAvailable() const
|
|
{
|
|
return bExtFBAmplitudeEnvelopeAvailable;
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::IsTouchControllerProExtensionAvailable() const
|
|
{
|
|
return bExtFBTouchControllerProAvailable;
|
|
}
|
|
|
|
XrAction FInputHapticsExtensionPlugin::GetXrHandHapticVibrationAction() const
|
|
{
|
|
return XrHandHapticVibrationAction;
|
|
}
|
|
|
|
XrAction FInputHapticsExtensionPlugin::GetXrThumbHapticVibrationAction() const
|
|
{
|
|
return XrThumbHapticVibrationAction;
|
|
}
|
|
|
|
XrAction FInputHapticsExtensionPlugin::GetXrIndexHapticVibrationAction() const
|
|
{
|
|
return XrIndexHapticVibrationAction;
|
|
}
|
|
|
|
XrPath* FInputHapticsExtensionPlugin::GetXrHandsSubactionPaths()
|
|
{
|
|
return XrPathBothHands;
|
|
}
|
|
|
|
XrPath* FInputHapticsExtensionPlugin::GetXrHandsHapticsSubactionPaths()
|
|
{
|
|
return XrPathBothHandsHaptics;
|
|
}
|
|
|
|
XrPath* FInputHapticsExtensionPlugin::GetXrThumbsHapticsSubactionPaths()
|
|
{
|
|
return XrPathBothThumbsHaptics;
|
|
}
|
|
|
|
XrPath* FInputHapticsExtensionPlugin::GetXrIndexesHapticsSubactionPaths()
|
|
{
|
|
return XrPathBothIndexesHaptics;
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
|
{
|
|
OutExtensions.Add(XR_FB_HAPTIC_PCM_EXTENSION_NAME);
|
|
OutExtensions.Add(XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME);
|
|
OutExtensions.Add(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME);
|
|
return true;
|
|
}
|
|
|
|
const void* FInputHapticsExtensionPlugin::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
|
{
|
|
bExtFBHapticsPcmAvailable = InModule->IsExtensionEnabled(XR_FB_HAPTIC_PCM_EXTENSION_NAME);
|
|
bExtFBAmplitudeEnvelopeAvailable = InModule->IsExtensionEnabled(XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME);
|
|
bExtFBTouchControllerProAvailable = InModule->IsExtensionEnabled(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME);
|
|
return InNext;
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
|
{
|
|
Instance = InInstance;
|
|
InitOpenXRFunctions(Instance);
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::PostCreateSession(XrSession InSession)
|
|
{
|
|
Session = InSession;
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::CreateHapticActions()
|
|
{
|
|
// Create action set
|
|
HapticsActionSet = XR_NULL_HANDLE;
|
|
XrActionSetCreateInfo ActionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
|
ActionSetInfo.next = nullptr;
|
|
FCStringAnsi::Strcpy(ActionSetInfo.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE, "oculushapticsactionset");
|
|
FCStringAnsi::Strcpy(ActionSetInfo.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE, "OculusHapticsActionSet");
|
|
XR_ENSURE(xrCreateActionSet(Instance, &ActionSetInfo, &HapticsActionSet));
|
|
|
|
// Create hand haptics paths
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left", &XrPathLeftHand));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/haptic", &XrPathLeftHandHaptics));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right", &XrPathRightHand));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/haptic", &XrPathRightHandHaptics));
|
|
XrPathBothHands[0] = XrPathLeftHand;
|
|
XrPathBothHands[1] = XrPathRightHand;
|
|
XrPathBothHandsHaptics[0] = XrPathLeftHandHaptics;
|
|
XrPathBothHandsHaptics[1] = XrPathRightHandHaptics;
|
|
|
|
// Create localized haptics paths
|
|
XR_ENSURE(xrStringToPath(Instance, "/interaction_profiles/facebook/touch_controller_pro", &XrQuestProInteractionProfile));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/thumb_haptic_fb", &XrPathLeftThumbHaptics));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/trigger_haptic_fb", &XrPathLeftIndexHaptics));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/thumb_haptic_fb", &XrPathRightThumbHaptics));
|
|
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/trigger_haptic_fb", &XrPathRightIndexHaptics));
|
|
|
|
XrPathBothThumbsHaptics[0] = XrPathLeftThumbHaptics;
|
|
XrPathBothThumbsHaptics[1] = XrPathRightThumbHaptics;
|
|
XrPathBothIndexesHaptics[0] = XrPathLeftIndexHaptics;
|
|
XrPathBothIndexesHaptics[1] = XrPathRightIndexHaptics;
|
|
|
|
// Create actions
|
|
const auto CreateVibrationOutputAction = [this](const char* ActionName) {
|
|
XrActionCreateInfo ActionCreateInfo{ XR_TYPE_ACTION_CREATE_INFO };
|
|
ActionCreateInfo.next = nullptr;
|
|
ActionCreateInfo.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
|
|
|
|
FCStringAnsi::Strcpy(ActionCreateInfo.actionName, XR_MAX_ACTION_NAME_SIZE, ActionName);
|
|
FCStringAnsi::Strcpy(ActionCreateInfo.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE, ActionName);
|
|
|
|
ActionCreateInfo.countSubactionPaths = sizeof(XrPathBothHands) / sizeof(XrPath);
|
|
ActionCreateInfo.subactionPaths = XrPathBothHands;
|
|
XrAction Action = XR_NULL_HANDLE;
|
|
XR_ENSURE(xrCreateAction(HapticsActionSet, &ActionCreateInfo, &Action));
|
|
return Action;
|
|
};
|
|
|
|
XrHandHapticVibrationAction = CreateVibrationOutputAction("hand_haptic_vibration");
|
|
XrThumbHapticVibrationAction = CreateVibrationOutputAction("hand_thumb_haptic_vibration");
|
|
XrIndexHapticVibrationAction = CreateVibrationOutputAction("hand_index_haptic_vibration");
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::GetSuggestedBindings(XrPath InInteractionProfile, TArray<XrActionSuggestedBinding>& OutBindings)
|
|
{
|
|
if (HapticsActionSet == XR_NULL_HANDLE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OutBindings.Add({ XrHandHapticVibrationAction, XrPathLeftHandHaptics });
|
|
OutBindings.Add({ XrHandHapticVibrationAction, XrPathRightHandHaptics });
|
|
if (InInteractionProfile == XrQuestProInteractionProfile)
|
|
{
|
|
OutBindings.Add({ XrIndexHapticVibrationAction, XrPathLeftIndexHaptics });
|
|
OutBindings.Add({ XrIndexHapticVibrationAction, XrPathRightIndexHaptics });
|
|
OutBindings.Add({ XrThumbHapticVibrationAction, XrPathLeftThumbHaptics });
|
|
OutBindings.Add({ XrThumbHapticVibrationAction, XrPathRightThumbHaptics });
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::AttachActionSets(TSet<XrActionSet>& OutActionSets)
|
|
{
|
|
if (HapticsActionSet != XR_NULL_PATH)
|
|
{
|
|
OutActionSets.Add(HapticsActionSet);
|
|
}
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::GetActiveActionSetsForSync(
|
|
TArray<XrActiveActionSet>& OutActiveSets)
|
|
{
|
|
if (HapticsActionSet != XR_NULL_PATH)
|
|
{
|
|
OutActiveSets.Add({ HapticsActionSet, XR_NULL_PATH });
|
|
}
|
|
}
|
|
|
|
bool FInputHapticsExtensionPlugin::GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath,
|
|
bool& OutHasHaptics)
|
|
{
|
|
// Called at the start of Epic's input action creation
|
|
if (HapticsActionSet != XR_NULL_HANDLE)
|
|
{
|
|
DestroyHapticActions();
|
|
}
|
|
CreateHapticActions();
|
|
return true;
|
|
}
|
|
|
|
void FInputHapticsExtensionPlugin::DestroyHapticActions()
|
|
{
|
|
if (HapticsActionSet != XR_NULL_HANDLE)
|
|
{
|
|
xrDestroyActionSet(HapticsActionSet);
|
|
XrHandHapticVibrationAction = XR_NULL_HANDLE;
|
|
XrThumbHapticVibrationAction = XR_NULL_HANDLE;
|
|
XrIndexHapticVibrationAction = XR_NULL_HANDLE;
|
|
HapticsActionSet = XR_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
} // namespace OculusXRInput
|