Started the project... finally...

Started the project. This right now only includes Meta XR and Android setup with VR template. More improvements to come!
This commit is contained in:
2024-05-06 20:03:14 +03:00
parent 372d02a195
commit e55214a398
1201 changed files with 181249 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
namespace UnrealBuildTool.Rules
{
public class OculusXRInput : ModuleRules
{
public OculusXRInput(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
PrivateIncludePathModuleNames.AddRange(
new string[]
{
"InputDevice", // For IInputDevice.h
"HeadMountedDisplay", // For IMotionController.h
"ImageWrapper"
});
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"ApplicationCore",
"Engine",
"InputCore",
"HeadMountedDisplay",
"OculusXRHMD",
"OculusXRMR",
"OVRPluginXR",
});
if (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 3))
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"XRBase",
});
}
PrivateIncludePaths.AddRange(
new string[] {
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
"OculusXRHMD/Private",
});
PublicIncludePaths.AddRange(
new string[] {
"Runtime/Renderer/Private",
"Runtime/Engine/Classes/Components",
});
if (Target.Platform == UnrealTargetPlatform.Win64)
{
RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/" + Target.Platform.ToString() + "/OpenXR/OVRPlugin.dll");
}
}
}
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// A class to render the currently connected controller.
// Similar to how hands are tracked.
#include "OculusXRControllerComponent.h"
#include "OculusXRInput.h"
#include "Components/StaticMeshComponent.h"
#include "OculusXRHandTracking.h"
#include <OculusXRInputModule.h>
UOculusXRControllerComponent::UOculusXRControllerComponent()
: Super(),
// These position and rotation offsets are needed to correctly position the controller
// when using natural or controller based hand positioning.
// Why do these need to be hardcoded and not come from the skeleton etc?
// It seems like the offset comes from somewhere in unreal in the first place,
// not from a bone position, so there's not a place to load the correct orientation from.
PositionOffsets{
{
FVector(0, 0, 0), // Side: None, Controller Mapping: None
FVector(0, 0, 0), // Side: None, Controller Mapping: Natural
FVector(0, 0, 0), // Side: None, Controller Mapping: Controller
},
{
FVector(0, 0, 0), // Side: Left, Controller Mapping: None
FVector(4.278, 9.969, 4.638), // Side: Left, Controller Mapping: Natural
FVector(4.278, 9.969, 4.638), // Side: Left, Controller Mapping: Controller
},
{
FVector(0, 0, 0), // Side: Right, Controller Mapping: None
FVector(-4.104, -9.993, -4.244), // Side: Right, Controller Mapping: Natural
FVector(-4.104, -9.993, -4.244), // Side: Right, Controller Mapping: Controller
},
}
, RotationOffsets{
{
FVector(0, 0, 0), // Side: None, Controller Mapping: None
FVector(0, 0, 0), // Side: None, Controller Mapping: Natural
FVector(0, 0, 0), // Side: None, Controller Mapping: Controller
},
{
FVector(0, 0, 0), // Side: Left, Controller Mapping: None
FVector(90, 166.229, 263.738), // Side: Left, Controller Mapping: Natural
FVector(90, 168.515, 259.149), // Side: Left, Controller Mapping: Controller
},
{
FVector(0, 0, 0), // Side: Right, Controller Mapping: None
FVector(90, 194.995, 83.863), // Side: Right, Controller Mapping: Natural
FVector(90, 191.485, 79.149), // Side: Right, Controller Mapping: Controller
},
}
{
_meshLoadingState = MeshLoadingState::None;
PrimaryComponentTick.bCanEverTick = true;
}
void UOculusXRControllerComponent::BeginPlay()
{
Super::BeginPlay();
}
void UOculusXRControllerComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// If we're in a capsense mode, we need to offset the controller position so that it's correct / consistent with the hand position.
if (_cachedControllerHandType != OculusXRInput::FOculusHandTracking::ControllerDrivenHandType)
{
_cachedControllerHandType = OculusXRInput::FOculusHandTracking::ControllerDrivenHandType;
const FVector positionOffset = PositionOffsets[static_cast<int>(SkeletonType)][static_cast<int>(_cachedControllerHandType)];
const FVector rotationOffset = RotationOffsets[static_cast<int>(SkeletonType)][static_cast<int>(_cachedControllerHandType)];
SetRelativeLocation(positionOffset);
SetRelativeRotation(FQuat::MakeFromEuler(rotationOffset));
}
bool isHandTrackingEnabled = UOculusXRInputFunctionLibrary::IsHandTrackingEnabled();
bool shouldHide = isHandTrackingEnabled && !(RenderWhenUsingControllerDrivenHands && OculusXRInput::FOculusHandTracking::ControllerDrivenHandType == EOculusXRControllerDrivenHandPoseTypes::Controller);
if (shouldHide && !bHiddenInGame)
{
SetHiddenInGame(true, false);
}
if (!shouldHide && bHiddenInGame)
{
SetHiddenInGame(false, false);
}
if (_meshLoadingState == MeshLoadingState::None || _controllerType != GetControllerType())
{
InitializeMesh();
}
}
EOculusXRControllerType UOculusXRControllerComponent::GetControllerType()
{
EControllerHand controllerHand = EControllerHand::AnyHand;
if (SkeletonType == EOculusXRSide::Left)
{
controllerHand = EControllerHand::Left;
}
else if (SkeletonType == EOculusXRSide::Right)
{
controllerHand = EControllerHand::Right;
}
return UOculusXRFunctionLibrary::GetControllerType(controllerHand);
}
void UOculusXRControllerComponent::InitializeMesh()
{
if (_runtimeMesh != nullptr)
{
SetStaticMesh(nullptr);
_streamableManager.Unload(_runtimeMeshPath);
_runtimeMesh = nullptr;
}
auto left_controller_path = TEXT("none");
auto right_controller_path = TEXT("none");
_controllerType = GetControllerType();
switch (_controllerType)
{
case EOculusXRControllerType::MetaQuestTouch:
left_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/LeftTouchForQuest2.LeftTouchForQuest2'");
right_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/RightTouchForQuest2.RightTouchForQuest2'");
break;
case EOculusXRControllerType::MetaQuestTouchPlus:
// We don't currently have a model for the touch plus controller, default to the touch pro.
case EOculusXRControllerType::MetaQuestTouchPro:
left_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/LeftMetaQuestTouchPro.LeftMetaQuestTouchPro'");
right_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/RightMetaQuestTouchPro.RightMetaQuestTouchPro'");
break;
case EOculusXRControllerType::None:
case EOculusXRControllerType::Unknown:
default:
return;
}
auto controllerPath = left_controller_path;
if (SkeletonType == EOculusXRSide::Right)
{
controllerPath = right_controller_path;
}
_runtimeMeshPath = FSoftObjectPath(controllerPath);
_loadAssetHandle = _streamableManager.RequestAsyncLoad(
_runtimeMeshPath,
FStreamableDelegate::CreateUObject(this, &UOculusXRControllerComponent::MeshLoaded));
}
void UOculusXRControllerComponent::MeshLoaded()
{
if (_loadAssetHandle.IsValid() && _loadAssetHandle.Get()->HasLoadCompleted())
{
_runtimeMesh = reinterpret_cast<UStaticMesh*>(_loadAssetHandle.Get()->GetLoadedAsset());
_meshLoadingState = MeshLoadingState::Loaded;
SetStaticMesh(_runtimeMesh);
}
}

View File

@@ -0,0 +1,67 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRControllerTracking.h"
#include "OculusXRHMD.h"
#include "OculusXRInput.h"
#include "Misc/CoreDelegates.h"
#include "IOculusXRInputModule.h"
#include "Haptics/HapticFeedbackEffect_Base.h"
namespace OculusXRInput
{
void FOculusXRControllerTracking::PlayHapticEffect(
class UHapticFeedbackEffect_Base* HapticEffect,
EControllerHand Hand,
EOculusXRHandHapticsLocation Location,
bool bAppend,
float Scale,
bool bLoop)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
OculusXRInputModule.Get()->PlayHapticEffect(HapticEffect, Hand, Location, bAppend, Scale, bLoop);
#endif
}
int FOculusXRControllerTracking::PlayHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate, bool bPCM, bool bAppend)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
return OculusXRInputModule.Get()->PlayHapticEffect(Hand, Amplitudes.Num(), Amplitudes.GetData(), SampleRate, bPCM, bAppend);
#endif
}
void FOculusXRControllerTracking::StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
SetHapticsByValue(0.f, 0.f, Hand, Location);
#endif
}
void FOculusXRControllerTracking::SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
OculusXRInputModule.Get()->SetHapticsByValue(Frequency, Amplitude, Hand, Location);
#endif
}
float FOculusXRControllerTracking::GetControllerSampleRateHz(EControllerHand Hand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
return OculusXRInputModule.Get()->GetControllerSampleRateHz(Hand);
#endif
return 0;
}
int FOculusXRControllerTracking::GetMaxHapticDuration(EControllerHand Hand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
return OculusXRInputModule.Get()->GetMaxHapticDuration(Hand);
#endif
return 0;
}
} // namespace OculusXRInput

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRInputFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "OculusXRControllerTracking"
DEFINE_LOG_CATEGORY_STATIC(LogOcXRControllerTracking, Log, All);
//-------------------------------------------------------------------------------------------------
// FOculusXRControllerTracking
//-------------------------------------------------------------------------------------------------
class UHapticFeedbackEffect_Base;
namespace OculusXRInput
{
class FOculusXRControllerTracking
{
public:
static void PlayHapticEffect(
UHapticFeedbackEffect_Base* HapticEffect,
EControllerHand Hand,
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
bool bAppend = false,
float Scale = 1.f,
bool bLoop = false);
static int PlayHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate, bool bPCM = false, bool bAppend = false);
static void StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
static void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
static float GetControllerSampleRateHz(EControllerHand Hand);
static int GetMaxHapticDuration(EControllerHand Hand);
};
} // namespace OculusXRInput
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,221 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHandComponent.h"
#include "OculusXRInput.h"
#include "Engine/SkeletalMesh.h"
#include "Components/InputComponent.h"
#include "Materials/MaterialInterface.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/PlayerController.h"
UOculusXRHandComponent::UOculusXRHandComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
PrimaryComponentTick.TickGroup = TG_PrePhysics;
bHasAuthority = false;
bAutoActivate = true;
bWantsInitializeComponent = true;
for (uint8 BoneIndex = 0; BoneIndex < (uint8)EOculusXRBone::Bone_Max; BoneIndex++)
{
BoneNameMappings.Add((EOculusXRBone)BoneIndex, TEXT(""));
}
}
void UOculusXRHandComponent::BeginPlay()
{
Super::BeginPlay();
// Use custom mesh if a skeletal mesh is already set, else try to load the runtime mesh
if (GetSkinnedAsset())
{
bCustomHandMesh = true;
bSkeletalMeshInitialized = true;
}
else
{
RuntimeSkeletalMesh = NewObject<USkeletalMesh>(this, TEXT("OculusHandMesh"));
InitializeSkeletalMesh();
}
}
void UOculusXRHandComponent::InitializeSkeletalMesh()
{
if (RuntimeSkeletalMesh)
{
if (UOculusXRInputFunctionLibrary::GetHandSkeletalMesh(RuntimeSkeletalMesh, SkeletonType, MeshType))
{
SetSkinnedAssetAndUpdate(RuntimeSkeletalMesh, true);
if (MaterialOverride)
{
SetMaterial(0, MaterialOverride);
}
CachedBaseMaterial = GetMaterial(0);
bSkeletalMeshInitialized = true;
// Initialize physics capsules on the runtime mesh
if (bInitializePhysics)
{
CollisionCapsules = UOculusXRInputFunctionLibrary::InitializeHandPhysics(SkeletonType, this);
}
}
}
}
void UOculusXRHandComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
#if WITH_EDITOR
if (!bSkeletalMeshInitialized && !bCustomHandMesh)
{
InitializeSkeletalMesh();
}
#endif
if (IsInGameThread())
{
// Cache state from the game thread for use on the render thread
const AActor* MyOwner = GetOwner();
bHasAuthority = MyOwner->HasLocalNetOwner();
int i = 0;
}
if (bHasAuthority)
{
bool bHidden = false;
if (UOculusXRInputFunctionLibrary::IsHandTrackingEnabled())
{
// Update Visibility based on Confidence
if (ConfidenceBehavior == EOculusXRConfidenceBehavior::HideActor)
{
EOculusXRTrackingConfidence TrackingConfidence = UOculusXRInputFunctionLibrary::GetTrackingConfidence(SkeletonType);
bHidden |= TrackingConfidence != EOculusXRTrackingConfidence::High;
}
// Update Hand Scale
if (bUpdateHandScale)
{
float NewScale = UOculusXRInputFunctionLibrary::GetHandScale(SkeletonType);
SetRelativeScale3D(FVector(NewScale));
}
// Update Bone Pose Rotations
if (GetSkinnedAsset())
{
UpdateBonePose();
}
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
// Check for system gesture pressed through player controller
if (APawn* Pawn = Cast<APawn>(GetOwner()))
{
if (APlayerController* PC = Pawn->GetController<APlayerController>())
{
if (PC->WasInputKeyJustPressed(SkeletonType == EOculusXRHandType::HandLeft ? OculusXRInput::FOculusKey::OculusHand_Left_SystemGesture : OculusXRInput::FOculusKey::OculusHand_Right_SystemGesture))
{
SystemGesturePressed();
}
if (PC->WasInputKeyJustReleased(SkeletonType == EOculusXRHandType::HandLeft ? OculusXRInput::FOculusKey::OculusHand_Left_SystemGesture : OculusXRInput::FOculusKey::OculusHand_Right_SystemGesture))
{
SystemGestureReleased();
}
}
}
#endif
}
else
{
bHidden = true;
}
if (bHidden != bHiddenInGame)
{
SetHiddenInGame(bHidden);
for (int32 i = 0; i < CollisionCapsules.Num(); i++)
{
CollisionCapsules[i].Capsule->SetCollisionEnabled(bHidden ? ECollisionEnabled::NoCollision : ECollisionEnabled::QueryAndPhysics);
}
}
}
}
void UOculusXRHandComponent::UpdateBonePose()
{
if (bCustomHandMesh)
{
for (auto& BoneElem : BoneNameMappings)
{
// Set Root Bone Rotaiton
if (BoneElem.Key == EOculusXRBone::Wrist_Root)
{
FQuat RootBoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, EOculusXRBone::Wrist_Root);
RootBoneRotation *= HandRootFixupRotation;
RootBoneRotation.Normalize();
BoneSpaceTransforms[0].SetRotation(RootBoneRotation);
}
else
{
// Set Remaing Bone Rotations
int32 BoneIndex = GetSkinnedAsset()->GetRefSkeleton().FindBoneIndex(BoneElem.Value);
if (BoneIndex >= 0)
{
FQuat BoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, (EOculusXRBone)BoneElem.Key);
BoneSpaceTransforms[BoneIndex].SetRotation(BoneRotation);
}
}
}
}
else
{
// Set Root Bone Rotation
FQuat RootBoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, EOculusXRBone::Wrist_Root);
RootBoneRotation *= HandRootFixupRotation;
RootBoneRotation.Normalize();
BoneSpaceTransforms[0].SetRotation(RootBoneRotation);
// Set Remaining Bone Rotations
for (uint32 BoneIndex = 1; BoneIndex < (uint32)GetSkinnedAsset()->GetRefSkeleton().GetNum(); BoneIndex++)
{
FQuat BoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, (EOculusXRBone)BoneIndex);
BoneSpaceTransforms[BoneIndex].SetRotation(BoneRotation);
}
}
MarkRefreshTransformDirty();
}
void UOculusXRHandComponent::SystemGesturePressed()
{
if (SystemGestureBehavior == EOculusXRSystemGestureBehavior::SwapMaterial)
{
if (SystemGestureMaterial)
{
SetMaterial(0, SystemGestureMaterial);
}
else
{
UE_LOG(LogTemp, Log, TEXT("System Gesture Behavior was set to Swap Material but no System Gesture Material was provided!"));
}
}
}
void UOculusXRHandComponent::SystemGestureReleased()
{
if (SystemGestureBehavior == EOculusXRSystemGestureBehavior::SwapMaterial)
{
if (CachedBaseMaterial)
{
SetMaterial(0, CachedBaseMaterial);
}
else
{
UE_LOG(LogTemp, Log, TEXT("System Gesture Behavior was set to Swap Material but no System Gesture Material was provided!"));
}
}
}

View File

@@ -0,0 +1,687 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHandTracking.h"
#include "OculusXRHMD.h"
#include "Misc/CoreDelegates.h"
#include "IOculusXRInputModule.h"
#include "Animation/Skeleton.h"
#include "BoneWeights.h"
#include "Components/SkeletalMeshComponent.h"
#include "Rendering/SkeletalMeshLODRenderData.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "Rendering/SkeletalMeshLODModel.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Materials/Material.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Engine/SkinnedAssetCommon.h"
#include "Model.h"
#include "MaterialDomain.h"
#define OCULUS_TO_UE4_SCALE 100.0f
namespace OculusXRInput
{
static FInputDeviceId GetDeviceID(int32 ControllerId)
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
return InDeviceId;
}
FQuat FOculusHandTracking::GetBoneRotation(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId)
{
FQuat Rotation = FQuat::Identity;
if (BoneId <= EOculusXRBone::Invalid && BoneId >= EOculusXRBone::Bone_Max)
{
return Rotation;
}
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
const FOculusHandControllerState& HandState = HandPair.HandControllerStates[Hand];
int32 OvrBoneId = ToOvrBone(BoneId);
Rotation = HandState.BoneRotations[OvrBoneId];
break;
}
}
}
}
#endif
return Rotation;
}
float FOculusHandTracking::GetHandScale(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].HandScale;
}
}
}
}
#endif
return 1.0f;
}
EOculusXRTrackingConfidence FOculusHandTracking::GetTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].TrackingConfidence;
}
}
}
}
#endif
return EOculusXRTrackingConfidence::Low;
}
EOculusXRTrackingConfidence FOculusHandTracking::GetFingerTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusHandAxes Finger)
{
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].FingerConfidences[(int)Finger];
}
}
}
}
return EOculusXRTrackingConfidence::Low;
}
FTransform FOculusHandTracking::GetPointerPose(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const float WorldToMeters)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
FTransform PoseTransform = HandPair.HandControllerStates[Hand].PointerPose;
PoseTransform.SetLocation(PoseTransform.GetLocation() * WorldToMeters);
return PoseTransform;
}
}
}
}
#endif
return FTransform();
}
bool FOculusHandTracking::IsPointerPoseValid(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsPointerPoseValid;
}
}
}
}
#endif
return false;
}
bool FOculusHandTracking::IsHandTrackingEnabled()
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
ovrpBool result;
FOculusXRHMDModule::GetPluginWrapper().GetHandTrackingEnabled(&result);
return result == ovrpBool_True;
#else
return false;
#endif
}
bool FOculusHandTracking::IsHandDominant(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsDominantHand;
}
}
}
}
#endif
return false;
}
bool FOculusHandTracking::IsHandPositionValid(int32 ControllerIndex, EOculusXRHandType DeviceHand)
{
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsPositionValid;
}
}
}
}
return false;
}
bool FOculusHandTracking::GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, const EOculusXRHandType SkeletonType, const EOculusXRHandType MeshType, const float WorldToMeters)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
if (HandSkeletalMesh)
{
ovrpMesh* OvrMesh = new ovrpMesh();
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
ovrpMeshType OvrMeshType = (ovrpMeshType)((int32)MeshType - 1);
ovrpResult SkelResult = FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton);
ovrpResult MeshResult = FOculusXRHMDModule::GetPluginWrapper().GetMesh(OvrMeshType, OvrMesh);
if (SkelResult != ovrpSuccess || MeshResult != ovrpSuccess)
{
#if !WITH_EDITOR
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get mesh or skeleton data from Oculus runtime."));
#endif
delete OvrMesh;
delete OvrSkeleton;
return false;
}
// Create Skeletal Mesh LOD Render Data
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = new FSkeletalMeshLODModel();
HandSkeletalMesh->GetImportedModel()->LODModels.Add(LodRenderData);
#else
FSkeletalMeshLODRenderData* LodRenderData = new FSkeletalMeshLODRenderData();
HandSkeletalMesh->AllocateResourceForRendering();
HandSkeletalMesh->GetResourceForRendering()->LODRenderData.Add(LodRenderData);
#endif
// Set default LOD Info
FSkeletalMeshLODInfo& LodInfo = HandSkeletalMesh->AddLODInfo();
LodInfo.ScreenSize = 0.3f;
LodInfo.LODHysteresis = 0.2f;
LodInfo.BuildSettings.bUseFullPrecisionUVs = true;
InitializeHandSkeleton(HandSkeletalMesh, OvrSkeleton, WorldToMeters);
// Add default material as backup
LodInfo.LODMaterialMap.Add(0);
UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface);
HandSkeletalMesh->GetMaterials().Add(DefaultMaterial);
HandSkeletalMesh->GetMaterials()[0].UVChannelData.bInitialized = true;
// Set skeletal mesh properties
HandSkeletalMesh->SetHasVertexColors(true);
HandSkeletalMesh->SetHasBeenSimplified(false);
HandSkeletalMesh->SetEnablePerPolyCollision(false);
InitializeHandMesh(HandSkeletalMesh, OvrMesh, WorldToMeters);
#if WITH_EDITOR
HandSkeletalMesh->InvalidateDeriveDataCacheGUID();
HandSkeletalMesh->PostEditChange();
#endif
// Create Skeleton object and merge all bones
HandSkeletalMesh->SetSkeleton(NewObject<USkeleton>());
HandSkeletalMesh->GetSkeleton()->MergeAllBonesToBoneTree(HandSkeletalMesh);
HandSkeletalMesh->PostLoad();
delete OvrMesh;
delete OvrSkeleton;
return true;
}
#endif
return false;
}
void FOculusHandTracking::InitializeHandMesh(USkeletalMesh* SkeletalMesh, const ovrpMesh* OvrMesh, const float WorldToMeters)
{
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
// Initialize mesh section
LodRenderData->Sections.SetNumUninitialized(1);
new (&LodRenderData->Sections[0]) FSkelMeshSection();
auto& MeshSection = LodRenderData->Sections[0];
// Set default mesh section properties
MeshSection.MaterialIndex = 0;
MeshSection.BaseIndex = 0;
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
MeshSection.BaseVertexIndex = 0;
MeshSection.MaxBoneInfluences = 4;
MeshSection.NumVertices = OvrMesh->NumVertices;
float MaxDistSq = MIN_flt;
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
{
FSoftSkinVertex SoftVertex;
FMemory::Memzero(SoftVertex.InfluenceWeights);
FMemory::Memzero(SoftVertex.InfluenceBones);
// Update vertex data
SoftVertex.Color = FColor::White;
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
SoftVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
SoftVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
SoftVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
SoftVertex.TangentY = FVector3f(0.0f, 1.0f, 0.0f); // SoftVertex.TangentZ^ SoftVertex.TangentX* SoftVertex.TangentZ.W;
SoftVertex.UVs[0] = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
// Update the Bounds
float VertexDistSq = SoftVertex.Position.SizeSquared();
if (VertexDistSq > MaxDistSq)
MaxDistSq = VertexDistSq;
// Update blend weights and indices
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
SoftVertex.InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
SoftVertex.InfluenceBones[0] = BlendIndices.x;
SoftVertex.InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
SoftVertex.InfluenceBones[1] = BlendIndices.y;
SoftVertex.InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
SoftVertex.InfluenceBones[2] = BlendIndices.z;
SoftVertex.InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
SoftVertex.InfluenceBones[3] = BlendIndices.w;
MeshSection.SoftVertices.Add(SoftVertex);
}
// Update bone map
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
{
MeshSection.BoneMap.Add(BoneIndex);
}
// Update LOD render data
LodRenderData->NumVertices = OvrMesh->NumVertices;
LodRenderData->NumTexCoords = 1;
// Create index buffer
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
{
LodRenderData->IndexBuffer.Add(OvrMesh->Indices[Index]);
}
// Finalize Bounds
float MaxDist = FMath::Sqrt(MaxDistSq);
FBoxSphereBounds Bounds;
Bounds.Origin = FVector::ZeroVector;
Bounds.BoxExtent = FVector(MaxDist);
Bounds.SphereRadius = MaxDist;
SkeletalMesh->SetImportedBounds(Bounds);
#else
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
// Initialize Mesh Section
LodRenderData->RenderSections.SetNumUninitialized(1);
new (&LodRenderData->RenderSections[0]) FSkelMeshRenderSection();
auto& MeshSection = LodRenderData->RenderSections[0];
// Initialize render section properties
MeshSection.MaterialIndex = 0;
MeshSection.BaseIndex = 0;
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
MeshSection.BaseVertexIndex = 0;
MeshSection.MaxBoneInfluences = 4;
MeshSection.NumVertices = OvrMesh->NumVertices;
MeshSection.bCastShadow = true;
MeshSection.bDisabled = false;
MeshSection.bRecomputeTangent = false;
// Initialize Vertex Buffers
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.Init(OvrMesh->NumVertices);
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.Init(OvrMesh->NumVertices, 1);
LodRenderData->StaticVertexBuffers.ColorVertexBuffer.Init(OvrMesh->NumVertices);
// Initialize Skin Weights
TArray<FSkinWeightInfo> InWeights;
InWeights.AddUninitialized(OvrMesh->NumVertices);
float MaxDistSq = MIN_flt;
TMap<int32, TArray<int32>> OverlappingVertices;
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
{
FMemory::Memzero(InWeights[VertexIndex].InfluenceWeights);
FMemory::Memzero(InWeights[VertexIndex].InfluenceBones);
// Initialize vertex data
FModelVertex ModelVertex;
// Update Model Vertex
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
ModelVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
ModelVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
ModelVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
ModelVertex.TexCoord = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
// Add Model Vertex data to vertex buffer
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex) = ModelVertex.Position;
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(VertexIndex, ModelVertex.TangentX, ModelVertex.GetTangentY(), ModelVertex.TangentZ);
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexUV(VertexIndex, 0, ModelVertex.TexCoord);
// Update the Bounds
float VertexDistSq = ModelVertex.Position.SizeSquared();
if (VertexDistSq > MaxDistSq)
MaxDistSq = VertexDistSq;
// Set vertex blend weights and indices
TArray<int32> Vertices;
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
InWeights[VertexIndex].InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
InWeights[VertexIndex].InfluenceBones[0] = BlendIndices.x;
Vertices.Add(BlendIndices.x);
InWeights[VertexIndex].InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
InWeights[VertexIndex].InfluenceBones[1] = BlendIndices.y;
Vertices.Add(BlendIndices.y);
InWeights[VertexIndex].InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
InWeights[VertexIndex].InfluenceBones[2] = BlendIndices.z;
Vertices.Add(BlendIndices.z);
InWeights[VertexIndex].InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
InWeights[VertexIndex].InfluenceBones[3] = BlendIndices.w;
Vertices.Add(BlendIndices.w);
OverlappingVertices.Add(VertexIndex, Vertices);
}
// Update bone map for mesh section
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
{
MeshSection.BoneMap.Add(BoneIndex);
}
// Finalize Bounds
float MaxDist = FMath::Sqrt(MaxDistSq);
FBoxSphereBounds Bounds;
Bounds.Origin = FVector::ZeroVector;
Bounds.BoxExtent = FVector(MaxDist);
Bounds.SphereRadius = MaxDist;
SkeletalMesh->SetImportedBounds(Bounds);
// Assign skin weights to vertex buffer
LodRenderData->SkinWeightVertexBuffer = InWeights;
MeshSection.DuplicatedVerticesBuffer.Init(OvrMesh->NumVertices, OverlappingVertices);
// Set index buffer
LodRenderData->MultiSizeIndexContainer.CreateIndexBuffer(sizeof(uint16_t));
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
{
LodRenderData->MultiSizeIndexContainer.GetIndexBuffer()->AddItem(OvrMesh->Indices[Index]);
}
#endif
}
void FOculusHandTracking::InitializeHandSkeleton(USkeletalMesh* SkeletalMesh, const ovrpSkeleton2* OvrSkeleton, const float WorldToMeters)
{
SkeletalMesh->GetRefSkeleton().Empty(OvrSkeleton->NumBones);
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
#else
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
#endif
SkeletalMesh->SetHasBeenSimplified(false);
SkeletalMesh->SetHasVertexColors(true);
checkf(OvrSkeleton->NumBones <= static_cast<unsigned int>(TNumericLimits<uint8>::Max()), TEXT("Bone indices are stored as uint8 type."));
for (uint8 BoneIndex = 0; BoneIndex < static_cast<uint8>(OvrSkeleton->NumBones); BoneIndex++)
{
LodRenderData->ActiveBoneIndices.Add(BoneIndex);
LodRenderData->RequiredBones.Add(BoneIndex);
FText BoneDisplayName;
if (!FindBoneDisplayName(BoneDisplayName, BoneIndex))
{
UE_LOG(LogOcHandTracking, Error, TEXT("Cannot find bone display name for bone index: %d."), BoneIndex)
continue;
}
FString BoneString = BoneDisplayName.ToString();
FName BoneName = FName(*BoneString);
FTransform Transform = FTransform::Identity;
FVector BonePosition = OvrBoneVectorToFVector(OvrSkeleton->Bones[BoneIndex].Pose.Position, WorldToMeters);
FQuat BoneRotation = BoneIndex == 0 ? FQuat(-1.0f, 0.0f, 0.0f, 1.0f) : OvrBoneQuatToFQuat(OvrSkeleton->Bones[BoneIndex].Pose.Orientation);
Transform.SetLocation(BonePosition);
Transform.SetRotation(BoneRotation);
FReferenceSkeletonModifier Modifier = FReferenceSkeletonModifier(SkeletalMesh->GetRefSkeleton(), nullptr);
int32 ParentIndex = -1;
if (BoneIndex > 0)
{
if (OvrSkeleton->Bones[BoneIndex].ParentBoneIndex == ovrpBoneId::ovrpBoneId_Invalid)
{
ParentIndex = 0;
}
else
{
ParentIndex = OvrSkeleton->Bones[BoneIndex].ParentBoneIndex;
}
}
Modifier.Add(FMeshBoneInfo(BoneName, BoneString, ParentIndex), Transform);
}
SkeletalMesh->CalculateInvRefMatrices();
}
TArray<FOculusXRCapsuleCollider> FOculusHandTracking::InitializeHandPhysics(const EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters)
{
TArray<FOculusXRCapsuleCollider> CollisionCapsules;
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
if (FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton) != ovrpSuccess)
{
#if !WITH_EDITOR
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get skeleton data from Oculus runtime."));
#endif
delete OvrSkeleton;
return CollisionCapsules;
}
#endif
TArray<UPrimitiveComponent*> IgnoreCapsules;
CollisionCapsules.AddDefaulted(OvrSkeleton->NumBoneCapsules);
for (uint32 CapsuleIndex = 0; CapsuleIndex < OvrSkeleton->NumBoneCapsules; CapsuleIndex++)
{
ovrpBoneCapsule OvrBoneCapsule = OvrSkeleton->BoneCapsules[CapsuleIndex];
UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(HandComponent);
FVector CapsulePointZero = OvrBoneVectorToFVector(OvrBoneCapsule.Points[0], WorldToMeters);
FVector CapsulePointOne = OvrBoneVectorToFVector(OvrBoneCapsule.Points[1], WorldToMeters);
FVector Delta = (CapsulePointOne - CapsulePointZero);
FName BoneName = HandComponent->GetSkinnedAsset()->GetRefSkeleton().GetBoneName(OvrBoneCapsule.BoneIndex);
float CapsuleHeight = Delta.Size();
float CapsuleRadius = OvrBoneCapsule.Radius * WorldToMeters;
Capsule->SetCapsuleRadius(CapsuleRadius);
Capsule->SetCapsuleHalfHeight(Delta.Size() / 2 + CapsuleRadius);
Capsule->SetupAttachment(HandComponent, BoneName);
Capsule->SetCollisionProfileName(HandComponent->GetCollisionProfileName());
Capsule->RegisterComponentWithWorld(HandComponent->GetWorld());
Capsule->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
FRotator CapsuleRotation = FQuat::FindBetweenVectors(FVector::RightVector, Delta).Rotator() + FRotator(0, 0, 90);
;
Capsule->SetRelativeRotation(CapsuleRotation);
Capsule->SetRelativeLocation(CapsulePointZero + (Delta / 2));
CollisionCapsules[CapsuleIndex].Capsule = Capsule;
CollisionCapsules[CapsuleIndex].BoneId = (EOculusXRBone)OvrBoneCapsule.BoneIndex;
IgnoreCapsules.Add(Capsule);
}
for (int32 CapsuleIndex = 0; CapsuleIndex < CollisionCapsules.Num(); CapsuleIndex++)
{
CollisionCapsules[CapsuleIndex].Capsule->MoveIgnoreComponents = IgnoreCapsules;
}
return CollisionCapsules;
}
ovrpBoneId FOculusHandTracking::ToOvrBone(EOculusXRBone Bone)
{
if (Bone > EOculusXRBone::Bone_Max)
return ovrpBoneId_Invalid;
return static_cast<ovrpBoneId>(Bone);
}
FString FOculusHandTracking::GetBoneName(const uint8 Bone)
{
FText DisplayName;
if (FindBoneDisplayName(DisplayName, Bone))
{
return DisplayName.ToString();
}
if (FindBoneDisplayName(DisplayName, static_cast<uint8>(EOculusXRBone::Invalid)))
{
return DisplayName.ToString();
}
return { "Invalid" };
}
bool FOculusHandTracking::FindBoneDisplayName(FText& DisplayName, uint8 Bone)
{
return StaticEnum<EOculusXRBone>()->FindDisplayNameTextByValue(DisplayName, Bone);
}
EOculusXRTrackingConfidence FOculusHandTracking::ToEOculusXRTrackingConfidence(ovrpTrackingConfidence Confidence)
{
EOculusXRTrackingConfidence TrackingConfidence = EOculusXRTrackingConfidence::Low;
switch (Confidence)
{
case ovrpTrackingConfidence_Low:
TrackingConfidence = EOculusXRTrackingConfidence::Low;
break;
case ovrpTrackingConfidence_High:
TrackingConfidence = EOculusXRTrackingConfidence::High;
break;
}
return TrackingConfidence;
}
FVector FOculusHandTracking::OvrBoneVectorToFVector(ovrpVector3f ovrpVector, float WorldToMeters)
{
return FVector(ovrpVector.x, -ovrpVector.y, ovrpVector.z) * WorldToMeters;
}
FQuat FOculusHandTracking::OvrBoneQuatToFQuat(ovrpQuatf ovrpQuat)
{
return FQuat(ovrpQuat.x, -ovrpQuat.y, ovrpQuat.z, -ovrpQuat.w);
}
EOculusXRControllerDrivenHandPoseTypes FOculusHandTracking::ControllerDrivenHandType = EOculusXRControllerDrivenHandPoseTypes::None;
void FOculusHandTracking::SetControllerDrivenHandPoses(const EOculusXRControllerDrivenHandPoseTypes Type)
{
FOculusHandTracking::ControllerDrivenHandType = Type;
switch (Type)
{
case EOculusXRControllerDrivenHandPoseTypes::None:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(false);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
break;
case EOculusXRControllerDrivenHandPoseTypes::Natural:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(true);
break;
case EOculusXRControllerDrivenHandPoseTypes::Controller:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
break;
}
}
} // namespace OculusXRInput

View File

@@ -0,0 +1,58 @@
// @lint-ignore-every LICENSELINT
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDModule.h"
#include "OculusXRInput.h"
#include "Engine/SkeletalMesh.h"
#include "Components/CapsuleComponent.h"
#include "OculusXRInputFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "OculusHandTracking"
DEFINE_LOG_CATEGORY_STATIC(LogOcHandTracking, Log, All);
//-------------------------------------------------------------------------------------------------
// FOculusHandTracking
//-------------------------------------------------------------------------------------------------
namespace OculusXRInput
{
class FOculusHandTracking
{
public:
// Oculus Hand Tracking
static FQuat GetBoneRotation(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId);
static float GetHandScale(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
static EOculusXRTrackingConfidence GetTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
static EOculusXRTrackingConfidence GetFingerTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusHandAxes Finger); // OCULUS STRIKE
static FTransform GetPointerPose(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const float WorldToMeters = 100.f);
static bool IsPointerPoseValid(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
static bool GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, const EOculusXRHandType SkeletonType, const EOculusXRHandType MeshType, const float WorldToMeters = 100.f);
static TArray<FOculusXRCapsuleCollider> InitializeHandPhysics(const EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters = 100.f);
static EOculusXRTrackingConfidence ToEOculusXRTrackingConfidence(ovrpTrackingConfidence Confidence);
static bool IsHandTrackingEnabled();
static bool IsHandDominant(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
static bool IsHandPositionValid(int32 ControllerIndex, EOculusXRHandType DeviceHand);
static void SetControllerDrivenHandPoses(const EOculusXRControllerDrivenHandPoseTypes Type);
// Helper functions
static ovrpBoneId ToOvrBone(EOculusXRBone Bone);
static FString GetBoneName(uint8 Bone);
static bool FindBoneDisplayName(FText& DisplayName, uint8 Bone);
// Converters for converting from ovr bone space (should match up with ovr avatar)
static FVector OvrBoneVectorToFVector(ovrpVector3f ovrpVector, float WorldToMeters);
static FQuat OvrBoneQuatToFQuat(ovrpQuatf ovrpQuat);
static EOculusXRControllerDrivenHandPoseTypes ControllerDrivenHandType;
private:
// Initializers for runtime hand assets
static void InitializeHandMesh(USkeletalMesh* SkeletalMesh, const ovrpMesh* OvrMesh, const float WorldToMeters);
static void InitializeHandSkeleton(USkeletalMesh* SkeletalMesh, const ovrpSkeleton2* OvrSkeleton, const float WorldToMeters);
};
} // namespace OculusXRInput
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,145 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IOculusXRInputModule.h"
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
#include "OculusXRHMDModule.h"
#include "GenericPlatform/IInputInterface.h"
#include "XRMotionControllerBase.h"
#include "IHapticDevice.h"
#include "OculusXRInputState.h"
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(push, 8)
#endif
#include "OculusXRPluginWrapper.h"
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(pop)
#endif
DEFINE_LOG_CATEGORY_STATIC(LogOcInput, Log, All);
class UHapticFeedbackEffect_Base;
struct FActiveHapticFeedbackEffect;
struct FOculusXRHapticsDesc;
namespace OculusXRInput
{
//-------------------------------------------------------------------------------------------------
// FOculusXRInput
//-------------------------------------------------------------------------------------------------
class FOculusXRInput : public IInputDevice, public FXRMotionControllerBase, public IHapticDevice
{
friend class FOculusHandTracking;
public:
/** Constructor that takes an initial message handler that will receive motion controller events */
FOculusXRInput(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler);
/** Clean everything up */
virtual ~FOculusXRInput();
static void PreInit();
/** Loads any settings from the config folder that we need */
static void LoadConfig();
// IInputDevice overrides
virtual void Tick(float DeltaTime) override;
virtual void SendControllerEvents() override;
virtual void SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
virtual void SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override;
virtual void SetChannelValues(int32 ControllerId, const FForceFeedbackValues& Values) override;
virtual bool SupportsForceFeedback(int32 ControllerId) override;
// IMotionController overrides
virtual FName GetMotionControllerDeviceTypeName() const override;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const EControllerHand DeviceHand, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const override;
virtual ETrackingStatus GetControllerTrackingStatus(const int32 ControllerIndex, const EControllerHand DeviceHand) const override;
#endif
virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const override;
virtual ETrackingStatus GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const override;
// IHapticDevice overrides
IHapticDevice* GetHapticDevice() override { return (IHapticDevice*)this; }
virtual void SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values) override;
void PlayHapticEffect(
UHapticFeedbackEffect_Base* HapticEffect,
EControllerHand Hand,
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
bool bAppend = false,
float Scale = 1.f,
bool bLoop = false);
int PlayHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1, bool bPCM = false, bool bAppend = false);
void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
virtual void GetHapticFrequencyRange(float& MinFrequency, float& MaxFrequency) const override;
virtual float GetHapticAmplitudeScale() const override;
uint32 GetNumberOfTouchControllers() const;
uint32 GetNumberOfHandControllers() const;
float GetControllerSampleRateHz(EControllerHand Hand);
int GetMaxHapticDuration(EControllerHand Hand);
private:
/** Applies force feedback settings to the controller */
void UpdateForceFeedback(const FOculusControllerPair& ControllerPair, const EControllerHand Hand);
bool OnControllerButtonPressed(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat);
bool OnControllerButtonReleased(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat);
void SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values, TSharedPtr<FOculusXRHapticsDesc> HapticsDesc);
ovrpHapticsLocation GetOVRPHapticsLocation(EOculusXRHandHapticsLocation InLocation);
void ProcessHaptics(const float DeltaTime);
bool GetOvrpHapticsDesc(int Hand);
private:
/** The recipient of motion controller input events */
TSharedPtr<FGenericApplicationMessageHandler> MessageHandler;
/** List of the connected pairs of controllers, with state for each controller device */
TArray<FOculusControllerPair> ControllerPairs;
FOculusRemoteControllerState Remote;
ovrpHapticsDesc OvrpHapticsDesc;
int LocalTrackingSpaceRecenterCount;
// Maintain a cache of resampled raw data so we don't resample it on every play. This is a map of OriginalRawData pointers, used only as a key, to ResampledRawData buffers.
// The values are pointers because the map could be reallocated and we cache raw pointers to the uint8 array data elsewhere.
TMap<const uint8*, TSharedPtr<TArray<uint8>>> ResampledRawDataCache;
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Left;
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Right;
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Left;
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Right;
double StartTime = 0.0;
/** Threshold for treating trigger pulls as button presses, from 0.0 to 1.0 */
static float TriggerThreshold;
/** Are Remote keys mapped to gamepad or not. */
static bool bRemoteKeysMappedToGamepad;
/** Repeat key delays, loaded from config */
static float InitialButtonRepeatDelay;
static float ButtonRepeatDelay;
static bool bPulledHapticsDesc;
};
} // namespace OculusXRInput
#endif //OCULUS_INPUT_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,175 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRInputFunctionLibrary.h"
#include "OculusXRHandTracking.h"
#include "OculusXRControllerTracking.h"
#include "Logging/MessageLog.h"
#include "Haptics/HapticFeedbackEffect_Buffer.h"
#include "Haptics/HapticFeedbackEffect_Curve.h"
#include "Haptics/HapticFeedbackEffect_SoundWave.h"
//-------------------------------------------------------------------------------------------------
// UOculusHandTrackingFunctionLibrary
//-------------------------------------------------------------------------------------------------
UOculusXRInputFunctionLibrary::UOculusXRInputFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UOculusXRInputFunctionLibrary::FHandMovementFilterDelegate UOculusXRInputFunctionLibrary::HandMovementFilter;
EOculusXRFinger UOculusXRInputFunctionLibrary::ConvertBoneToFinger(const EOculusXRBone Bone)
{
switch (Bone)
{
case EOculusXRBone::Index_1:
case EOculusXRBone::Index_2:
case EOculusXRBone::Index_3:
case EOculusXRBone::Index_Tip:
return EOculusXRFinger::Index;
case EOculusXRBone::Middle_1:
case EOculusXRBone::Middle_2:
case EOculusXRBone::Middle_3:
case EOculusXRBone::Middle_Tip:
return EOculusXRFinger::Middle;
case EOculusXRBone::Pinky_0:
case EOculusXRBone::Pinky_1:
case EOculusXRBone::Pinky_2:
case EOculusXRBone::Pinky_3:
case EOculusXRBone::Pinky_Tip:
return EOculusXRFinger::Pinky;
case EOculusXRBone::Ring_1:
case EOculusXRBone::Ring_2:
case EOculusXRBone::Ring_3:
case EOculusXRBone::Ring_Tip:
return EOculusXRFinger::Ring;
case EOculusXRBone::Thumb_0:
case EOculusXRBone::Thumb_1:
case EOculusXRBone::Thumb_2:
case EOculusXRBone::Thumb_3:
case EOculusXRBone::Thumb_Tip:
return EOculusXRFinger::Thumb;
default:
return EOculusXRFinger::Invalid;
}
}
EOculusXRTrackingConfidence UOculusXRInputFunctionLibrary::GetFingerTrackingConfidence(const EOculusXRHandType DeviceHand, const EOculusXRFinger Finger, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::GetFingerTrackingConfidence(ControllerIndex, DeviceHand, (OculusXRInput::EOculusHandAxes)(uint8)Finger);
}
bool UOculusXRInputFunctionLibrary::GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, EOculusXRHandType SkeletonType, EOculusXRHandType MeshType, float WorldToMeters)
{
return OculusXRInput::FOculusHandTracking::GetHandSkeletalMesh(HandSkeletalMesh, SkeletonType, MeshType, WorldToMeters);
}
TArray<FOculusXRCapsuleCollider> UOculusXRInputFunctionLibrary::InitializeHandPhysics(EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters)
{
return OculusXRInput::FOculusHandTracking::InitializeHandPhysics(SkeletonType, HandComponent, WorldToMeters);
}
FQuat UOculusXRInputFunctionLibrary::GetBoneRotation(const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::GetBoneRotation(ControllerIndex, DeviceHand, BoneId);
}
EOculusXRTrackingConfidence UOculusXRInputFunctionLibrary::GetTrackingConfidence(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::GetTrackingConfidence(ControllerIndex, DeviceHand);
}
FTransform UOculusXRInputFunctionLibrary::GetPointerPose(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::GetPointerPose(ControllerIndex, DeviceHand);
}
bool UOculusXRInputFunctionLibrary::IsPointerPoseValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::IsPointerPoseValid(ControllerIndex, DeviceHand);
}
float UOculusXRInputFunctionLibrary::GetHandScale(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::GetHandScale(ControllerIndex, DeviceHand);
}
EOculusXRHandType UOculusXRInputFunctionLibrary::GetDominantHand(const int32 ControllerIndex)
{
EOculusXRHandType DominantHand = EOculusXRHandType::None;
if (OculusXRInput::FOculusHandTracking::IsHandDominant(ControllerIndex, EOculusXRHandType::HandLeft))
{
DominantHand = EOculusXRHandType::HandLeft;
}
else if (OculusXRInput::FOculusHandTracking::IsHandDominant(ControllerIndex, EOculusXRHandType::HandRight))
{
DominantHand = EOculusXRHandType::HandRight;
}
return DominantHand;
}
bool UOculusXRInputFunctionLibrary::IsHandTrackingEnabled()
{
return OculusXRInput::FOculusHandTracking::IsHandTrackingEnabled();
}
bool UOculusXRInputFunctionLibrary::IsHandPositionValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
{
return OculusXRInput::FOculusHandTracking::IsHandPositionValid(ControllerIndex, DeviceHand);
}
FString UOculusXRInputFunctionLibrary::GetBoneName(EOculusXRBone BoneId)
{
const auto ovrBoneId = OculusXRInput::FOculusHandTracking::ToOvrBone(BoneId);
if (ovrBoneId == ovrpBoneId_Invalid)
{
return OculusXRInput::FOculusHandTracking::GetBoneName(static_cast<uint8>(EOculusXRBone::Invalid));
}
return OculusXRInput::FOculusHandTracking::GetBoneName(static_cast<uint8>(ovrBoneId));
}
void UOculusXRInputFunctionLibrary::PlayCurveHapticEffect(class UHapticFeedbackEffect_Curve* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location, float Scale, bool bLoop)
{
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, Location, false, Scale, bLoop);
}
void UOculusXRInputFunctionLibrary::PlayBufferHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location, float Scale, bool bLoop)
{
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, Location, false, Scale, bLoop);
}
void UOculusXRInputFunctionLibrary::PlayAmplitudeEnvelopeHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand)
{
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(Hand, HapticEffect->Amplitudes, HapticEffect->SampleRate, false, false);
}
void UOculusXRInputFunctionLibrary::PlaySoundWaveHapticEffect(class UHapticFeedbackEffect_SoundWave* HapticEffect, EControllerHand Hand, bool bAppend, float Scale, bool bLoop)
{
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, EOculusXRHandHapticsLocation::Hand, bAppend, Scale, bLoop);
}
void UOculusXRInputFunctionLibrary::StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location)
{
OculusXRInput::FOculusXRControllerTracking::StopHapticEffect(Hand, Location);
}
void UOculusXRInputFunctionLibrary::SetHapticsByValue(const float Frequency, const float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
{
OculusXRInput::FOculusXRControllerTracking::SetHapticsByValue(Frequency, Amplitude, Hand, Location);
}
float UOculusXRInputFunctionLibrary::GetControllerSampleRateHz(EControllerHand Hand)
{
return OculusXRInput::FOculusXRControllerTracking::GetControllerSampleRateHz(Hand);
}
int UOculusXRInputFunctionLibrary::GetMaxHapticDuration(EControllerHand Hand)
{
return OculusXRInput::FOculusXRControllerTracking::GetMaxHapticDuration(Hand);
}
void UOculusXRInputFunctionLibrary::SetControllerDrivenHandPoses(EOculusXRControllerDrivenHandPoseTypes Type)
{
return OculusXRInput::FOculusHandTracking::SetControllerDrivenHandPoses(Type);
}

View File

@@ -0,0 +1,72 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRInputModule.h"
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
#include "OculusXRInput.h"
#include "OculusXRHMDModule.h"
#define LOCTEXT_NAMESPACE "OculusXRInput"
//-------------------------------------------------------------------------------------------------
// FOculusXRInputModule
//-------------------------------------------------------------------------------------------------
void FOculusXRInputModule::StartupModule()
{
IInputDeviceModule::StartupModule();
OculusXRInput::FOculusXRInput::PreInit();
}
TSharedPtr<class IInputDevice> FOculusXRInputModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
{
if (IOculusXRHMDModule::IsAvailable())
{
if (FOculusXRHMDModule::Get().PreInit())
{
TSharedPtr<OculusXRInput::FOculusXRInput> InputDevice(new OculusXRInput::FOculusXRInput(InMessageHandler));
OculusXRInputDevice = InputDevice;
return InputDevice;
}
// else, they may just not have a oculus headset plugged in (which we have to account for - no need for a warning)
}
else
{
UE_LOG(LogOcInput, Warning, TEXT("OculusXRInput plugin enabled, but OculusXRHMD plugin is not available."));
}
return nullptr;
}
uint32 FOculusXRInputModule::GetNumberOfTouchControllers() const
{
if (OculusXRInputDevice.IsValid())
{
return OculusXRInputDevice.Pin()->GetNumberOfTouchControllers();
}
return 0;
}
uint32 FOculusXRInputModule::GetNumberOfHandControllers() const
{
if (OculusXRInputDevice.IsValid())
{
return OculusXRInputDevice.Pin()->GetNumberOfHandControllers();
}
return 0;
}
TSharedPtr<IInputDevice> FOculusXRInputModule::GetInputDevice() const
{
if (OculusXRInputDevice.IsValid())
{
return OculusXRInputDevice.Pin();
}
return nullptr;
}
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRInputModule, OculusXRInput)
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IOculusXRInputModule.h"
#include "IInputDevice.h"
#include "Templates/SharedPointer.h"
#define LOCTEXT_NAMESPACE "OculusXRInput"
//-------------------------------------------------------------------------------------------------
// FOculusXRInputModule
//-------------------------------------------------------------------------------------------------
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
namespace OculusXRInput
{
class FOculusXRInput;
}
class FOculusXRInputModule : public IOculusXRInputModule
{
TWeakPtr<OculusXRInput::FOculusXRInput> OculusXRInputDevice;
// IInputDeviceModule overrides
virtual void StartupModule() override;
virtual TSharedPtr<class IInputDevice> CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
// IOculusXRInputModule overrides
virtual uint32 GetNumberOfTouchControllers() const override;
virtual uint32 GetNumberOfHandControllers() const override;
virtual TSharedPtr<IInputDevice> GetInputDevice() const override;
};
#else // OCULUS_INPUT_SUPPORTED_PLATFORMS
class FOculusXRInputModule : public FDefaultModuleImpl
{
virtual uint32 GetNumberOfTouchControllers() const
{
return 0;
};
virtual uint32 GetNumberOfHandControllers() const
{
return 0;
};
};
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,544 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IOculusXRInputModule.h"
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
#include "IMotionController.h"
#include "InputCoreTypes.h"
#include "OculusXRInputFunctionLibrary.h"
#include "GenericPlatform/GenericApplicationMessageHandler.h"
namespace OculusXRInput
{
//-------------------------------------------------------------------------------------------------
// Button names
//-------------------------------------------------------------------------------------------------
enum class EOculusTouchControllerButton
{
// NOTE: The Trigger and Grip digital buttons are synthetic. Oculus hardware doesn't support a digital press for these
Trigger,
Grip,
XA,
YB,
Thumbstick,
Thumbstick_Up,
Thumbstick_Down,
Thumbstick_Left,
Thumbstick_Right,
Menu,
Thumbstick_Touch,
Trigger_Touch,
XA_Touch,
YB_Touch,
/** Total number of controller buttons */
TotalButtonCount
};
enum class EOculusRemoteControllerButton
{
DPad_Up,
DPad_Down,
DPad_Left,
DPad_Right,
Enter,
Back,
VolumeUp,
VolumeDown,
Home,
/** Total number of controller buttons */
TotalButtonCount
};
enum class EOculusTouchCapacitiveAxes
{
Thumbstick,
Trigger,
XA,
YB,
IndexPointing,
ThumbUp,
ThumbRest,
/** Total number of capacitive axes */
TotalAxisCount
};
enum class EOculusHandButton
{
Thumb,
Index,
Middle,
Ring,
Pinky,
System,
Menu,
TotalButtonCount
};
enum class EOculusHandAxes
{
Thumb,
Index,
Middle,
Ring,
Pinky,
TotalAxisCount
};
//-------------------------------------------------------------------------------------------------
// FOculusKey
//-------------------------------------------------------------------------------------------------
struct FOculusKey
{
static const FKey OculusTouch_Left_Thumbstick;
static const FKey OculusTouch_Left_Trigger;
static const FKey OculusTouch_Left_FaceButton1; // X or A
static const FKey OculusTouch_Left_FaceButton2; // Y or B
static const FKey OculusTouch_Left_IndexPointing;
static const FKey OculusTouch_Left_ThumbUp;
static const FKey OculusTouch_Left_ThumbRest;
static const FKey OculusTouch_Left_ThumbRest_Force;
static const FKey OculusTouch_Left_Stylus_Force;
static const FKey OculusTouch_Left_IndexTrigger_Curl;
static const FKey OculusTouch_Left_IndexTrigger_Slide;
static const FKey OculusTouch_Left_IndexTrigger_Force;
static const FKey OculusTouch_Right_Thumbstick;
static const FKey OculusTouch_Right_Trigger;
static const FKey OculusTouch_Right_FaceButton1; // X or A
static const FKey OculusTouch_Right_FaceButton2; // Y or B
static const FKey OculusTouch_Right_IndexPointing;
static const FKey OculusTouch_Right_ThumbUp;
static const FKey OculusTouch_Right_ThumbRest;
static const FKey OculusTouch_Right_ThumbRest_Force;
static const FKey OculusTouch_Right_Stylus_Force;
static const FKey OculusTouch_Right_IndexTrigger_Curl;
static const FKey OculusTouch_Right_IndexTrigger_Slide;
static const FKey OculusTouch_Right_IndexTrigger_Force;
static const FKey OculusRemote_DPad_Up;
static const FKey OculusRemote_DPad_Down;
static const FKey OculusRemote_DPad_Left;
static const FKey OculusRemote_DPad_Right;
static const FKey OculusRemote_Enter;
static const FKey OculusRemote_Back;
static const FKey OculusRemote_VolumeUp;
static const FKey OculusRemote_VolumeDown;
static const FKey OculusRemote_Home;
static const FKey OculusHand_Left_ThumbPinch;
static const FKey OculusHand_Left_IndexPinch;
static const FKey OculusHand_Left_MiddlePinch;
static const FKey OculusHand_Left_RingPinch;
static const FKey OculusHand_Left_PinkyPinch;
static const FKey OculusHand_Right_ThumbPinch;
static const FKey OculusHand_Right_IndexPinch;
static const FKey OculusHand_Right_MiddlePinch;
static const FKey OculusHand_Right_RingPinch;
static const FKey OculusHand_Right_PinkyPinch;
static const FKey OculusHand_Left_SystemGesture;
static const FKey OculusHand_Right_SystemGesture;
static const FKey OculusHand_Left_ThumbPinchStrength;
static const FKey OculusHand_Left_IndexPinchStrength;
static const FKey OculusHand_Left_MiddlePinchStrength;
static const FKey OculusHand_Left_RingPinchStrength;
static const FKey OculusHand_Left_PinkyPinchStrength;
static const FKey OculusHand_Right_ThumbPinchStrength;
static const FKey OculusHand_Right_IndexPinchStrength;
static const FKey OculusHand_Right_MiddlePinchStrength;
static const FKey OculusHand_Right_RingPinchStrength;
static const FKey OculusHand_Right_PinkyPinchStrength;
};
//-------------------------------------------------------------------------------------------------
// FOculusKeyNames
//-------------------------------------------------------------------------------------------------
struct FOculusKeyNames
{
typedef FName Type;
static const FName OculusTouch_Left_Thumbstick;
static const FName OculusTouch_Left_Trigger;
static const FName OculusTouch_Left_FaceButton1; // X or A
static const FName OculusTouch_Left_FaceButton2; // Y or B
static const FName OculusTouch_Left_IndexPointing;
static const FName OculusTouch_Left_ThumbUp;
static const FName OculusTouch_Left_ThumbRest;
static const FName OculusTouch_Right_Thumbstick;
static const FName OculusTouch_Right_Trigger;
static const FName OculusTouch_Right_FaceButton1; // X or A
static const FName OculusTouch_Right_FaceButton2; // Y or B
static const FName OculusTouch_Right_IndexPointing;
static const FName OculusTouch_Right_ThumbUp;
static const FName OculusTouch_Right_ThumbRest;
static const FName OculusRemote_DPad_Up;
static const FName OculusRemote_DPad_Down;
static const FName OculusRemote_DPad_Left;
static const FName OculusRemote_DPad_Right;
static const FName OculusRemote_Enter;
static const FName OculusRemote_Back;
static const FName OculusRemote_VolumeUp;
static const FName OculusRemote_VolumeDown;
static const FName OculusRemote_Home;
static const FName OculusHand_Left_ThumbPinch;
static const FName OculusHand_Left_IndexPinch;
static const FName OculusHand_Left_MiddlePinch;
static const FName OculusHand_Left_RingPinch;
static const FName OculusHand_Left_PinkyPinch;
static const FName OculusHand_Right_ThumbPinch;
static const FName OculusHand_Right_IndexPinch;
static const FName OculusHand_Right_MiddlePinch;
static const FName OculusHand_Right_RingPinch;
static const FName OculusHand_Right_PinkyPinch;
static const FName OculusHand_Left_SystemGesture;
static const FName OculusHand_Right_SystemGesture;
static const FName OculusHand_Left_ThumbPinchStrength;
static const FName OculusHand_Left_IndexPinchStrength;
static const FName OculusHand_Left_MiddlePinchStrength;
static const FName OculusHand_Left_RingPinchStrength;
static const FName OculusHand_Left_PinkyPinchStrength;
static const FName OculusHand_Right_ThumbPinchStrength;
static const FName OculusHand_Right_IndexPinchStrength;
static const FName OculusHand_Right_MiddlePinchStrength;
static const FName OculusHand_Right_RingPinchStrength;
static const FName OculusHand_Right_PinkyPinchStrength;
};
//-------------------------------------------------------------------------------------------------
// FOculusButtonState - Digital button state
//-------------------------------------------------------------------------------------------------
struct FOculusButtonState
{
/** The Unreal button this maps to. Different depending on whether this is the Left or Right hand controller */
FName Key;
/** The Unreal button this maps to. Different depending on whether this is the Left or Right hand controller */
FName EmulatedKey;
/** Whether we're pressed or not. While pressed, we will generate repeat presses on a timer */
bool bIsPressed;
/** Next time a repeat event should be generated for each button */
double NextRepeatTime;
/** Default constructor that just sets sensible defaults */
FOculusButtonState()
: Key(NAME_None), EmulatedKey(NAME_None), bIsPressed(false), NextRepeatTime(0.0)
{
}
};
//-------------------------------------------------------------------------------------------------
// FOculusTouchCapacitiveState - Capacitive Axis State
//-------------------------------------------------------------------------------------------------
struct FOculusAxisState
{
/** The axis that this button state maps to */
FName Axis;
/** How close the finger is to this button, from 0.f to 1.f */
float State;
FOculusAxisState()
: Axis(NAME_None)
, State(0.f)
{
}
};
struct FOculusControllerState
{
/** True if the device is connected, otherwise false */
bool bIsConnected;
/** True if position is being tracked, otherwise false */
bool bIsPositionTracked;
/** True if position is valid (tracked or estimated), otherwise false */
bool bIsPositionValid;
/** True if orientation is being tracked, otherwise false */
bool bIsOrientationTracked;
/** True if orientation is valid (tracked or estimated), otherwise false */
bool bIsOrientationValid;
FOculusControllerState()
: bIsConnected(false), bIsPositionTracked(false), bIsPositionValid(false), bIsOrientationTracked(false), bIsOrientationValid(false)
{
}
};
//-------------------------------------------------------------------------------------------------
// FOculusTouchControllerState - Input state for an Oculus motion controller
//-------------------------------------------------------------------------------------------------
struct FOculusTouchControllerState : FOculusControllerState
{
/** Analog trigger */
float TriggerAxis;
/** Grip trigger */
float GripAxis;
/** Thumbstick */
FVector2D ThumbstickAxes;
/** Thumbstick */
FVector2D TouchpadAxes;
/** Button states */
FOculusButtonState Buttons[(int32)EOculusTouchControllerButton::TotalButtonCount];
/** Capacitive Touch axes */
FOculusAxisState CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::TotalAxisCount];
/** Thumb Rest Force **/
float ThumbRestForce;
/** Stylus tip**/
float StylusForce;
/** Index trigger Curl**/
float IndexTriggerCurl;
/** Index trigger Slide**/
float IndexTriggerSlide;
/** Second stage Index trigger force **/
float IndexTriggerForce;
/** Whether or not we're playing a haptic effect. If true, force feedback calls will be early-outed in favor of the haptic effect */
bool bPlayingHapticEffect;
/** Haptic frequency (zero to disable) */
float HapticFrequency;
/** Haptic amplitude (zero to disable) */
float HapticAmplitude;
/** Force feedback haptic frequency (zero to disable) */
float ForceFeedbackHapticFrequency;
/** Force feedback haptic amplitude (zero to disable) */
float ForceFeedbackHapticAmplitude;
/** Number of times that controller was recentered (for mobile controllers) */
int RecenterCount;
public:
FHapticFeedbackBuffer ResampledHapticBuffer;
void ResampleHapticBufferData(const FHapticFeedbackBuffer& HapticBuffer, TMap<const uint8*, TSharedPtr<TArray<uint8>>>& ResampledRawDataCache);
/** Explicit constructor sets up sensible defaults */
FOculusTouchControllerState(const EControllerHand Hand)
: TriggerAxis(0.0f), GripAxis(0.0f), ThumbstickAxes(FVector2D::ZeroVector), bPlayingHapticEffect(false), HapticFrequency(0.0f), HapticAmplitude(0.0f), ForceFeedbackHapticFrequency(0.0f), ForceFeedbackHapticAmplitude(0.0f), RecenterCount(0)
{
for (FOculusButtonState& Button : Buttons)
{
Button.bIsPressed = false;
Button.NextRepeatTime = 0.0;
}
Buttons[(int32)EOculusTouchControllerButton::Trigger].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Trigger_Click.GetFName() : EKeys::OculusTouch_Right_Trigger_Click.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Grip].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Grip_Click.GetFName() : EKeys::OculusTouch_Right_Grip_Click.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Thumbstick].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Click.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Click.GetFName();
Buttons[(int32)EOculusTouchControllerButton::XA].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_X_Click.GetFName() : EKeys::OculusTouch_Right_A_Click.GetFName();
Buttons[(int32)EOculusTouchControllerButton::YB].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Y_Click.GetFName() : EKeys::OculusTouch_Right_B_Click.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Up].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Up.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Up.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Down].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Down.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Down.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Left].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Left.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Left.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Right].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Right.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Right.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Menu].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Menu_Click.GetFName() : FName("OculusTouch_Right_System_Click");
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Touch.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Touch.GetFName();
Buttons[(int32)EOculusTouchControllerButton::Trigger_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Trigger_Touch.GetFName() : EKeys::OculusTouch_Right_Trigger_Touch.GetFName();
Buttons[(int32)EOculusTouchControllerButton::XA_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_X_Touch.GetFName() : EKeys::OculusTouch_Right_A_Touch.GetFName();
Buttons[(int32)EOculusTouchControllerButton::YB_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Y_Touch.GetFName() : EKeys::OculusTouch_Right_B_Touch.GetFName();
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::Thumbstick].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_Thumbstick : FOculusKeyNames::OculusTouch_Right_Thumbstick;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::Trigger].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_Trigger : FOculusKeyNames::OculusTouch_Right_Trigger;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::XA].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_FaceButton1 : FOculusKeyNames::OculusTouch_Right_FaceButton1;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::YB].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_FaceButton2 : FOculusKeyNames::OculusTouch_Right_FaceButton2;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::IndexPointing].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_IndexPointing : FOculusKeyNames::OculusTouch_Right_IndexPointing;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::ThumbUp].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_ThumbUp : FOculusKeyNames::OculusTouch_Right_ThumbUp;
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::ThumbRest].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_ThumbRest : FOculusKeyNames::OculusTouch_Right_ThumbRest;
}
/** Default constructor does nothing. Don't use it. This only exists because we cannot initialize an array of objects with no default constructor on non-C++ 11 compliant compilers (VS 2013) */
FOculusTouchControllerState()
{
}
};
//-------------------------------------------------------------------------------------------------
// FOculusHandControllerState - Input state for an Oculus Hands
//-------------------------------------------------------------------------------------------------
struct FOculusHandControllerState : FOculusControllerState
{
/** True if the pointer pose for hands is valid */
bool bIsPointerPoseValid;
/** True if the current hand is the dominant hand */
bool bIsDominantHand;
/** Scale of the hand */
float HandScale;
/** Pose of the pointer */
FTransform PointerPose;
/** Tracking confidence of hand tracking */
EOculusXRTrackingConfidence TrackingConfidence;
/** Finger Pinch States **/
FOculusButtonState HandButtons[(int32)EOculusHandButton::TotalButtonCount];
/** Finger Pinch Strength States **/
FOculusAxisState HandAxes[(int32)EOculusHandAxes::TotalAxisCount];
/** Finger Confidences **/
EOculusXRTrackingConfidence FingerConfidences[(int32)EOculusHandAxes::TotalAxisCount] = {};
FQuat BoneRotations[(int32)EOculusXRBone::Bone_Max];
FOculusHandControllerState(const EControllerHand Hand)
{
TrackingConfidence = EOculusXRTrackingConfidence::Low;
bIsPointerPoseValid = false;
bIsDominantHand = false;
HandScale = 0.0f;
PointerPose = FTransform::Identity;
for (FOculusButtonState& Button : HandButtons)
{
Button.bIsPressed = false;
Button.NextRepeatTime = 0.0;
}
for (int32 i = 0; i < (int32)EOculusXRBone::Bone_Max; i++)
{
BoneRotations[i] = FQuat::Identity;
}
HandButtons[(int32)EOculusHandButton::Thumb].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_ThumbPinch : FOculusKeyNames::OculusHand_Right_ThumbPinch;
HandButtons[(int32)EOculusHandButton::Index].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_IndexPinch : FOculusKeyNames::OculusHand_Right_IndexPinch;
HandButtons[(int32)EOculusHandButton::Middle].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_MiddlePinch : FOculusKeyNames::OculusHand_Right_MiddlePinch;
HandButtons[(int32)EOculusHandButton::Ring].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_RingPinch : FOculusKeyNames::OculusHand_Right_RingPinch;
HandButtons[(int32)EOculusHandButton::Pinky].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_PinkyPinch : FOculusKeyNames::OculusHand_Right_PinkyPinch;
HandButtons[(int32)EOculusHandButton::System].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_SystemGesture : FOculusKeyNames::OculusHand_Right_SystemGesture;
HandButtons[(int32)EOculusHandButton::Menu].Key = (Hand == EControllerHand::Left) ? FGamepadKeyNames::SpecialLeft : FGamepadKeyNames::SpecialRight;
HandAxes[(int32)EOculusHandAxes::Thumb].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_ThumbPinchStrength : FOculusKeyNames::OculusHand_Right_ThumbPinchStrength;
HandAxes[(int32)EOculusHandAxes::Index].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_IndexPinchStrength : FOculusKeyNames::OculusHand_Right_IndexPinchStrength;
HandAxes[(int32)EOculusHandAxes::Middle].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_MiddlePinchStrength : FOculusKeyNames::OculusHand_Right_MiddlePinchStrength;
HandAxes[(int32)EOculusHandAxes::Ring].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_RingPinchStrength : FOculusKeyNames::OculusHand_Right_RingPinchStrength;
HandAxes[(int32)EOculusHandAxes::Pinky].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_PinkyPinchStrength : FOculusKeyNames::OculusHand_Right_PinkyPinchStrength;
}
FOculusHandControllerState()
{
}
};
//-------------------------------------------------------------------------------------------------
// FOculusRemoteControllerState
//-------------------------------------------------------------------------------------------------
struct FOculusRemoteControllerState
{
/** Button states */
FOculusButtonState Buttons[(int32)EOculusRemoteControllerButton::TotalButtonCount];
FOculusRemoteControllerState()
{
for (FOculusButtonState& Button : Buttons)
{
Button.bIsPressed = false;
Button.NextRepeatTime = 0.0;
}
Buttons[(int32)EOculusRemoteControllerButton::DPad_Up].Key = FOculusKeyNames::OculusRemote_DPad_Up;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Down].Key = FOculusKeyNames::OculusRemote_DPad_Down;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Left].Key = FOculusKeyNames::OculusRemote_DPad_Left;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Right].Key = FOculusKeyNames::OculusRemote_DPad_Right;
Buttons[(int32)EOculusRemoteControllerButton::Enter].Key = FOculusKeyNames::OculusRemote_Enter;
Buttons[(int32)EOculusRemoteControllerButton::Back].Key = FOculusKeyNames::OculusRemote_Back;
Buttons[(int32)EOculusRemoteControllerButton::VolumeUp].Key = FOculusKeyNames::OculusRemote_VolumeUp;
Buttons[(int32)EOculusRemoteControllerButton::VolumeDown].Key = FOculusKeyNames::OculusRemote_VolumeDown;
Buttons[(int32)EOculusRemoteControllerButton::Home].Key = FOculusKeyNames::OculusRemote_Home;
}
void MapKeysToGamepad()
{
Buttons[(int32)EOculusRemoteControllerButton::DPad_Up].EmulatedKey = FGamepadKeyNames::DPadUp;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Down].EmulatedKey = FGamepadKeyNames::DPadDown;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Left].EmulatedKey = FGamepadKeyNames::DPadLeft;
Buttons[(int32)EOculusRemoteControllerButton::DPad_Right].EmulatedKey = FGamepadKeyNames::DPadRight;
Buttons[(int32)EOculusRemoteControllerButton::Enter].EmulatedKey = FGamepadKeyNames::SpecialRight;
Buttons[(int32)EOculusRemoteControllerButton::Back].EmulatedKey = FGamepadKeyNames::SpecialLeft;
}
};
//-------------------------------------------------------------------------------------------------
// FOculusControllerPair - A pair of Oculus controllers, hand/touch, one for either hand
//-------------------------------------------------------------------------------------------------
struct FOculusControllerPair
{
/** The input device ID for this oculus controller */
FInputDeviceId DeviceId;
/** Current device state for either hand */
FOculusTouchControllerState TouchControllerStates[2];
FOculusHandControllerState HandControllerStates[2];
FOculusControllerPair()
: DeviceId(INPUTDEVICEID_NONE), TouchControllerStates(), HandControllerStates()
{
TouchControllerStates[(int32)EControllerHand::Left] = FOculusTouchControllerState(EControllerHand::Left);
TouchControllerStates[(int32)EControllerHand::Right] = FOculusTouchControllerState(EControllerHand::Right);
HandControllerStates[(int32)EControllerHand::Left] = FOculusHandControllerState(EControllerHand::Left);
HandControllerStates[(int32)EControllerHand::Right] = FOculusHandControllerState(EControllerHand::Right);
}
};
} // namespace OculusXRInput
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,54 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
#include "IInputDeviceModule.h"
#define OCULUS_INPUT_SUPPORTED_PLATFORMS (PLATFORM_WINDOWS && WINVER > 0x0502) || (PLATFORM_ANDROID_ARM || PLATFORM_ANDROID_ARM64)
/**
* The public interface to this module. In most cases, this interface is only public to sibling modules
* within this plugin.
*/
class IOculusXRInputModule : public IInputDeviceModule
{
public:
/**
* Singleton-like access to this module's interface. This is just for convenience!
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
*
* @return Returns singleton instance, loading the module on demand if needed
*/
static inline IOculusXRInputModule& Get()
{
return FModuleManager::LoadModuleChecked<IOculusXRInputModule>("OculusXRInput");
}
/**
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
*
* @return True if the module is loaded and ready to use
*/
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("OculusXRInput");
}
/**
* Gets the number of Touch controllers that are active, so that games that require them can check to make sure they're present
*
* @return The number of Touch controllers that are active (but not necessarily tracked)
*/
virtual uint32 GetNumberOfTouchControllers() const = 0;
/**
* Gets the number of hands that are active, so that games that require them can check to make sure they're present
*
* @return The number of Hands that are active (but not necessarily tracked)
*/
virtual uint32 GetNumberOfHandControllers() const = 0;
virtual TSharedPtr<IInputDevice> GetInputDevice() const = 0;
};

View File

@@ -0,0 +1,61 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
// A class to render the currently connected controller.
// Similar to how hands are tracked.
#pragma once
#include "OculusXRInputFunctionLibrary.h"
#include "OculusXRFunctionLibrary.h"
#include "Components/StaticMeshComponent.h"
#include <Engine/StreamableManager.h>
// Must always be the last include.
#include "OculusXRControllerComponent.generated.h"
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusHand)
class UOculusXRControllerComponent : public UStaticMeshComponent
{
GENERATED_BODY()
public:
UOculusXRControllerComponent();
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
/** The skeleton that will be loaded */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Properties")
EOculusXRSide SkeletonType;
/** Should this controller be rendered when using controller driven hand poses */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Properties")
bool RenderWhenUsingControllerDrivenHands;
private:
enum MeshLoadingState
{
None,
Loading,
Loaded
};
UStaticMesh* _runtimeMesh;
MeshLoadingState _meshLoadingState;
TSharedPtr<FStreamableHandle> _loadAssetHandle;
FStreamableManager _streamableManager;
EOculusXRControllerType _controllerType;
FSoftObjectPath _runtimeMeshPath;
EOculusXRControllerDrivenHandPoseTypes _cachedControllerHandType;
void InitializeMesh();
void MeshLoaded();
EOculusXRControllerType GetControllerType();
const FVector PositionOffsets
[EOculusXRSideCount]
[EOculusXRControllerDrivenHandPoseTypesCount];
const FVector RotationOffsets
[EOculusXRSideCount]
[EOculusXRControllerDrivenHandPoseTypesCount];
};

View File

@@ -0,0 +1,100 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRInputFunctionLibrary.h"
#include "Components/PoseableMeshComponent.h"
#include "OculusXRHandComponent.generated.h"
UENUM(BlueprintType)
enum class EOculusXRConfidenceBehavior : uint8
{
None,
HideActor
};
UENUM(BlueprintType)
enum class EOculusXRSystemGestureBehavior : uint8
{
None,
SwapMaterial
};
static const FQuat HandRootFixupRotation = FQuat(-0.5f, -0.5f, 0.5f, 0.5f);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusHand)
class OCULUSXRINPUT_API UOculusXRHandComponent : public UPoseableMeshComponent
{
GENERATED_UCLASS_BODY()
public:
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
/** The hand skeleton that will be loaded */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
EOculusXRHandType SkeletonType;
/** The hand mesh that will be applied to the skeleton */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
EOculusXRHandType MeshType;
/** Behavior for when hand tracking loses high confidence tracking */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
EOculusXRConfidenceBehavior ConfidenceBehavior = EOculusXRConfidenceBehavior::HideActor;
/** Behavior for when the system gesture is actived */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
EOculusXRSystemGestureBehavior SystemGestureBehavior = EOculusXRSystemGestureBehavior::SwapMaterial;
/** Material that gets applied to the hands when the system gesture is active */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
class UMaterialInterface* SystemGestureMaterial;
/** Whether or not to initialize physics capsules on the skeletal mesh */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
bool bInitializePhysics;
/** Whether or not the hand scale should update based on values from the runtime to match the users hand scale */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
bool bUpdateHandScale;
/** Material override for the runtime skeletal mesh */
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
class UMaterialInterface* MaterialOverride;
/** Bone mapping for custom hand skeletal meshes */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CustomSkeletalMesh")
TMap<EOculusXRBone, FName> BoneNameMappings;
/** List of capsule colliders created for the skeletal mesh */
UPROPERTY(BlueprintReadOnly, Category = "HandProperties")
TArray<FOculusXRCapsuleCollider> CollisionCapsules;
/** Whether or not the runtime skeletal mesh has been loaded and initialized */
UPROPERTY(BlueprintReadOnly, Category = "HandProperties")
bool bSkeletalMeshInitialized = false;
protected:
virtual void SystemGesturePressed();
virtual void SystemGestureReleased();
private:
/** Whether or not this component has authority within the frame */
bool bHasAuthority;
/** Whether or not a custom hand mesh is being used */
bool bCustomHandMesh = false;
/** Whether or not the physics capsules have been initialized */
bool bInitializedPhysics = false;
USkeletalMesh* RuntimeSkeletalMesh;
UMaterialInterface* CachedBaseMaterial;
void InitializeSkeletalMesh();
void UpdateBonePose();
};

View File

@@ -0,0 +1,350 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Components/CapsuleComponent.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "OculusXRInputFunctionLibrary.generated.h"
UENUM(BlueprintType)
enum class EOculusXRHandType : uint8
{
None,
HandLeft,
HandRight,
};
UENUM(BlueprintType)
enum class EOculusXRSide : uint8
{
None = 0,
Left = 1,
Right = 2,
};
const int EOculusXRSideCount = 3;
UENUM(BlueprintType)
enum class EOculusXRTrackingConfidence : uint8
{
Low,
High
};
UENUM(BlueprintType)
enum class EOculusXRFinger : uint8
{
Thumb,
Index,
Middle,
Ring,
Pinky,
Invalid
};
/**
* EOculusXRBone is enum representing the Bone Ids that come from the Oculus Runtime.
*/
UENUM(BlueprintType)
enum class EOculusXRBone : uint8
{
Wrist_Root UMETA(DisplayName = "Wrist Root"),
Hand_Start = Wrist_Root UMETA(DisplayName = "Hand Start"),
Forearm_Stub UMETA(DisplayName = "Forearm Stub"),
Thumb_0 UMETA(DisplayName = "Thumb0"),
Thumb_1 UMETA(DisplayName = "Thumb1"),
Thumb_2 UMETA(DisplayName = "Thumb2"),
Thumb_3 UMETA(DisplayName = "Thumb3"),
Index_1 UMETA(DisplayName = "Index1"),
Index_2 UMETA(DisplayName = "Index2"),
Index_3 UMETA(DisplayName = "Index3"),
Middle_1 UMETA(DisplayName = "Middle1"),
Middle_2 UMETA(DisplayName = "Middle2"),
Middle_3 UMETA(DisplayName = "Middle3"),
Ring_1 UMETA(DisplayName = "Ring1"),
Ring_2 UMETA(DisplayName = "Ring2"),
Ring_3 UMETA(DisplayName = "Ring3"),
Pinky_0 UMETA(DisplayName = "Pinky0"),
Pinky_1 UMETA(DisplayName = "Pinky1"),
Pinky_2 UMETA(DisplayName = "Pinky2"),
Pinky_3 UMETA(DisplayName = "Pinky3"),
Thumb_Tip UMETA(DisplayName = "Thumb Tip"),
Max_Skinnable = Thumb_Tip UMETA(DisplayName = "Max Skinnable"),
Index_Tip UMETA(DisplayName = "Index Tip"),
Middle_Tip UMETA(DisplayName = "Middle Tip"),
Ring_Tip UMETA(DisplayName = "Ring Tip"),
Pinky_Tip UMETA(DisplayName = "Pinky Tip"),
Hand_End UMETA(DisplayName = "Hand End"),
Bone_Max = Hand_End UMETA(DisplayName = "Hand Max"),
Invalid UMETA(DisplayName = "Invalid")
};
/** Defines the haptics location of controller hands for tracking. */
UENUM(BlueprintType)
enum class EOculusXRHandHapticsLocation : uint8
{
Hand = 0, // Haptics is applied to the whole controller
Thumb, // Haptics is applied to the thumb finger location
Index, // Haptics is applied to the index finger location
HandHapticsLocation_Count UMETA(Hidden, DisplayName = "<INVALID>"),
};
/** Define how a controllers button touches will be used to generate a hand pose. */
UENUM(BlueprintType)
enum class EOculusXRControllerDrivenHandPoseTypes : uint8
{
None = 0, // Controllers do not generate any hand poses.
Natural, // Controller button inputs will be used to generate a normal hand pose.
Controller, // Controller button inputs will be used to generate a hand pose holding a controller.
};
const int EOculusXRControllerDrivenHandPoseTypesCount = 3;
struct FOculusXRHapticsDesc
{
FOculusXRHapticsDesc(
EOculusXRHandHapticsLocation ILocation = EOculusXRHandHapticsLocation::Hand,
bool bIAppend = false)
: Location(ILocation), bAppend(bIAppend)
{
}
void Restart()
{
Location = EOculusXRHandHapticsLocation::Hand;
bAppend = false;
}
EOculusXRHandHapticsLocation Location;
bool bAppend;
};
/**
* FOculusXRCapsuleCollider is a struct that contains information on the physics/collider capsules created by the runtime for hands.
*
* @var Capsule The UCapsuleComponent that is the collision capsule on the bone. Use this to register for overlap/collision events
* @var BoneIndex The Bone that this collision capsule is parented to. Corresponds to the EOculusXRBone enum.
*
*/
USTRUCT(BlueprintType)
struct OCULUSXRINPUT_API FOculusXRCapsuleCollider
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "OculusLibrary|HandTracking")
UCapsuleComponent* Capsule = nullptr;
UPROPERTY(BlueprintReadOnly, Category = "OculusLibrary|HandTracking")
EOculusXRBone BoneId = EOculusXRBone::Wrist_Root;
};
UCLASS()
class OCULUSXRINPUT_API UOculusXRInputFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_UCLASS_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
static EOculusXRFinger ConvertBoneToFinger(const EOculusXRBone Bone);
DECLARE_MULTICAST_DELEGATE_FourParams(FHandMovementFilterDelegate, EControllerHand, FVector*, FRotator*, bool*);
static FHandMovementFilterDelegate HandMovementFilter; /// Called to modify Hand position and orientation whenever it is queried
/**
* Creates a new runtime hand skeletal mesh.
*
* @param HandSkeletalMesh (out) Skeletal Mesh object that will be used for the runtime hand mesh
* @param SkeletonType (in) The skeleton type that will be used for generating the hand bones
* @param MeshType (in) The mesh type that will be used for generating the hand mesh
* @param WorldTometers (in) Optional change to the world to meters conversion value
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
static bool GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, EOculusXRHandType SkeletonType, EOculusXRHandType MeshType, const float WorldToMeters = 100.0f);
/**
* Initializes physics capsules for collision and physics on the runtime mesh
*
* @param SkeletonType (in) The skeleton type that will be used to generated the capsules
* @param HandComponent (in) The skinned mesh component that the capsules will be attached to
* @param WorldTometers (in) Optional change to the world to meters conversion value
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
static TArray<FOculusXRCapsuleCollider> InitializeHandPhysics(EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters = 100.0f);
/**
* Get the rotation of a specific bone
*
* @param DeviceHand (in) The hand to get the rotations from
* @param BoneId (in) The specific bone to get the rotation from
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static FQuat GetBoneRotation(const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId, const int32 ControllerIndex = 0);
/**
* Get the pointer pose
*
* @param DeviceHand (in) The hand to get the pointer pose from
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static FTransform GetPointerPose(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
/**
* Check if the pointer pose is a valid pose
*
* @param DeviceHand (in) The hand to get the pointer status from
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static bool IsPointerPoseValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
/**
* Get the tracking confidence of the hand
*
* @param DeviceHand (in) The hand to get tracking confidence of
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static EOculusXRTrackingConfidence GetTrackingConfidence(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
/**
* Get the tracking confidence of a finger
*
* @param DeviceHand (in) The hand to get tracking confidence of
* @param ControllerIndex (in) Optional different controller index
* @param Finger (in) The finger to get tracking confidence of
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static EOculusXRTrackingConfidence GetFingerTrackingConfidence(const EOculusXRHandType DeviceHand, const EOculusXRFinger Finger, const int32 ControllerIndex = 0);
/**
* Get the scale of the hand
*
* @param DeviceHand (in) The hand to get scale of
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static float GetHandScale(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
/**
* Get the user's dominant hand
*
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static EOculusXRHandType GetDominantHand(const int32 ControllerIndex = 0);
/**
* Check if hand tracking is enabled currently
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static bool IsHandTrackingEnabled();
/**
* Check if the hand position is valid
*
* @param DeviceHand (in) The hand to get the position from
* @param ControllerIndex (in) Optional different controller index
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static bool IsHandPositionValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
/**
* Get the bone name from the bone index
*
* @param BoneIndex (in) Bone index to get the name of
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
static FString GetBoneName(EOculusXRBone BoneId);
/**
* Play a haptic feedback curve on the player's controller with location support.
* The curve data will be sampled and sent to controller to vibrate a specific location at each frame.
* @param HapticEffect The haptic effect to play
* @param Hand Which hand to play the effect on
* @param Location Which hand location to play the effect on
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void PlayCurveHapticEffect(class UHapticFeedbackEffect_Curve* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand, float Scale = 1.f, bool bLoop = false);
/**
* Play a haptic feedback buffer on the player's controller with location support.
* In each frame, the buffer data will be sampled and the individual sampled data will be sent to controller to vibrate a specific location.
* @param HapticEffect The haptic effect to play
* @param Hand Which hand to play the effect on
* @param Location Which hand location to play the effect on
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void PlayBufferHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand, float Scale = 1.f, bool bLoop = false);
/**
* Play a haptic feedback buffer on the player's controller.
* All buffer data will be sent to controller together in one frame.
* Data duration should be no greater than controller's maximum haptics duration which can be queried with GetMaxHapticDuration.
* @param HapticEffect The haptic effect to play
* @param Hand Which hand to play the effect on
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void PlayAmplitudeEnvelopeHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand);
/**
* Play a haptic feedback soundwave on the player's controller.
* In each frame, the soundwave data will be split into a batch of data and sent to controller.
* The data duration of each frame is equal to controller's maximum haptics duration which can be queried with GetMaxHapticDuration.
* @param HapticEffect The haptic effect to play
* @param Hand Which hand to play the effect on
* @param bAppend False: any existing samples will be cleared and a new haptic effect will begin; True: samples will be appended to the currently playing effect
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void PlaySoundWaveHapticEffect(class UHapticFeedbackEffect_SoundWave* HapticEffect, EControllerHand Hand, bool bAppend = false, float Scale = 1.f, bool bLoop = false);
/**
* Stops a playing haptic feedback curve at a specific location.
* @param HapticEffect The haptic effect to stop
* @param Hand Which hand to stop the effect for
* @param Location Which hand location to play the effect on
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
/**
* Set the value of the haptics for the specified hand and location directly, using frequency and amplitude. NOTE: If a curve is already
* playing for this hand, it will be cancelled in favour of the specified values.
*
* @param Frequency The normalized frequency [0.0, 1.0] to play through the haptics system
* @param Amplitude The normalized amplitude [0.0, 1.0] to set the haptic feedback to
* @param Hand Which hand to play the effect on
* @param Location Which hand location to play the effect on
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void SetHapticsByValue(const float Frequency, const float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
/**
* Get the controller haptics sample rate.
* @param Hand Which hand to play the effect on
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static float GetControllerSampleRateHz(EControllerHand Hand);
/**
* Get the maximum duration (in seconds) that the controller haptics can handle each time.
* @param Hand Which hand to play the effect on
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static int GetMaxHapticDuration(EControllerHand Hand);
/**
* Set if / how controller inputs are used to build a syntheic hand pose.
* @param Type How the hand should be posed.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
static void SetControllerDrivenHandPoses(EOculusXRControllerDrivenHandPoseTypes Type);
};