Android build settings + metaxr
This commit is contained in:
65
Plugins/MetaXR/Source/OculusXRInput/OculusXRInput.Build.cs
Normal file
65
Plugins/MetaXR/Source/OculusXRInput/OculusXRInput.Build.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// @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",
|
||||
"KhronosOpenXRHeaders",
|
||||
"OculusXRHMD",
|
||||
"OculusXRMR",
|
||||
"OVRPluginXR",
|
||||
"OpenXRHMD"
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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()
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRControllerLegacyPoseTransformComponent.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.h"
|
||||
#include "GameFramework/WorldSettings.h"
|
||||
|
||||
UOculusXRControllerLegacyPoseTransformComponent::UOculusXRControllerLegacyPoseTransformComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetComponentTickEnabled(false);
|
||||
}
|
||||
|
||||
void UOculusXRControllerLegacyPoseTransformComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
USceneComponent* AttachedParentPtr = GetAttachParent();
|
||||
if (OculusXR::IsOpenXRSystem() && AttachedParentPtr != nullptr)
|
||||
{
|
||||
AttachedParentPtr->AddLocalTransform(FTransform(OculusPoseToGripRotation, OculusPoseToGripPosition * GetWorld()->GetWorldSettings()->WorldToMeters).Inverse());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
void FOculusXRControllerTracking::PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
OculusXRInputModule.Get()->PlayAmplitudeEnvelopeHapticEffect(Hand, Amplitudes.Num(), Amplitudes.GetData(), SampleRate);
|
||||
#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
|
||||
@@ -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 void PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate);
|
||||
|
||||
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
|
||||
@@ -0,0 +1,249 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#include "OculusXRHandComponent.h"
|
||||
#include "OculusXRInput.h"
|
||||
#include "OculusXRInputModule.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.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()
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = static_cast<FOculusXRInputModule*>(&FOculusXRInputModule::Get());
|
||||
if (OculusXR::IsOpenXRSystem() && !InputModule->GetHandTrackingOpenXRExtension()->bIsInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
#else
|
||||
// OpenXR session is created after the hand component is initialized
|
||||
if (OculusXR::IsOpenXRSystem() && !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(SkeletonType);
|
||||
}
|
||||
|
||||
#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(EOculusXRHandType HandType)
|
||||
{
|
||||
FQuat HandRootFixupRotation = HandRootFixupRotationOVR;
|
||||
if (OculusXR::IsOpenXRSystem())
|
||||
{
|
||||
check(HandType == EOculusXRHandType::HandLeft || HandType == EOculusXRHandType::HandRight);
|
||||
if (HandType == EOculusXRHandType::HandLeft)
|
||||
{
|
||||
HandRootFixupRotation = LeftHandRootFixupRotationOpenXR;
|
||||
}
|
||||
else if (HandType == EOculusXRHandType::HandRight)
|
||||
{
|
||||
HandRootFixupRotation = RightHandRootFixupRotationOpenXR;
|
||||
}
|
||||
}
|
||||
|
||||
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!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
1196
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRHandTracking.cpp
Normal file
1196
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRHandTracking.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
// @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 "OculusXRInputHandTrackingTypes.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 EHandBoneId ToHandBone(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 FVector HandBoneVectorToFVector(XrVector3f XrVector, float WorldToMeters);
|
||||
static FQuat HandBoneQuatToFQuat(XrQuaternionf XrQuat);
|
||||
|
||||
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);
|
||||
static void InitializeHandMeshOpenXR(USkeletalMesh* SkeletalMesh, const TSharedPtr<FHandMesh> Mesh, const float WorldToMeters);
|
||||
static void InitializeHandSkeletonOpenXR(USkeletalMesh* SkeletalMesh, const TSharedPtr<FHandSkeleton> Skeleton, const float WorldToMeters);
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
1864
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInput.cpp
Normal file
1864
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInput.cpp
Normal file
File diff suppressed because it is too large
Load Diff
172
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInput.h
Normal file
172
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInput.h
Normal file
@@ -0,0 +1,172 @@
|
||||
// @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);
|
||||
|
||||
#define OVR_HAP_LOGGING 0
|
||||
|
||||
class UHapticFeedbackEffect_Base;
|
||||
struct FActiveHapticFeedbackEffect;
|
||||
struct FOculusXRHapticsDesc;
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
class IOculusXRInputBase
|
||||
{
|
||||
public:
|
||||
virtual ~IOculusXRInputBase() = default;
|
||||
|
||||
virtual void PlayHapticEffect(UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bAppend = false,
|
||||
float Scale = 1.f,
|
||||
bool bLoop = false) = 0;
|
||||
virtual void PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1) = 0;
|
||||
virtual void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand) = 0;
|
||||
|
||||
virtual float GetControllerSampleRateHz(EControllerHand Hand) const = 0;
|
||||
virtual int GetMaxHapticDuration(EControllerHand Hand) const = 0;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInput
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class FOculusXRInput : public IInputDevice, public FXRMotionControllerBase, public IHapticDevice
|
||||
{
|
||||
friend class FOculusHandTracking;
|
||||
friend class FOculusXRInputOVR;
|
||||
|
||||
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);
|
||||
void PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1);
|
||||
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);
|
||||
|
||||
static void ShutdownXRFunctionLibrary();
|
||||
|
||||
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;
|
||||
|
||||
protected:
|
||||
static TSharedPtr<IOculusXRInputBase> GetOculusXRInputBaseImpl();
|
||||
static TSharedPtr<IOculusXRInputBase> FunctionLibraryImpl;
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
@@ -0,0 +1,449 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRInputExtensionPlugin.h"
|
||||
#include "OculusXRInputState.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "IOpenXRHMDModule.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "Misc/CoreDelegates.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
void FInputExtensionPlugin::SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
|
||||
{
|
||||
MessageHandler = InMessageHandler;
|
||||
}
|
||||
|
||||
bool FInputExtensionPlugin::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
bool FInputExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FInputExtensionPlugin::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
||||
{
|
||||
bExtTouchControllerProximityAvailable = InModule->IsExtensionEnabled(XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME);
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
||||
{
|
||||
Instance = InInstance;
|
||||
|
||||
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
|
||||
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::OnEvent(XrSession InSession, const XrEventDataBaseHeader* InHeader)
|
||||
{
|
||||
check(InHeader != nullptr);
|
||||
if (InHeader->type == XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING)
|
||||
{
|
||||
const XrEventDataReferenceSpaceChangePending& SpaceChange =
|
||||
reinterpret_cast<const XrEventDataReferenceSpaceChangePending&>(*InHeader);
|
||||
if (SpaceChange.referenceSpaceType == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT)
|
||||
{
|
||||
FCoreDelegates::VRControllerRecentered.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::CreateDerivedActions()
|
||||
{
|
||||
InitializeDerivedActionsArray();
|
||||
|
||||
DerivedActionSet = XR_NULL_HANDLE;
|
||||
XrActionSetCreateInfo ActionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
ActionSetInfo.next = nullptr;
|
||||
// Using max priority since these actions are needed to calculate and send derived inputs
|
||||
ActionSetInfo.priority = ToXrPriority(MAX_int32);
|
||||
FCStringAnsi::Strcpy(ActionSetInfo.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE, "oculustouchderivedinputsactionset");
|
||||
FCStringAnsi::Strcpy(ActionSetInfo.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE, "OculusTouchDerivedInputsActionSet");
|
||||
XR_ENSURE(xrCreateActionSet(Instance, &ActionSetInfo, &DerivedActionSet));
|
||||
|
||||
for (FDerivedActionProperties& DerivedAction : DerivedActions)
|
||||
{
|
||||
XrActionCreateInfo ActionInfo{ XR_TYPE_ACTION_CREATE_INFO };
|
||||
ActionInfo.next = nullptr;
|
||||
ActionInfo.actionType = DerivedAction.Type;
|
||||
ActionInfo.countSubactionPaths = 0;
|
||||
FCStringAnsi::Strcpy(ActionInfo.actionName, XR_MAX_ACTION_NAME_SIZE, TCHAR_TO_ANSI(*DerivedAction.Name.ToLower()));
|
||||
FCStringAnsi::Strcpy(ActionInfo.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE, TCHAR_TO_ANSI(*DerivedAction.Name));
|
||||
XR_ENSURE(xrCreateAction(DerivedActionSet, &ActionInfo, &DerivedAction.Action));
|
||||
}
|
||||
}
|
||||
|
||||
bool FInputExtensionPlugin::GetSuggestedBindings(XrPath InInteractionProfile, TArray<XrActionSuggestedBinding>& OutBindings)
|
||||
{
|
||||
if (DerivedActionSet == XR_NULL_HANDLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FString ProfilePath = FOpenXRPath(InInteractionProfile).ToString();
|
||||
FDerivedActionProfile ActiveProfile = FDerivedActionProfile::OculusTouch;
|
||||
if (ProfilePath == OculusTouchProfilePath)
|
||||
{
|
||||
ActiveProfile = FDerivedActionProfile::OculusTouch;
|
||||
}
|
||||
else if (ProfilePath == OculusTouchProProfilePath)
|
||||
{
|
||||
ActiveProfile = FDerivedActionProfile::OculusTouchPro;
|
||||
}
|
||||
else if (ProfilePath == OculusTouchPlusProfilePath)
|
||||
{
|
||||
ActiveProfile = FDerivedActionProfile::OculusTouchPlus;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (FDerivedActionProperties& DerivedAction : DerivedActions)
|
||||
{
|
||||
if (DerivedAction.Profile != FDerivedActionProfile::All && DerivedAction.Profile != ActiveProfile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
XrPath Path;
|
||||
xrStringToPath(Instance, TCHAR_TO_ANSI(*DerivedAction.OpenXRPath), &Path);
|
||||
OutBindings.Add({ DerivedAction.Action, Path });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::AttachActionSets(TSet<XrActionSet>& OutActionSets)
|
||||
{
|
||||
if (DerivedActionSet != XR_NULL_PATH)
|
||||
{
|
||||
OutActionSets.Add(DerivedActionSet);
|
||||
}
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::GetActiveActionSetsForSync(TArray<XrActiveActionSet>& OutActiveSets)
|
||||
{
|
||||
if (DerivedActionSet != XR_NULL_PATH)
|
||||
{
|
||||
OutActiveSets.Add({ DerivedActionSet, XR_NULL_PATH });
|
||||
}
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::PostSyncActions(XrSession InSession)
|
||||
{
|
||||
if (DerivedActionSet == XR_NULL_PATH)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const double CurrentTime = FPlatformTime::Seconds();
|
||||
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
|
||||
const FPlatformUserId UserId = DeviceMapper.GetPrimaryPlatformUser();
|
||||
const FInputDeviceId DeviceId = DeviceMapper.GetDefaultInputDevice();
|
||||
|
||||
for (FDerivedActionProperties& DerivedAction : DerivedActions)
|
||||
{
|
||||
FDerivedInputState& SavedState = DerivedKeysToState.FindOrAdd(DerivedAction.InputKey);
|
||||
if (DerivedAction.Type == XrActionType::XR_ACTION_TYPE_FLOAT_INPUT)
|
||||
{
|
||||
XrActionStateFloat State{ XR_TYPE_ACTION_STATE_FLOAT };
|
||||
XrActionStateGetInfo Info{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
Info.action = DerivedAction.Action;
|
||||
|
||||
XrResult Result = xrGetActionStateFloat(InSession, &Info, &State);
|
||||
if (XR_SUCCEEDED(Result) && State.isActive)
|
||||
{
|
||||
// handle keys with bool-like float values that need to be inverted
|
||||
if (KeysToInvert.Contains(DerivedAction.InputKey))
|
||||
{
|
||||
State.currentState = FMath::IsNearlyEqual(State.currentState, 0.f) ? 1.f : 0.f;
|
||||
}
|
||||
|
||||
if (State.changedSinceLastSync)
|
||||
{
|
||||
MessageHandler->OnControllerAnalog(DerivedAction.InputKey.GetFName(), UserId, DeviceId, State.currentState);
|
||||
}
|
||||
SavedState.ValueFloat = State.currentState;
|
||||
SavedState.ChangedSinceLastSync = (bool)State.changedSinceLastSync;
|
||||
}
|
||||
}
|
||||
else if (DerivedAction.Type == XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT)
|
||||
{
|
||||
XrActionStateBoolean State{ XR_TYPE_ACTION_STATE_BOOLEAN };
|
||||
XrActionStateGetInfo Info{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||
Info.action = DerivedAction.Action;
|
||||
|
||||
XrResult Result = xrGetActionStateBoolean(InSession, &Info, &State);
|
||||
if (XR_SUCCEEDED(Result) && State.isActive)
|
||||
{
|
||||
const bool bIsPressed = (bool)State.currentState;
|
||||
if (State.changedSinceLastSync)
|
||||
{
|
||||
SendControllerButtonPressed(DerivedAction.InputKey, bIsPressed, UserId, DeviceId, false);
|
||||
SavedState.NextRepeatTime = CurrentTime + InitialButtonRepeatDelay;
|
||||
}
|
||||
else if (bIsPressed && SavedState.NextRepeatTime <= CurrentTime)
|
||||
{
|
||||
SendControllerButtonPressed(DerivedAction.InputKey, bIsPressed, UserId, DeviceId, true);
|
||||
SavedState.NextRepeatTime = CurrentTime + ButtonRepeatDelay;
|
||||
}
|
||||
SavedState.ValueBool = bIsPressed;
|
||||
SavedState.ChangedSinceLastSync = (bool)State.changedSinceLastSync;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
checkf(false, TEXT("Invalid XrActionType for handling Oculus Derived Inputs"));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle derived thumbstick cardinal dpad directions
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
if (Settings->bThumbstickDpadEmulationEnabled)
|
||||
{
|
||||
for (bool isLeft : { true, false })
|
||||
{
|
||||
// Reset changed since last sync state
|
||||
DerivedKeysToState.FindOrAdd(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Up : EKeys::OculusTouch_Right_Thumbstick_Up).ChangedSinceLastSync = false;
|
||||
DerivedKeysToState.FindOrAdd(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Down : EKeys::OculusTouch_Right_Thumbstick_Down).ChangedSinceLastSync = false;
|
||||
DerivedKeysToState.FindOrAdd(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Left : EKeys::OculusTouch_Right_Thumbstick_Left).ChangedSinceLastSync = false;
|
||||
DerivedKeysToState.FindOrAdd(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Right : EKeys::OculusTouch_Right_Thumbstick_Right).ChangedSinceLastSync = false;
|
||||
|
||||
const FDerivedInputState* ThumbstickXState = DerivedKeysToState.Find(isLeft ? EKeys::OculusTouch_Left_Thumbstick_X : EKeys::OculusTouch_Right_Thumbstick_X);
|
||||
const FDerivedInputState* ThumbstickYState = DerivedKeysToState.Find(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Y : EKeys::OculusTouch_Right_Thumbstick_Y);
|
||||
if (ThumbstickXState != nullptr && ThumbstickYState != nullptr && (ThumbstickXState->ChangedSinceLastSync || ThumbstickYState->ChangedSinceLastSync))
|
||||
{
|
||||
// Calculating quadrants for the thumbstick cardinal directions
|
||||
float Angle = FMath::Atan2(ThumbstickYState->ValueFloat, ThumbstickXState->ValueFloat);
|
||||
FVector2D Thumbsticks = FVector2D(ThumbstickXState->ValueFloat, ThumbstickYState->ValueFloat);
|
||||
bool IsAboveThreshold = Thumbsticks.Size() > 0.7f;
|
||||
bool IsUpPressed = IsAboveThreshold && Angle >= (1.0f / 8.0f) * PI && Angle <= (7.0f / 8.0f) * PI;
|
||||
bool IsDownPressed = IsAboveThreshold && Angle >= (-7.0f / 8.0f) * PI && Angle <= (-1.0f / 8.0f) * PI;
|
||||
bool IsLeftPressed = IsAboveThreshold && Angle <= (-5.0f / 8.0f) * PI || Angle >= (5.0f / 8.0f) * PI;
|
||||
bool IsRightPressed = IsAboveThreshold && Angle >= (-3.0f / 8.0f) * PI && Angle <= (3.0f / 8.0f) * PI;
|
||||
|
||||
SendDerivedDpadButtonPressed(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Up : EKeys::OculusTouch_Right_Thumbstick_Up, IsUpPressed, UserId, DeviceId, CurrentTime);
|
||||
SendDerivedDpadButtonPressed(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Down : EKeys::OculusTouch_Right_Thumbstick_Down, IsDownPressed, UserId, DeviceId, CurrentTime);
|
||||
SendDerivedDpadButtonPressed(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Left : EKeys::OculusTouch_Right_Thumbstick_Left, IsLeftPressed, UserId, DeviceId, CurrentTime);
|
||||
SendDerivedDpadButtonPressed(isLeft ? EKeys::OculusTouch_Left_Thumbstick_Right : EKeys::OculusTouch_Right_Thumbstick_Right, IsRightPressed, UserId, DeviceId, CurrentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::SendControllerButtonPressed(FKey InKey, bool bIsPressed, FPlatformUserId UserId, FInputDeviceId DeviceId, bool bIsRepeat)
|
||||
{
|
||||
if (bIsPressed)
|
||||
{
|
||||
MessageHandler->OnControllerButtonPressed(InKey.GetFName(), UserId, DeviceId, bIsRepeat);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageHandler->OnControllerButtonReleased(InKey.GetFName(), UserId, DeviceId, bIsRepeat);
|
||||
}
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::SendDerivedDpadButtonPressed(FKey InKey, bool bIsPressed, FPlatformUserId UserId, FInputDeviceId DeviceId, double CurrentTime)
|
||||
{
|
||||
FDerivedInputState& SavedState = DerivedKeysToState.FindOrAdd(InKey);
|
||||
if (SavedState.ValueBool != bIsPressed)
|
||||
{
|
||||
SendControllerButtonPressed(InKey, bIsPressed, UserId, DeviceId, false);
|
||||
SavedState.NextRepeatTime = CurrentTime + InitialButtonRepeatDelay;
|
||||
SavedState.ChangedSinceLastSync = true;
|
||||
}
|
||||
else if (bIsPressed && SavedState.NextRepeatTime <= CurrentTime)
|
||||
{
|
||||
SendControllerButtonPressed(InKey, bIsPressed, UserId, DeviceId, true);
|
||||
SavedState.NextRepeatTime = CurrentTime + ButtonRepeatDelay;
|
||||
}
|
||||
SavedState.ValueBool = bIsPressed;
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::DestroyDerivedActions()
|
||||
{
|
||||
xrDestroyActionSet(DerivedActionSet);
|
||||
DerivedActionSet = XR_NULL_HANDLE;
|
||||
DerivedActions.Empty();
|
||||
}
|
||||
|
||||
bool FInputExtensionPlugin::GetInputKeyOverrides(TArray<FInputKeyOpenXRProperties>& OutOverrides)
|
||||
{
|
||||
// Each time Unreal sets up input, we must recreate the derived action set
|
||||
if (DerivedActionSet != XR_NULL_HANDLE)
|
||||
{
|
||||
DestroyDerivedActions();
|
||||
}
|
||||
CreateDerivedActions();
|
||||
|
||||
// Input keys compatible with all oculus interaction profiles
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_X_Click, "/user/hand/left/input/x/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Y_Click, "/user/hand/left/input/y/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_X_Touch, "/user/hand/left/input/x/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Y_Touch, "/user/hand/left/input/y/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Menu_Click, "/user/hand/left/input/menu/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Grip_Click, "/user/hand/left/input/squeeze");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Grip_Axis, "/user/hand/left/input/squeeze/value");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Trigger_Click, "/user/hand/left/input/trigger");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Trigger_Axis, "/user/hand/left/input/trigger/value");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Trigger_Touch, "/user/hand/left/input/trigger/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Thumbstick_2D, "/user/hand/left/input/thumbstick");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Thumbstick_X, "/user/hand/left/input/thumbstick/x");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Thumbstick_Y, "/user/hand/left/input/thumbstick/y");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Thumbstick_Click, "/user/hand/left/input/thumbstick/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Left_Thumbstick_Touch, "/user/hand/left/input/thumbstick/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_A_Click, "/user/hand/right/input/a/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_B_Click, "/user/hand/right/input/b/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_A_Touch, "/user/hand/right/input/a/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_B_Touch, "/user/hand/right/input/b/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Grip_Click, "/user/hand/right/input/squeeze");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Grip_Axis, "/user/hand/right/input/squeeze/value");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Trigger_Click, "/user/hand/right/input/trigger");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Trigger_Axis, "/user/hand/right/input/trigger/value");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Trigger_Touch, "/user/hand/right/input/trigger/touch");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Thumbstick_2D, "/user/hand/right/input/thumbstick");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Thumbstick_X, "/user/hand/right/input/thumbstick/x");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Thumbstick_Y, "/user/hand/right/input/thumbstick/y");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Thumbstick_Click, "/user/hand/right/input/thumbstick/click");
|
||||
CreateForAllProfiles(OutOverrides, EKeys::OculusTouch_Right_Thumbstick_Touch, "/user/hand/right/input/thumbstick/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_ThumbRest, "/user/hand/left/input/thumbrest/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_ThumbRest, "/user/hand/right/input/thumbrest/touch");
|
||||
|
||||
if (bExtTouchControllerProximityAvailable)
|
||||
{
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_IndexPointing, "/user/hand/left/input/trigger/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_Trigger_Proximity, "/user/hand/left/input/trigger/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_ThumbUp, "/user/hand/left/input/thumb_fb/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_Thumb_Proximity, "/user/hand/left/input/thumb_fb/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_IndexPointing, "/user/hand/right/input/trigger/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_Trigger_Proximity, "/user/hand/right/input/trigger/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_ThumbUp, "/user/hand/right/input/thumb_fb/proximity_fb");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_Thumb_Proximity, "/user/hand/right/input/thumb_fb/proximity_fb");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexPointing.ToString(), OculusTouchProProfile, "/user/hand/left/input/trigger/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_Trigger_Proximity.ToString(), OculusTouchProProfile, "/user/hand/left/input/trigger/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_ThumbUp.ToString(), OculusTouchProProfile, "/user/hand/left/input/thumb_fb/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_Thumb_Proximity.ToString(), OculusTouchProProfile, "/user/hand/left/input/thumb_fb/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexPointing.ToString(), OculusTouchProProfile, "/user/hand/right/input/trigger/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_Trigger_Proximity.ToString(), OculusTouchProProfile, "/user/hand/right/input/trigger/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_ThumbUp.ToString(), OculusTouchProProfile, "/user/hand/right/input/thumb_fb/proximity_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_Thumb_Proximity.ToString(), OculusTouchProProfile, "/user/hand/right/input/thumb_fb/proximity_fb" });
|
||||
}
|
||||
|
||||
// These keys are duplicated with what Epic already has. Binding for backwards compatibility.
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_Thumbstick, "/user/hand/left/input/thumbstick/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_Trigger, "/user/hand/left/input/trigger/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_FaceButton1, "/user/hand/left/input/x/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Left_FaceButton2, "/user/hand/left/input/y/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_Thumbstick, "/user/hand/right/input/thumbstick/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_Trigger, "/user/hand/right/input/trigger/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_FaceButton1, "/user/hand/right/input/a/touch");
|
||||
CreateForAllProfiles(OutOverrides, FOculusKey::OculusTouch_Right_FaceButton2, "/user/hand/right/input/b/touch");
|
||||
|
||||
// Input keys compatible with only touch pro
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_ThumbRest_Force.ToString(), OculusTouchProProfile, "/user/hand/left/input/thumbrest/force" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_Stylus_Force.ToString(), OculusTouchProProfile, "/user/hand/left/input/stylus_fb/force" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_ThumbRest_Force.ToString(), OculusTouchProProfile, "/user/hand/right/input/thumbrest/force" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_Stylus_Force.ToString(), OculusTouchProProfile, "/user/hand/right/input/stylus_fb/force" });
|
||||
|
||||
// Input keys compatible with only touch plus
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexTrigger_Force.ToString(), OculusTouchPlusProfile, "/user/hand/left/input/trigger/force" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexTrigger_Force.ToString(), OculusTouchPlusProfile, "/user/hand/right/input/trigger/force" });
|
||||
|
||||
// Input key compatible with a mixed selection
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexTrigger_Curl.ToString(), OculusTouchProProfile, "/user/hand/left/input/trigger/curl_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexTrigger_Curl.ToString(), OculusTouchPlusProfile, "/user/hand/left/input/trigger/curl_meta" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexTrigger_Slide.ToString(), OculusTouchProProfile, "/user/hand/left/input/trigger/slide_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Left_IndexTrigger_Slide.ToString(), OculusTouchPlusProfile, "/user/hand/left/input/trigger/slide_meta" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexTrigger_Curl.ToString(), OculusTouchProProfile, "/user/hand/right/input/trigger/curl_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexTrigger_Curl.ToString(), OculusTouchPlusProfile, "/user/hand/right/input/trigger/curl_meta" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexTrigger_Slide.ToString(), OculusTouchProProfile, "/user/hand/right/input/trigger/slide_fb" });
|
||||
OutOverrides.Add({ FOculusKey::OculusTouch_Right_IndexTrigger_Slide.ToString(), OculusTouchPlusProfile, "/user/hand/right/input/trigger/slide_meta" });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::CreateForAllProfiles(TArray<FInputKeyOpenXRProperties>& OutOverrides, FKey InKey, FString Path)
|
||||
{
|
||||
OutOverrides.Add({ InKey.ToString(), OculusTouchProfile, Path });
|
||||
OutOverrides.Add({ InKey.ToString(), OculusTouchProProfile, Path });
|
||||
OutOverrides.Add({ InKey.ToString(), OculusTouchPlusProfile, Path });
|
||||
}
|
||||
|
||||
void FInputExtensionPlugin::InitializeDerivedActionsArray()
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
if (Settings->bThumbstickDpadEmulationEnabled)
|
||||
{
|
||||
// Actions used to derive thumbstick cardinal dpad directions
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbstickX", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Left_Thumbstick_X, "/user/hand/left/input/thumbstick/x", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbstickY", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Left_Thumbstick_Y, "/user/hand/left/input/thumbstick/y", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbstickX", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Right_Thumbstick_X, "/user/hand/right/input/thumbstick/x", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbstickY", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Right_Thumbstick_Y, "/user/hand/right/input/thumbstick/y", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
|
||||
// Remaining thumbstick actions since all thumbstick inputs will now be handled by the derived action set
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbstickClick", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Left_Thumbstick_Click, "/user/hand/left/input/thumbstick/click", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbstickClick", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Right_Thumbstick_Click, "/user/hand/right/input/thumbstick/click", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbstickTouch", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Left_Thumbstick_Touch, "/user/hand/left/input/thumbstick/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbstickTouch", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Right_Thumbstick_Touch, "/user/hand/right/input/thumbstick/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbstick", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Thumbstick, "/user/hand/left/input/thumbstick/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbstick", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Thumbstick, "/user/hand/right/input/thumbstick/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
}
|
||||
|
||||
// Proximity actions are flipped from OpenXR -> UE, so handle these here
|
||||
if (bExtTouchControllerProximityAvailable)
|
||||
{
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftIndexPointing", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexPointing, "/user/hand/left/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightIndexPointing", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexPointing, "/user/hand/right/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTriggerProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Trigger_Proximity, "/user/hand/left/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTriggerProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Trigger_Proximity, "/user/hand/right/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbUp", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_ThumbUp, "/user/hand/left/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbUp", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_ThumbUp, "/user/hand/right/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Thumb_Proximity, "/user/hand/left/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Thumb_Proximity, "/user/hand/right/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
}
|
||||
else
|
||||
{
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftIndexPointing", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexPointing, "/user/hand/left/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightIndexPointing", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexPointing, "/user/hand/right/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTriggerProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Trigger_Proximity, "/user/hand/left/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTriggerProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Trigger_Proximity, "/user/hand/right/input/trigger/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbUp", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_ThumbUp, "/user/hand/left/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbUp", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_ThumbUp, "/user/hand/right/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftThumbProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Thumb_Proximity, "/user/hand/left/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightThumbProximity", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Thumb_Proximity, "/user/hand/right/input/thumb_fb/proximity_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
}
|
||||
|
||||
// Remaining trigger actions since all trigger inputs will now be handled by the derived action set
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTriggerClick", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Left_Trigger_Click, "/user/hand/left/input/trigger", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTriggerAxis", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Left_Trigger_Axis, "/user/hand/left/input/trigger/value", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTriggerTouch", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Left_Trigger_Touch, "/user/hand/left/input/trigger/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTriggerClick", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Right_Trigger_Click, "/user/hand/right/input/trigger", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTriggerAxis", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, EKeys::OculusTouch_Right_Trigger_Axis, "/user/hand/right/input/trigger/value", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTriggerTouch", XrActionType::XR_ACTION_TYPE_BOOLEAN_INPUT, EKeys::OculusTouch_Right_Trigger_Touch, "/user/hand/right/input/trigger/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedLeftTrigger", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_Trigger, "/user/hand/left/input/trigger/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
DerivedActions.Add({ "OculusTouchDerivedRightTrigger", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_Trigger, "/user/hand/right/input/trigger/touch", XR_NULL_HANDLE, FDerivedActionProfile::All });
|
||||
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedLeftTriggerForce", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexTrigger_Force, "/user/hand/left/input/trigger/force", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedRightTriggerForce", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexTrigger_Force, "/user/hand/right/input/trigger/force", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
|
||||
DerivedActions.Add({ "OculusTouchProDerivedLeftTriggerCurl", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexTrigger_Curl, "/user/hand/left/input/trigger/curl_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedLeftTriggerCurl", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexTrigger_Curl, "/user/hand/left/input/trigger/curl_meta", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
DerivedActions.Add({ "OculusTouchProDerivedLeftTriggerSlide", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexTrigger_Slide, "/user/hand/left/input/trigger/slide_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedLeftTriggerSlide", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Left_IndexTrigger_Slide, "/user/hand/left/input/trigger/slide_meta", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
DerivedActions.Add({ "OculusTouchProDerivedRightTriggerCurl", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexTrigger_Curl, "/user/hand/right/input/trigger/curl_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedRightTriggerCurl", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexTrigger_Curl, "/user/hand/right/input/trigger/curl_meta", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
DerivedActions.Add({ "OculusTouchProDerivedRightTriggerSlide", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexTrigger_Slide, "/user/hand/right/input/trigger/slide_fb", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPro });
|
||||
DerivedActions.Add({ "OculusTouchPlusDerivedRightTriggerSlide", XrActionType::XR_ACTION_TYPE_FLOAT_INPUT, FOculusKey::OculusTouch_Right_IndexTrigger_Slide, "/user/hand/right/input/trigger/slide_meta", XR_NULL_HANDLE, FDerivedActionProfile::OculusTouchPlus });
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OculusXRInput.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
enum FDerivedActionProfile
|
||||
{
|
||||
All,
|
||||
OculusTouch,
|
||||
OculusTouchPro,
|
||||
OculusTouchPlus
|
||||
};
|
||||
|
||||
struct FDerivedActionProperties
|
||||
{
|
||||
FString Name;
|
||||
XrActionType Type;
|
||||
FKey InputKey;
|
||||
FString OpenXRPath;
|
||||
XrAction Action;
|
||||
FDerivedActionProfile Profile;
|
||||
};
|
||||
|
||||
struct FDerivedInputState
|
||||
{
|
||||
bool ValueBool;
|
||||
float ValueFloat;
|
||||
bool ChangedSinceLastSync;
|
||||
double NextRepeatTime;
|
||||
|
||||
FDerivedInputState()
|
||||
: ValueBool(false), ValueFloat(0.f), ChangedSinceLastSync(false), NextRepeatTime(0.0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FInputExtensionPlugin : public IOpenXRExtensionPlugin, public IInputDevice
|
||||
{
|
||||
public:
|
||||
FInputExtensionPlugin()
|
||||
: InitialButtonRepeatDelay(DefaultInitialButtonRepeatDelay), ButtonRepeatDelay(DefaultButtonRepeatDelay), MessageHandler(nullptr), bExtTouchControllerProximityAvailable(false), Instance(XR_NULL_HANDLE), DerivedActions({}), DerivedKeysToState({}), DerivedActionSet(XR_NULL_HANDLE)
|
||||
{
|
||||
}
|
||||
|
||||
void RegisterOpenXRExtensionPlugin()
|
||||
{
|
||||
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
#endif
|
||||
}
|
||||
|
||||
// IInputDevice
|
||||
virtual void SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
|
||||
void Tick(float DeltaTime) override {};
|
||||
void SendControllerEvents() override {};
|
||||
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
|
||||
void SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override {};
|
||||
void SetChannelValues(int32 ControllerId, const FForceFeedbackValues& Values) override {};
|
||||
|
||||
private:
|
||||
const FString OculusTouchProfile = TEXT("OculusTouch");
|
||||
const FString OculusTouchProProfile = TEXT("OculusTouchPro");
|
||||
const FString OculusTouchPlusProfile = TEXT("OculusTouchPlus");
|
||||
const FString OculusTouchProfilePath = TEXT("/interaction_profiles/oculus/touch_controller");
|
||||
const FString OculusTouchProProfilePath = TEXT("/interaction_profiles/facebook/touch_controller_pro");
|
||||
const FString OculusTouchPlusProfilePath = TEXT("/interaction_profiles/meta/touch_controller_plus");
|
||||
const TSet<FKey> KeysToInvert = { FOculusKey::OculusTouch_Left_IndexPointing, FOculusKey::OculusTouch_Right_IndexPointing, FOculusKey::OculusTouch_Left_ThumbUp, FOculusKey::OculusTouch_Right_ThumbUp };
|
||||
|
||||
float InitialButtonRepeatDelay;
|
||||
float ButtonRepeatDelay;
|
||||
|
||||
TSharedPtr<FGenericApplicationMessageHandler> MessageHandler;
|
||||
bool bExtTouchControllerProximityAvailable;
|
||||
XrInstance Instance;
|
||||
|
||||
TArray<FDerivedActionProperties> DerivedActions;
|
||||
TMap<FKey, FDerivedInputState> DerivedKeysToState;
|
||||
XrActionSet DerivedActionSet;
|
||||
|
||||
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
public:
|
||||
// IOpenXRExtensionPlugin
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual void OnEvent(XrSession InSession, const XrEventDataBaseHeader* InHeader) override;
|
||||
virtual bool GetInputKeyOverrides(TArray<FInputKeyOpenXRProperties>& OutOverrides) override;
|
||||
virtual void PostCreateInstance(XrInstance InInstance) override;
|
||||
virtual bool GetSuggestedBindings(XrPath InInteractionProfile, TArray<XrActionSuggestedBinding>& OutBindings) override;
|
||||
virtual void PostSyncActions(XrSession InSession) override;
|
||||
virtual void AttachActionSets(TSet<XrActionSet>& OutActionSets) override;
|
||||
virtual void GetActiveActionSetsForSync(TArray<XrActiveActionSet>& OutActiveSets);
|
||||
|
||||
private:
|
||||
virtual void CreateForAllProfiles(TArray<FInputKeyOpenXRProperties>& OutOverrides, FKey InKey, FString Path);
|
||||
virtual void CreateDerivedActions();
|
||||
virtual void InitializeDerivedActionsArray();
|
||||
virtual void SendControllerButtonPressed(FKey InKey, bool bIsPressed, FPlatformUserId UserId, FInputDeviceId DeviceId, bool bIsRepeat);
|
||||
virtual void SendDerivedDpadButtonPressed(FKey InKey, bool bIsPressed, FPlatformUserId UserId, FInputDeviceId DeviceId, double CurrentTime);
|
||||
virtual void DestroyDerivedActions();
|
||||
#endif
|
||||
};
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,186 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
#include "OculusXRControllerLegacyPoseTransformComponent.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::PlayAmplitudeEnvelopeHapticEffect(Hand, HapticEffect->Amplitudes, HapticEffect->SampleRate);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EOculusXRControllerDrivenHandPoseTypes UOculusXRInputFunctionLibrary::GetControllerDrivenHandPoses()
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::ControllerDrivenHandType;
|
||||
}
|
||||
|
||||
FTransform UOculusXRInputFunctionLibrary::GetLegacyOculusPoseTransform(float WorldToMeters)
|
||||
{
|
||||
return FTransform(OculusPoseToGripRotation, OculusPoseToGripPosition * WorldToMeters).Inverse();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "OculusXRInputHandTrackingTypes.h"
|
||||
#include "OculusXRInputState.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FHandTrackingExtensionPlugin : public IOpenXRExtensionPlugin, public IInputDevice
|
||||
{
|
||||
public:
|
||||
FHandTrackingExtensionPlugin()
|
||||
{
|
||||
HandControllerStates.Add(EOculusXRHandType::HandLeft, FOculusHandControllerState(EControllerHand::Left));
|
||||
HandControllerStates.Add(EOculusXRHandType::HandRight, FOculusHandControllerState(EControllerHand::Right));
|
||||
memset(XrLeftHandJointLocations, 0, sizeof(XrLeftHandJointLocations));
|
||||
memset(XrRightHandJointLocations, 0, sizeof(XrRightHandJointLocations));
|
||||
memset(XrLeftHandJointVelocities, 0, sizeof(XrLeftHandJointVelocities));
|
||||
memset(XrRightHandJointVelocities, 0, sizeof(XrRightHandJointVelocities));
|
||||
}
|
||||
|
||||
void RegisterOpenXRExtensionPlugin()
|
||||
{
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
}
|
||||
|
||||
// IInputDevice
|
||||
virtual void SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
|
||||
void Tick(float DeltaTime) override {};
|
||||
void SendControllerEvents() override {};
|
||||
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
|
||||
void SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override {};
|
||||
void SetChannelValues(int32 ControllerId, const FForceFeedbackValues& Values) override {};
|
||||
|
||||
// IOpenXRExtensionPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnGetSystem(XrInstance InInstance, const void* InNext) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual void PostCreateInstance(XrInstance InInstance) override;
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual const void* OnBeginSession(XrSession InSession, const void* InNext) override;
|
||||
virtual void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override;
|
||||
virtual void OnDestroySession(XrSession InSession) override;
|
||||
|
||||
virtual bool IsHandTrackingAvailable();
|
||||
virtual bool IsHandTrackingActive();
|
||||
virtual bool GetSkeleton(EOculusXRHandType HandType, TSharedPtr<FHandSkeleton> Skeleton);
|
||||
virtual bool GetMesh(EOculusXRHandType HandType, TSharedPtr<FHandMesh> Mesh);
|
||||
|
||||
TMap<EOculusXRHandType, FOculusHandControllerState> HandControllerStates;
|
||||
bool bIsInitialized = false;
|
||||
|
||||
private:
|
||||
virtual void SendControllerButtonPressed(FName Key, bool IsPressed, FPlatformUserId UserId, FInputDeviceId DeviceId);
|
||||
|
||||
XrHandJointLocationEXT XrLeftHandJointLocations[XR_HAND_JOINT_COUNT_EXT];
|
||||
XrHandJointLocationEXT XrRightHandJointLocations[XR_HAND_JOINT_COUNT_EXT];
|
||||
XrHandJointVelocityEXT XrLeftHandJointVelocities[XR_HAND_JOINT_COUNT_EXT];
|
||||
XrHandJointVelocityEXT XrRightHandJointVelocities[XR_HAND_JOINT_COUNT_EXT];
|
||||
|
||||
PFN_xrGetHandMeshFB xrGetHandMeshFB = nullptr;
|
||||
PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT = nullptr;
|
||||
PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT = nullptr;
|
||||
PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT = nullptr;
|
||||
|
||||
virtual XrPosef TransformXrHandJointPose(EOculusXRHandType HandType, XrHandJointEXT Joint, XrPosef JointPose, XrPosef RootPose, bool ApplyRootPose);
|
||||
virtual XrHandJointEXT MapBoneToXrHandJoint(EHandBoneId Bone);
|
||||
virtual EHandBoneId MapXrHandJointToBone(XrHandJointEXT Joint);
|
||||
|
||||
virtual FVector XrPoseVectorToFVector(XrVector3f XrVector);
|
||||
virtual FQuat XrPoseQuatToFQuat(XrQuaternionf XrQuat);
|
||||
virtual FQuat XrBoneQuatToFQuat(XrQuaternionf XrQuat);
|
||||
|
||||
bool bExtHandTrackingMeshAvailable = false;
|
||||
bool bHandTrackingAvailable = false;
|
||||
bool bHandTrackingActive = false;
|
||||
|
||||
TSharedPtr<FGenericApplicationMessageHandler> MessageHandler;
|
||||
TMap<EOculusXRHandType, XrHandTrackerEXT> OculusHandTrackers;
|
||||
XrInstance Instance = XR_NULL_HANDLE;
|
||||
FInternalHandsState InternalHandsState;
|
||||
};
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,245 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
#include "xr_linear.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
const XrPosef XrLeftHandLegacyBindPoseThumb0 = {
|
||||
{ 0.375387, 0.424584, -0.007779, 0.823864 },
|
||||
{ 0.020069, 0.011554, -0.010497 }
|
||||
};
|
||||
const XrPosef XrRightHandLegacyBindPoseThumb0 = {
|
||||
{ 0.375387, 0.424584, -0.007779, 0.823864 },
|
||||
{ -0.020069, -0.011554, 0.010497 }
|
||||
};
|
||||
const XrPosef XrLeftHandLegacyBindPoseThumb1 = {
|
||||
{ 0.260230, 0.024331, 0.125678, 0.957023 },
|
||||
{ 0.024853, 0.000000, -0.000000 }
|
||||
};
|
||||
const XrPosef XrRightHandLegacyBindPoseThumb1 = {
|
||||
{ 0.260230, 0.024331, 0.125678, 0.957023 },
|
||||
{ -0.024853, 0.000000, 0.000000 }
|
||||
};
|
||||
const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 )
|
||||
const int MaxHandCount = 2;
|
||||
|
||||
enum class EHandFinger
|
||||
{
|
||||
Thumb = 0,
|
||||
Index = 1,
|
||||
Middle = 2,
|
||||
Ring = 3,
|
||||
Pinky = 4,
|
||||
Count = 5,
|
||||
};
|
||||
|
||||
enum class EHandBoneId
|
||||
{
|
||||
Invalid = -1,
|
||||
|
||||
// hand bones
|
||||
Start = 0,
|
||||
WristRoot = Start + 0, // root frame of the hand, where the wrist is located
|
||||
ForearmStub = Start + 1, // frame for user's forearm
|
||||
Thumb0 = Start + 2, // thumb trapezium bone
|
||||
Thumb1 = Start + 3, // thumb metacarpal bone
|
||||
Thumb2 = Start + 4, // thumb proximal phalange bone
|
||||
Thumb3 = Start + 5, // thumb distal phalange bone
|
||||
Index1 = Start + 6, // index proximal phalange bone
|
||||
Index2 = Start + 7, // index intermediate phalange bone
|
||||
Index3 = Start + 8, // index distal phalange bone
|
||||
Middle1 = Start + 9, // middle proximal phalange bone
|
||||
Middle2 = Start + 10, // middle intermediate phalange bone
|
||||
Middle3 = Start + 11, // middle distal phalange bone
|
||||
Ring1 = Start + 12, // ring proximal phalange bone
|
||||
Ring2 = Start + 13, // ring intermediate phalange bone
|
||||
Ring3 = Start + 14, // ring distal phalange bone
|
||||
Pinky0 = Start + 15, // pinky metacarpal bone
|
||||
Pinky1 = Start + 16, // pinky proximal phalange bone
|
||||
Pinky2 = Start + 17, // pinky intermediate phalange bone
|
||||
Pinky3 = Start + 18, // pinky distal phalange bone
|
||||
MaxSkinnable = Start + 19,
|
||||
// Bone tips are position only. They are not used for skinning but useful for hit-testing.
|
||||
// NOTE: ThumbTip == MaxSkinnable since the extended tips need to be contiguous
|
||||
ThumbTip = MaxSkinnable + 0, // tip of the thumb
|
||||
IndexTip = MaxSkinnable + 1, // tip of the index finger
|
||||
MiddleTip = MaxSkinnable + 2, // tip of the middle finger
|
||||
RingTip = MaxSkinnable + 3, // tip of the ring finger
|
||||
PinkyTip = MaxSkinnable + 4, // tip of the pinky
|
||||
End = MaxSkinnable + 5,
|
||||
|
||||
// Hand Skeleton V2 format
|
||||
StartV2 = 0,
|
||||
PalmV2 = StartV2 + 0, // PALM = 0,
|
||||
WristV2 = StartV2 + 1, // WRIST = 1,
|
||||
ThumbMetacarpalV2 = StartV2 + 2, // THUMB_METACARPAL = 2,
|
||||
ThumbProximalV2 = StartV2 + 3, // THUMB_PROXIMAL = 3,
|
||||
ThumbDistalV2 = StartV2 + 4, // THUMB_DISTAL = 4,
|
||||
ThumbTipV2 = StartV2 + 5, // THUMB_TIP = 5,
|
||||
IndexMetacarpalV2 = StartV2 + 6, // INDEX_METACARPAL = 6,
|
||||
IndexProximalV2 = StartV2 + 7, // INDEX_PROXIMAL = 7,
|
||||
IndexIntermediateV2 = StartV2 + 8, // INDEX_INTERMEDIATE = 8,
|
||||
IndexDistalV2 = StartV2 + 9, // INDEX_DISTAL = 9,
|
||||
IndexTipV2 = StartV2 + 10, // INDEX_TIP = 10,
|
||||
MiddleMetacarpalV2 = StartV2 + 11, // MIDDLE_METACARPAL = 11,
|
||||
MiddleProximalV2 = StartV2 + 12, // MIDDLE_PROXIMAL = 12,
|
||||
MiddleIntermediateV2 = StartV2 + 13, // MIDDLE_INTERMEDIATE = 13,
|
||||
MiddleDistalV2 = StartV2 + 14, // MIDDLE_DISTAL = 14,
|
||||
MiddleTipV2 = StartV2 + 15, // MIDDLE_TIP = 15,
|
||||
RingMetacarpalV2 = StartV2 + 16, // RING_METACARPAL = 16,
|
||||
RingProximalV2 = StartV2 + 17, // RING_PROXIMAL = 17,
|
||||
RingIntermediateV2 = StartV2 + 18, // RING_INTERMEDIATE = 18,
|
||||
RingDistalV2 = StartV2 + 19, // RING_DISTAL = 19,
|
||||
RingTipV2 = StartV2 + 20, // RING_TIP = 20,
|
||||
LittleMetacarpalV2 = StartV2 + 21, // LITTLE_METACARPAL = 21,
|
||||
LittleProximalV2 = StartV2 + 22, // LITTLE_PROXIMAL = 22,
|
||||
LittleIntermediateV2 = StartV2 + 23, // LITTLE_INTERMEDIATE = 23,
|
||||
LittleDistalV2 = StartV2 + 24, // LITTLE_DISTAL = 24,
|
||||
LittleTipV2 = StartV2 + 25, // LITTLE_TIP = 25,
|
||||
EndV2 = StartV2 + 26,
|
||||
|
||||
// add other skeleton bone definitions here...
|
||||
Max = EndV2,
|
||||
};
|
||||
|
||||
enum class EHandFingerPinch
|
||||
{
|
||||
Thumb = (1 << static_cast<int32>(EHandFinger::Thumb)),
|
||||
Index = (1 << static_cast<int32>(EHandFinger::Index)),
|
||||
Middle = (1 << static_cast<int32>(EHandFinger::Middle)),
|
||||
Ring = (1 << static_cast<int32>(EHandFinger::Ring)),
|
||||
Pinky = (1 << static_cast<int32>(EHandFinger::Pinky)),
|
||||
Max,
|
||||
};
|
||||
|
||||
enum class EHandSkeletonConstants
|
||||
{
|
||||
MaxHandBones = static_cast<int32>(EHandBoneId::End),
|
||||
MaxHandBones_V2 = static_cast<int32>(EHandBoneId::EndV2),
|
||||
MaxBones = static_cast<int32>(EHandBoneId::Max),
|
||||
MaxBoneCapsules = 19,
|
||||
};
|
||||
|
||||
struct FHandBoneCapsule
|
||||
{
|
||||
short BoneIndex;
|
||||
// Points at either end of the cylinder inscribed in the capsule. Also the center points for
|
||||
// spheres at either end of the capsule. Points A and B in the diagram above.
|
||||
XrVector3f Points[2];
|
||||
// The radius of the capsule cylinder and of the half-sphere caps on the ends of the capsule.
|
||||
float Radius;
|
||||
};
|
||||
|
||||
struct FHandBone
|
||||
{
|
||||
EHandBoneId BoneId;
|
||||
// index of this bone's parent bone (-1 if no parent)
|
||||
short ParentBoneIndex;
|
||||
XrPosef Pose;
|
||||
};
|
||||
|
||||
struct FHandSkeleton
|
||||
{
|
||||
EOculusXRHandType HandType;
|
||||
unsigned int NumBones;
|
||||
unsigned int NumBoneCapsules;
|
||||
FHandBone Bones[static_cast<int32>(EHandSkeletonConstants::MaxBones)];
|
||||
FHandBoneCapsule BoneCapsules[static_cast<int32>(EHandSkeletonConstants::MaxBoneCapsules)];
|
||||
};
|
||||
|
||||
enum class EHandMeshConstants
|
||||
{
|
||||
MaxVertices = 3000,
|
||||
MaxIndices = MaxVertices * 6,
|
||||
};
|
||||
|
||||
struct FHandMesh
|
||||
{
|
||||
// Type of mesh this data describes.
|
||||
EOculusXRHandType HandType;
|
||||
// Number of unique vertices in the mesh.
|
||||
unsigned int NumVertices;
|
||||
// Number of unique indices in the mesh.
|
||||
unsigned int NumIndices;
|
||||
// An array of count NumVertices positions for each vertex. Always valid.
|
||||
XrVector3f VertexPositions[static_cast<int32>(EHandMeshConstants::MaxVertices)];
|
||||
// An array of count NumIndices of vertex indices specifying triangles that make up the mesh. Always valid.
|
||||
short Indices[static_cast<int32>(EHandMeshConstants::MaxIndices)];
|
||||
// An array of count NumVertices of normals for each vertex.
|
||||
// If null, this attribute is not used.
|
||||
XrVector3f VertexNormals[static_cast<int32>(EHandMeshConstants::MaxVertices)];
|
||||
// An array of count NumVertices of texture coordinates for each vertex.
|
||||
// If null, this attribute is not used.
|
||||
XrVector2f VertexUV0[static_cast<int32>(EHandMeshConstants::MaxVertices)];
|
||||
// An array of count NumVertices of blend indices for each of the bones that each vertex is weighted to.
|
||||
// Always valid. An index of < 0 means no blend weight.
|
||||
XrVector4sFB BlendIndices[static_cast<int32>(EHandMeshConstants::MaxVertices)];
|
||||
// An array of count NumVertices of weights for each of the bones affecting each vertex. Always valid.
|
||||
XrVector4f BlendWeights[static_cast<int32>(EHandMeshConstants::MaxVertices)];
|
||||
};
|
||||
|
||||
enum class EHandStatus
|
||||
{
|
||||
HandTracked = (1 << 0), // hand is currently tracked by hand tracking
|
||||
InputValid = (1 << 1), // if this is set the pointer pose and pinch data is usable
|
||||
SystemGestureInProgress = (1 << 6), // if this is set the user is performing the system gesture
|
||||
DominantHand = (1 << 7), // if this is set the hand is considered the dominant hand
|
||||
MenuPressed = (1 << 8), // if this is set the hand performed the system gesture as the non-dominant hand
|
||||
};
|
||||
|
||||
struct FInternalHandState
|
||||
{
|
||||
// Hand Status bitfield described by ovrpHandStatus flags.
|
||||
unsigned int Status;
|
||||
|
||||
// Root pose of the hand in world space. Not to be confused with the root bone's transform.
|
||||
// The root bone can still be offset from this by the skeleton's rest pose.
|
||||
XrPosef RootPose;
|
||||
|
||||
// Current rotation of each bone.
|
||||
XrQuaternionf BoneRotations[static_cast<int32>(EHandSkeletonConstants::MaxHandBones)];
|
||||
|
||||
// Provides a bitmask indicating if each finger is "pinched" or not. Indexable via bitshifting with the ovrpHandFinger
|
||||
// enum i.e. (1 << ovrpHandFinger_Index)
|
||||
unsigned int Pinches;
|
||||
|
||||
// Provides a 0.0f to 1.0f value of how "pinched" each finger is. Indexable via the ovrpHandFinger enum.
|
||||
float PinchStrength[static_cast<int32>(EHandFinger::Count)];
|
||||
|
||||
// World space position and translation of the pointer attached to the hand.
|
||||
XrPosef PointerPose;
|
||||
|
||||
float HandScale;
|
||||
|
||||
// Tracking confidence. Range [0,1], 0.0 = lowest confidence, 1.0 = highest confidence.
|
||||
// This is useful for smoothly de-emphasizing hands as confidence decreases.
|
||||
// This is the amount of confidence that the system has that the entire hand pose is correct.
|
||||
EOculusXRTrackingConfidence HandConfidence;
|
||||
|
||||
// Per-finger tracking confidence. Range [0,1], 0.0 = lowest confidence, 1.0 = highest confidence.
|
||||
// This is the amount of confidence the system has that the individual finger poses are correct.
|
||||
EOculusXRTrackingConfidence FingerConfidences[static_cast<int32>(EHandFinger::Count)];
|
||||
|
||||
// Time stamp for the pose that was requested in global system time.
|
||||
double RequestedTimeStamp;
|
||||
|
||||
// Time stamp of the captured sample that the pose was extrapolated from.
|
||||
double SampleTimeStamp;
|
||||
|
||||
bool PosesGeneratedByControllerData;
|
||||
};
|
||||
|
||||
struct FInternalHandsState
|
||||
{
|
||||
FInternalHandState HandState[MaxHandCount];
|
||||
XrTime PredictedDisplayTime;
|
||||
bool bHandMenuButtonPressed = false;
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRInputHapticsExtensionPlugin.h"
|
||||
|
||||
#include "IOpenXRHMDModule.h"
|
||||
#include "OculusXRInputExtensionPlugin.h"
|
||||
#include "OculusXRInputXRFunctions.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
XrInstance FInputHapticsExtensionPlugin::GetOpenXRInstance() const
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
XrSession FInputHapticsExtensionPlugin::GetOpenXRSession() const
|
||||
{
|
||||
return Session;
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::IsPCMExtensionAvailable() const
|
||||
{
|
||||
return bExtFBHapticsPcmAvailable;
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::IsAmplitudeEnvelopeExtensionAvailable() const
|
||||
{
|
||||
return bExtFBAmplitudeEnvelopeAvailable;
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::IsTouchControllerProExtensionAvailable() const
|
||||
{
|
||||
return bExtFBTouchControllerProAvailable;
|
||||
}
|
||||
|
||||
XrAction FInputHapticsExtensionPlugin::GetXrHandHapticVibrationAction() const
|
||||
{
|
||||
return XrHandHapticVibrationAction;
|
||||
}
|
||||
|
||||
XrAction FInputHapticsExtensionPlugin::GetXrThumbHapticVibrationAction() const
|
||||
{
|
||||
return XrThumbHapticVibrationAction;
|
||||
}
|
||||
|
||||
XrAction FInputHapticsExtensionPlugin::GetXrIndexHapticVibrationAction() const
|
||||
{
|
||||
return XrIndexHapticVibrationAction;
|
||||
}
|
||||
|
||||
XrPath* FInputHapticsExtensionPlugin::GetXrHandsSubactionPaths()
|
||||
{
|
||||
return XrPathBothHands;
|
||||
}
|
||||
|
||||
XrPath* FInputHapticsExtensionPlugin::GetXrHandsHapticsSubactionPaths()
|
||||
{
|
||||
return XrPathBothHandsHaptics;
|
||||
}
|
||||
|
||||
XrPath* FInputHapticsExtensionPlugin::GetXrThumbsHapticsSubactionPaths()
|
||||
{
|
||||
return XrPathBothThumbsHaptics;
|
||||
}
|
||||
|
||||
XrPath* FInputHapticsExtensionPlugin::GetXrIndexesHapticsSubactionPaths()
|
||||
{
|
||||
return XrPathBothIndexesHaptics;
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_FB_HAPTIC_PCM_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FInputHapticsExtensionPlugin::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
|
||||
{
|
||||
bExtFBHapticsPcmAvailable = InModule->IsExtensionEnabled(XR_FB_HAPTIC_PCM_EXTENSION_NAME);
|
||||
bExtFBAmplitudeEnvelopeAvailable = InModule->IsExtensionEnabled(XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME);
|
||||
bExtFBTouchControllerProAvailable = InModule->IsExtensionEnabled(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME);
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
||||
{
|
||||
Instance = InInstance;
|
||||
InitOpenXRFunctions(Instance);
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::PostCreateSession(XrSession InSession)
|
||||
{
|
||||
Session = InSession;
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::CreateHapticActions()
|
||||
{
|
||||
// Create action set
|
||||
HapticsActionSet = XR_NULL_HANDLE;
|
||||
XrActionSetCreateInfo ActionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||
ActionSetInfo.next = nullptr;
|
||||
FCStringAnsi::Strcpy(ActionSetInfo.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE, "oculushapticsactionset");
|
||||
FCStringAnsi::Strcpy(ActionSetInfo.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE, "OculusHapticsActionSet");
|
||||
XR_ENSURE(xrCreateActionSet(Instance, &ActionSetInfo, &HapticsActionSet));
|
||||
|
||||
// Create hand haptics paths
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left", &XrPathLeftHand));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/haptic", &XrPathLeftHandHaptics));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right", &XrPathRightHand));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/haptic", &XrPathRightHandHaptics));
|
||||
XrPathBothHands[0] = XrPathLeftHand;
|
||||
XrPathBothHands[1] = XrPathRightHand;
|
||||
XrPathBothHandsHaptics[0] = XrPathLeftHandHaptics;
|
||||
XrPathBothHandsHaptics[1] = XrPathRightHandHaptics;
|
||||
|
||||
// Create localized haptics paths
|
||||
XR_ENSURE(xrStringToPath(Instance, "/interaction_profiles/facebook/touch_controller_pro", &XrQuestProInteractionProfile));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/thumb_haptic_fb", &XrPathLeftThumbHaptics));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/left/output/trigger_haptic_fb", &XrPathLeftIndexHaptics));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/thumb_haptic_fb", &XrPathRightThumbHaptics));
|
||||
XR_ENSURE(xrStringToPath(Instance, "/user/hand/right/output/trigger_haptic_fb", &XrPathRightIndexHaptics));
|
||||
|
||||
XrPathBothThumbsHaptics[0] = XrPathLeftThumbHaptics;
|
||||
XrPathBothThumbsHaptics[1] = XrPathRightThumbHaptics;
|
||||
XrPathBothIndexesHaptics[0] = XrPathLeftIndexHaptics;
|
||||
XrPathBothIndexesHaptics[1] = XrPathRightIndexHaptics;
|
||||
|
||||
// Create actions
|
||||
const auto CreateVibrationOutputAction = [this](const char* ActionName) {
|
||||
XrActionCreateInfo ActionCreateInfo{ XR_TYPE_ACTION_CREATE_INFO };
|
||||
ActionCreateInfo.next = nullptr;
|
||||
ActionCreateInfo.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
|
||||
|
||||
FCStringAnsi::Strcpy(ActionCreateInfo.actionName, XR_MAX_ACTION_NAME_SIZE, ActionName);
|
||||
FCStringAnsi::Strcpy(ActionCreateInfo.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE, ActionName);
|
||||
|
||||
ActionCreateInfo.countSubactionPaths = sizeof(XrPathBothHands) / sizeof(XrPath);
|
||||
ActionCreateInfo.subactionPaths = XrPathBothHands;
|
||||
XrAction Action = XR_NULL_HANDLE;
|
||||
XR_ENSURE(xrCreateAction(HapticsActionSet, &ActionCreateInfo, &Action));
|
||||
return Action;
|
||||
};
|
||||
|
||||
XrHandHapticVibrationAction = CreateVibrationOutputAction("hand_haptic_vibration");
|
||||
XrThumbHapticVibrationAction = CreateVibrationOutputAction("hand_thumb_haptic_vibration");
|
||||
XrIndexHapticVibrationAction = CreateVibrationOutputAction("hand_index_haptic_vibration");
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::GetSuggestedBindings(XrPath InInteractionProfile, TArray<XrActionSuggestedBinding>& OutBindings)
|
||||
{
|
||||
if (HapticsActionSet == XR_NULL_HANDLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutBindings.Add({ XrHandHapticVibrationAction, XrPathLeftHandHaptics });
|
||||
OutBindings.Add({ XrHandHapticVibrationAction, XrPathRightHandHaptics });
|
||||
if (InInteractionProfile == XrQuestProInteractionProfile)
|
||||
{
|
||||
OutBindings.Add({ XrIndexHapticVibrationAction, XrPathLeftIndexHaptics });
|
||||
OutBindings.Add({ XrIndexHapticVibrationAction, XrPathRightIndexHaptics });
|
||||
OutBindings.Add({ XrThumbHapticVibrationAction, XrPathLeftThumbHaptics });
|
||||
OutBindings.Add({ XrThumbHapticVibrationAction, XrPathRightThumbHaptics });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::AttachActionSets(TSet<XrActionSet>& OutActionSets)
|
||||
{
|
||||
if (HapticsActionSet != XR_NULL_PATH)
|
||||
{
|
||||
OutActionSets.Add(HapticsActionSet);
|
||||
}
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::GetActiveActionSetsForSync(
|
||||
TArray<XrActiveActionSet>& OutActiveSets)
|
||||
{
|
||||
if (HapticsActionSet != XR_NULL_PATH)
|
||||
{
|
||||
OutActiveSets.Add({ HapticsActionSet, XR_NULL_PATH });
|
||||
}
|
||||
}
|
||||
|
||||
bool FInputHapticsExtensionPlugin::GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath,
|
||||
bool& OutHasHaptics)
|
||||
{
|
||||
// Called at the start of Epic's input action creation
|
||||
if (HapticsActionSet != XR_NULL_HANDLE)
|
||||
{
|
||||
DestroyHapticActions();
|
||||
}
|
||||
CreateHapticActions();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FInputHapticsExtensionPlugin::DestroyHapticActions()
|
||||
{
|
||||
if (HapticsActionSet != XR_NULL_HANDLE)
|
||||
{
|
||||
xrDestroyActionSet(HapticsActionSet);
|
||||
XrHandHapticVibrationAction = XR_NULL_HANDLE;
|
||||
XrThumbHapticVibrationAction = XR_NULL_HANDLE;
|
||||
XrIndexHapticVibrationAction = XR_NULL_HANDLE;
|
||||
HapticsActionSet = XR_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FInputHapticsExtensionPlugin : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
void RegisterOpenXRExtensionPlugin()
|
||||
{
|
||||
#if defined(WITH_OCULUS_BRANCH)
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
#endif
|
||||
}
|
||||
|
||||
XrInstance GetOpenXRInstance() const;
|
||||
XrSession GetOpenXRSession() const;
|
||||
|
||||
bool IsPCMExtensionAvailable() const;
|
||||
bool IsAmplitudeEnvelopeExtensionAvailable() const;
|
||||
bool IsTouchControllerProExtensionAvailable() const;
|
||||
|
||||
XrAction GetXrHandHapticVibrationAction() const;
|
||||
XrAction GetXrThumbHapticVibrationAction() const;
|
||||
XrAction GetXrIndexHapticVibrationAction() const;
|
||||
|
||||
XrPath* GetXrHandsSubactionPaths();
|
||||
XrPath* GetXrHandsHapticsSubactionPaths();
|
||||
XrPath* GetXrThumbsHapticsSubactionPaths();
|
||||
XrPath* GetXrIndexesHapticsSubactionPaths();
|
||||
|
||||
// IOpenXRExtensionPlugin
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
|
||||
virtual void PostCreateInstance(XrInstance InInstance) override;
|
||||
virtual void PostCreateSession(XrSession InSession) override;
|
||||
virtual bool GetSuggestedBindings(XrPath InInteractionProfile, TArray<XrActionSuggestedBinding>& OutBindings) override;
|
||||
virtual void AttachActionSets(TSet<XrActionSet>& OutActionSets) override;
|
||||
virtual void GetActiveActionSetsForSync(TArray<XrActiveActionSet>& OutActiveSets) override;
|
||||
virtual bool GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath, bool& OutHasHaptics) override;
|
||||
|
||||
private:
|
||||
void CreateHapticActions();
|
||||
void DestroyHapticActions();
|
||||
|
||||
bool bExtFBHapticsPcmAvailable = false;
|
||||
bool bExtFBAmplitudeEnvelopeAvailable = false;
|
||||
bool bExtFBTouchControllerProAvailable = false;
|
||||
|
||||
XrInstance Instance = XR_NULL_HANDLE;
|
||||
XrSession Session = XR_NULL_HANDLE;
|
||||
|
||||
XrActionSet HapticsActionSet = XR_NULL_HANDLE;
|
||||
|
||||
XrAction XrHandHapticVibrationAction = XR_NULL_HANDLE;
|
||||
|
||||
XrPath XrPathLeftHand = XR_NULL_PATH;
|
||||
XrPath XrPathLeftHandHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathRightHand = XR_NULL_PATH;
|
||||
XrPath XrPathRightHandHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathBothHands[2] = { XR_NULL_PATH, XR_NULL_PATH };
|
||||
XrPath XrPathBothHandsHaptics[2] = { XR_NULL_PATH, XR_NULL_PATH };
|
||||
|
||||
// Used for localized haptics
|
||||
XrAction XrThumbHapticVibrationAction = XR_NULL_HANDLE;
|
||||
XrAction XrIndexHapticVibrationAction = XR_NULL_HANDLE;
|
||||
|
||||
XrPath XrQuestProInteractionProfile = XR_NULL_PATH;
|
||||
XrPath XrPathRightIndexHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathRightThumbHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathLeftThumbHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathLeftIndexHaptics = XR_NULL_PATH;
|
||||
XrPath XrPathBothThumbsHaptics[2] = { XR_NULL_PATH, XR_NULL_PATH };
|
||||
XrPath XrPathBothIndexesHaptics[2] = { XR_NULL_PATH, XR_NULL_PATH };
|
||||
};
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,105 @@
|
||||
// @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
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
OculusXRInput::FInputExtensionPlugin* FOculusXRInputModule::GetInputOpenXRExtension() const
|
||||
{
|
||||
return InputExtensionPlugin.Get();
|
||||
}
|
||||
|
||||
OculusXRInput::FHandTrackingExtensionPlugin* FOculusXRInputModule::GetHandTrackingOpenXRExtension() const
|
||||
{
|
||||
return HandTrackingExtensionPlugin.Get();
|
||||
}
|
||||
|
||||
OculusXRInput::FInputHapticsExtensionPlugin* FOculusXRInputModule::GetHapticsOpenXRExtension() const
|
||||
{
|
||||
return HapticsExtensionPlugin.Get();
|
||||
}
|
||||
|
||||
void FOculusXRInputModule::StartupModule()
|
||||
{
|
||||
IInputDeviceModule::StartupModule();
|
||||
OculusXRInput::FOculusXRInput::PreInit();
|
||||
|
||||
HapticsExtensionPlugin = MakeShareable(new OculusXRInput::FInputHapticsExtensionPlugin());
|
||||
HapticsExtensionPlugin->RegisterOpenXRExtensionPlugin();
|
||||
HandTrackingExtensionPlugin = MakeShareable(new OculusXRInput::FHandTrackingExtensionPlugin());
|
||||
HandTrackingExtensionPlugin->RegisterOpenXRExtensionPlugin();
|
||||
InputExtensionPlugin = MakeShareable(new OculusXRInput::FInputExtensionPlugin());
|
||||
InputExtensionPlugin->RegisterOpenXRExtensionPlugin();
|
||||
TouchProInputExtensionPlugin = MakeShareable(new OculusXRInput::FTouchProInputExtensionPlugin());
|
||||
TouchProInputExtensionPlugin->RegisterOpenXRExtensionPlugin();
|
||||
TouchPlusInputExtensionPlugin = MakeShareable(new OculusXRInput::FTouchPlusInputExtensionPlugin());
|
||||
TouchPlusInputExtensionPlugin->RegisterOpenXRExtensionPlugin();
|
||||
}
|
||||
|
||||
void FOculusXRInputModule::ShutdownModule()
|
||||
{
|
||||
OculusXRInput::FOculusXRInput::ShutdownXRFunctionLibrary();
|
||||
}
|
||||
|
||||
TSharedPtr<class IInputDevice> FOculusXRInputModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
|
||||
{
|
||||
InputExtensionPlugin->SetMessageHandler(InMessageHandler);
|
||||
HandTrackingExtensionPlugin->SetMessageHandler(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
|
||||
@@ -0,0 +1,76 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "IInputDevice.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "OculusXRInputExtensionPlugin.h"
|
||||
#include "OculusXRInputHapticsExtensionPlugin.h"
|
||||
#include "OculusXRInputHandTrackingExtensionPlugin.h"
|
||||
#include "OculusXRTouchPlusInputExtensionPlugin.h"
|
||||
#include "OculusXRTouchProInputExtensionPlugin.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRInput"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInputModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusXRInput;
|
||||
}
|
||||
|
||||
class FOculusXRInputModule : public IOculusXRInputModule
|
||||
{
|
||||
public:
|
||||
OculusXRInput::FInputExtensionPlugin* GetInputOpenXRExtension() const;
|
||||
OculusXRInput::FHandTrackingExtensionPlugin* GetHandTrackingOpenXRExtension() const;
|
||||
OculusXRInput::FInputHapticsExtensionPlugin* GetHapticsOpenXRExtension() const;
|
||||
|
||||
TWeakPtr<OculusXRInput::FOculusXRInput> OculusXRInputDevice;
|
||||
|
||||
// IInputDeviceModule overrides
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() 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;
|
||||
|
||||
private:
|
||||
typedef TSharedPtr<OculusXRInput::FInputHapticsExtensionPlugin, ESPMode::ThreadSafe> FHapticsExtensionPluginPtr;
|
||||
FHapticsExtensionPluginPtr HapticsExtensionPlugin;
|
||||
typedef TSharedPtr<OculusXRInput::FHandTrackingExtensionPlugin, ESPMode::ThreadSafe> FHandTrackingExtensionPluginPtr;
|
||||
FHandTrackingExtensionPluginPtr HandTrackingExtensionPlugin;
|
||||
typedef TSharedPtr<OculusXRInput::FInputExtensionPlugin, ESPMode::ThreadSafe> FInputExtensionPluginPtr;
|
||||
FInputExtensionPluginPtr InputExtensionPlugin;
|
||||
typedef TSharedPtr<OculusXRInput::FTouchProInputExtensionPlugin, ESPMode::ThreadSafe> FTouchProInputExtensionPluginPtr;
|
||||
FTouchProInputExtensionPluginPtr TouchProInputExtensionPlugin;
|
||||
typedef TSharedPtr<OculusXRInput::FTouchPlusInputExtensionPlugin, ESPMode::ThreadSafe> FTouchPlusInputExtensionPluginPtr;
|
||||
FTouchPlusInputExtensionPluginPtr TouchPlusInputExtensionPlugin;
|
||||
};
|
||||
|
||||
#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
|
||||
125
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInputOVR.cpp
Normal file
125
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInputOVR.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRInputOVR.h"
|
||||
|
||||
#include "Haptics/HapticFeedbackEffect_Base.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
FOculusXRInput* GetOculusXRInput()
|
||||
{
|
||||
const TSharedPtr<FOculusXRInput> OculusXRInput = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (!OculusXRInput)
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("Failed getting Oculus XR input."));
|
||||
return nullptr;
|
||||
}
|
||||
return OculusXRInput.Get();
|
||||
}
|
||||
|
||||
float FOculusXRInputOVR::GetControllerSampleRateHz(EControllerHand Hand) const
|
||||
{
|
||||
float SampleRateHz = 0.f;
|
||||
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
|
||||
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetControllerSampleRateHz(OvrpController, &SampleRateHz)))
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("GetControllerSampleRateHz failed."));
|
||||
}
|
||||
return SampleRateHz;
|
||||
}
|
||||
|
||||
int FOculusXRInputOVR::GetMaxHapticDuration(EControllerHand Hand) const
|
||||
{
|
||||
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
|
||||
if (!GetOculusXRInput()->GetOvrpHapticsDesc((int32)Hand))
|
||||
return 0;
|
||||
|
||||
return GetOculusXRInput()->OvrpHapticsDesc.MaximumBufferSamplesCount / GetOculusXRInput()->OvrpHapticsDesc.SampleRateHz;
|
||||
}
|
||||
|
||||
void FOculusXRInputOVR::PlayHapticEffect(
|
||||
UHapticFeedbackEffect_Base* HapticEffect, EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location, bool bAppend, float Scale,
|
||||
bool bLoop)
|
||||
{
|
||||
if (HapticEffect)
|
||||
{
|
||||
switch (Hand)
|
||||
{
|
||||
case EControllerHand::Left:
|
||||
GetOculusXRInput()->ActiveHapticEffect_Left.Reset();
|
||||
GetOculusXRInput()->HapticsDesc_Left.Reset();
|
||||
GetOculusXRInput()->ActiveHapticEffect_Left = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
|
||||
GetOculusXRInput()->HapticsDesc_Left = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
|
||||
break;
|
||||
case EControllerHand::Right:
|
||||
GetOculusXRInput()->ActiveHapticEffect_Right.Reset();
|
||||
GetOculusXRInput()->HapticsDesc_Right.Reset();
|
||||
GetOculusXRInput()->ActiveHapticEffect_Right = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
|
||||
GetOculusXRInput()->HapticsDesc_Right = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Invalid hand specified (%d) for haptic feedback effect %s"), (int32)Hand, *HapticEffect->GetName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRInputOVR::PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand,
|
||||
int SamplesCount, void* Samples,
|
||||
int InSampleRate)
|
||||
{
|
||||
int TimeToSend = GetMaxHapticDuration(Hand);
|
||||
if (TimeToSend == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
|
||||
int SampleRate = (InSampleRate > 0 ? InSampleRate : GetOculusXRInput()->OvrpHapticsDesc.SampleRateHz);
|
||||
int MaxSamplesCount = TimeToSend * SampleRate;
|
||||
if (SamplesCount > MaxSamplesCount || SamplesCount < GetOculusXRInput()->OvrpHapticsDesc.MinimumBufferSamplesCount)
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("Sample count should be between %d and %d which last %d time."),
|
||||
GetOculusXRInput()->OvrpHapticsDesc.MinimumBufferSamplesCount, MaxSamplesCount, TimeToSend);
|
||||
}
|
||||
int WantToSend = FMath::Min(SamplesCount, MaxSamplesCount);
|
||||
WantToSend = FMath::Max(WantToSend, GetOculusXRInput()->OvrpHapticsDesc.MinimumBufferSamplesCount);
|
||||
|
||||
float* BufferToSend = (float*)FMemory::Malloc(WantToSend * sizeof(*BufferToSend));
|
||||
for (int i = 0; i < WantToSend; i++)
|
||||
{
|
||||
float Amplitude = ((uint8_t*)Samples)[i] / 255.0f;
|
||||
Amplitude = FMath::Min(1.0f, Amplitude);
|
||||
Amplitude = FMath::Max(0.0f, Amplitude);
|
||||
BufferToSend[i] = Amplitude;
|
||||
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("amplitude, %.3f"), Amplitude);
|
||||
}
|
||||
|
||||
ovrpHapticsAmplitudeEnvelopeVibration HapticsVibration;
|
||||
HapticsVibration.Duration = WantToSend / SampleRate;
|
||||
HapticsVibration.AmplitudeCount = WantToSend;
|
||||
HapticsVibration.Amplitudes = BufferToSend;
|
||||
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerHapticsAmplitudeEnvelope(
|
||||
OvrpController,
|
||||
HapticsVibration);
|
||||
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("HAEHaptics is finished: AmplitudeCount: %d, SampleRate: %d"),
|
||||
HapticsVibration.AmplitudeCount,
|
||||
SampleRate);
|
||||
|
||||
if (BufferToSend)
|
||||
{
|
||||
FMemory::Free(BufferToSend);
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRInputOVR::SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
const ovrpController OvrpController = (EControllerHand(Hand) == EControllerHand::Left) ? ovrpController_LTouch : ovrpController_RTouch;
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerLocalizedVibration(OvrpController, GetOculusXRInput()->GetOVRPHapticsLocation(Location), Frequency, Amplitude);
|
||||
UE_CLOG(OVR_HAP_LOGGING, LogOcInput, Log, TEXT("LocalizedVibration is finished: Location: %d, Frequency: %f, Amplitude: %f"), (int)(Location), Frequency, Amplitude);
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRInput.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusXRInputOVR : public IOculusXRInputBase
|
||||
{
|
||||
public:
|
||||
virtual void PlayHapticEffect(UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bAppend = false,
|
||||
float Scale = 1.f,
|
||||
bool bLoop = false) override;
|
||||
virtual void PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1) override;
|
||||
virtual void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand) override;
|
||||
|
||||
virtual float GetControllerSampleRateHz(EControllerHand Hand) const override;
|
||||
virtual int GetMaxHapticDuration(EControllerHand Hand) const override;
|
||||
};
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,356 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRInputOpenXR.h"
|
||||
#include "Haptics/HapticFeedbackEffect_Base.h"
|
||||
#include "OculusXRInputExtensionPlugin.h"
|
||||
#include "OculusXRInputModule.h"
|
||||
#include "OculusXRInputXRFunctions.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
FOculusXRInputModule* GetInputModule()
|
||||
{
|
||||
FOculusXRInputModule* InputModule = static_cast<FOculusXRInputModule*>(&FOculusXRInputModule::Get());
|
||||
if (!InputModule)
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("Failed getting Oculus XR input module."));
|
||||
return nullptr;
|
||||
}
|
||||
return InputModule;
|
||||
}
|
||||
|
||||
bool InstanceAndSessionAreValid(
|
||||
const FOculusXRInputModule* InputModule)
|
||||
{
|
||||
if (!InputModule->GetHapticsOpenXRExtension()->GetOpenXRInstance() || !InputModule->GetHapticsOpenXRExtension()->GetOpenXRSession())
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("Failed getting OpenXR instance or session."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ControllerHandToHandIndex(EControllerHand Hand)
|
||||
{
|
||||
int HandIndex = -1;
|
||||
switch (Hand)
|
||||
{
|
||||
case EControllerHand::Left:
|
||||
HandIndex = 0;
|
||||
break;
|
||||
case EControllerHand::Right:
|
||||
HandIndex = 1;
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogOcInput, Error, TEXT("No action defined for %s."), *UEnum::GetValueAsString(Hand));
|
||||
}
|
||||
return HandIndex;
|
||||
}
|
||||
|
||||
XrAction LocationToXrAction(EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = GetInputModule();
|
||||
|
||||
if (Location != EOculusXRHandHapticsLocation::Hand && !InputModule->GetHapticsOpenXRExtension()->IsTouchControllerProExtensionAvailable())
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Touch Controller Pro extension is not available."));
|
||||
return XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
switch (Location)
|
||||
{
|
||||
case EOculusXRHandHapticsLocation::Hand:
|
||||
return InputModule->GetHapticsOpenXRExtension()->GetXrHandHapticVibrationAction();
|
||||
case EOculusXRHandHapticsLocation::Thumb:
|
||||
return InputModule->GetHapticsOpenXRExtension()->GetXrThumbHapticVibrationAction();
|
||||
case EOculusXRHandHapticsLocation::Index:
|
||||
return InputModule->GetHapticsOpenXRExtension()->GetXrIndexHapticVibrationAction();
|
||||
default:
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Invalid location specified (%d)"), (int32)Location);
|
||||
}
|
||||
return XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
float FOculusXRInputOpenXR::GetControllerSampleRateHz(EControllerHand Hand) const
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = GetInputModule();
|
||||
if (!InputModule || !InstanceAndSessionAreValid(InputModule))
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
if (!InputModule->GetHapticsOpenXRExtension()->IsPCMExtensionAvailable())
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("PCM extension is not available."));
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
const int HandIndex = ControllerHandToHandIndex(Hand);
|
||||
if (HandIndex == -1)
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
XrHapticActionInfo HapticActionInfo = { XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
HapticActionInfo.action = InputModule->GetHapticsOpenXRExtension()->GetXrHandHapticVibrationAction();
|
||||
HapticActionInfo.subactionPath = InputModule->GetHapticsOpenXRExtension()->GetXrHandsSubactionPaths()[HandIndex];
|
||||
HapticActionInfo.next = nullptr;
|
||||
|
||||
XrDevicePcmSampleRateGetInfoFB DeviceSampleRate = { XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB };
|
||||
|
||||
const XrResult result = xrGetDeviceSampleRateFB(InputModule->GetHapticsOpenXRExtension()->GetOpenXRSession(), &HapticActionInfo, &DeviceSampleRate);
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("xrGetDeviceSampleRateFB failed."));
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
return DeviceSampleRate.sampleRate;
|
||||
}
|
||||
|
||||
int FOculusXRInputOpenXR::GetMaxHapticDuration(EControllerHand Hand) const
|
||||
{
|
||||
const float SampleRate = GetControllerSampleRateHz(Hand);
|
||||
if (SampleRate == 0.f)
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Sample rate equals 0"));
|
||||
return 0;
|
||||
}
|
||||
return XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB / SampleRate;
|
||||
}
|
||||
|
||||
void FOculusXRInputOpenXR::Tick(float DeltaTime)
|
||||
{
|
||||
if (ActiveHapticEffect_Left.IsValid())
|
||||
{
|
||||
FHapticFeedbackValues LeftHaptics;
|
||||
const bool bPlaying = ActiveHapticEffect_Left->Update(DeltaTime, LeftHaptics);
|
||||
if (!bPlaying)
|
||||
{
|
||||
ActiveHapticEffect_Left->bLoop ? HapticsDesc_Left->Restart() : HapticsDesc_Left.Reset();
|
||||
ActiveHapticEffect_Left->bLoop ? ActiveHapticEffect_Left->Restart() : ActiveHapticEffect_Left.Reset();
|
||||
}
|
||||
|
||||
SetHapticFeedbackValues(EControllerHand::Left, LeftHaptics, HapticsDesc_Left.Get());
|
||||
}
|
||||
|
||||
if (ActiveHapticEffect_Right.IsValid())
|
||||
{
|
||||
FHapticFeedbackValues RightHaptics;
|
||||
const bool bPlaying = ActiveHapticEffect_Right->Update(DeltaTime, RightHaptics);
|
||||
if (!bPlaying)
|
||||
{
|
||||
ActiveHapticEffect_Right->bLoop ? HapticsDesc_Right->Restart() : HapticsDesc_Right.Reset();
|
||||
ActiveHapticEffect_Right->bLoop ? ActiveHapticEffect_Right->Restart() : ActiveHapticEffect_Right.Reset();
|
||||
}
|
||||
|
||||
SetHapticFeedbackValues(EControllerHand::Right, RightHaptics, HapticsDesc_Right.Get());
|
||||
}
|
||||
}
|
||||
|
||||
// Tick will only get called if the object is created in FOculusXRInput::GetOculusXRInputBaseImpl(), so we do not need ETickableTickType::Conditional
|
||||
ETickableTickType FOculusXRInputOpenXR::GetTickableTickType() const
|
||||
{
|
||||
return ETickableTickType::Always;
|
||||
}
|
||||
|
||||
TStatId FOculusXRInputOpenXR::GetStatId() const
|
||||
{
|
||||
RETURN_QUICK_DECLARE_CYCLE_STAT(FOculusXRInputOpenXR, STATGROUP_Tickables);
|
||||
}
|
||||
|
||||
void FOculusXRInputOpenXR::SetHapticFeedbackValues(EControllerHand Hand, const FHapticFeedbackValues& Values, FOculusXRHapticsDesc* HapticsDesc)
|
||||
{
|
||||
FHapticFeedbackBuffer* const HapticBuffer = Values.HapticBuffer;
|
||||
const bool bHasBuffer = HapticBuffer && HapticBuffer->BufferLength > 0;
|
||||
|
||||
// UHapticFeedbackEffect_SoundWave
|
||||
if (bHasBuffer)
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = GetInputModule();
|
||||
if (!InputModule || !InstanceAndSessionAreValid(InputModule))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!InputModule->GetHapticsOpenXRExtension()->IsPCMExtensionAvailable())
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("PCM extension is not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
int SamplesToSend = 0.036f * HapticBuffer->SamplingRate; // Related to the duration that each PCM haptic batch lasts (36ms)
|
||||
if (SamplesToSend == 0 || HapticBuffer->SamplesSent == HapticBuffer->BufferLength)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Makes sure we are not overloading it
|
||||
SamplesToSend = FMath::Min(SamplesToSend, XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB);
|
||||
SamplesToSend = FMath::Min(SamplesToSend, (HapticBuffer->BufferLength - HapticBuffer->SamplesSent) / 2);
|
||||
|
||||
uint32_t SamplesConsumed = 0;
|
||||
std::vector<float> PCMBuffer(SamplesToSend);
|
||||
for (int i = 0; i < SamplesToSend; i++)
|
||||
{
|
||||
const uint32 DataIndex = HapticBuffer->CurrentPtr + (i * 2);
|
||||
const int16* const RawData = reinterpret_cast<const int16*>(&HapticBuffer->RawData[DataIndex]);
|
||||
float SampleValue = (*RawData * HapticBuffer->ScaleFactor) / INT16_MAX;
|
||||
SampleValue = FMath::Min(1.0f, SampleValue);
|
||||
SampleValue = FMath::Max(-1.0f, SampleValue);
|
||||
PCMBuffer[i] = SampleValue;
|
||||
}
|
||||
|
||||
XrHapticActionInfo HapticActionInfo = { XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
HapticActionInfo.action = LocationToXrAction(HapticsDesc->Location);
|
||||
HapticActionInfo.subactionPath = InputModule->GetHapticsOpenXRExtension()->GetXrHandsSubactionPaths()[ControllerHandToHandIndex(Hand)];
|
||||
|
||||
XrHapticPcmVibrationFB HapticPcmVibration = { XR_TYPE_HAPTIC_PCM_VIBRATION_FB };
|
||||
HapticPcmVibration.buffer = PCMBuffer.data();
|
||||
HapticPcmVibration.bufferSize = SamplesToSend;
|
||||
HapticPcmVibration.sampleRate = HapticBuffer->SamplingRate;
|
||||
HapticPcmVibration.samplesConsumed = &SamplesConsumed;
|
||||
HapticPcmVibration.append = HapticsDesc->bIsFirstCall ? HapticsDesc->bAppend : true;
|
||||
|
||||
const XrResult result = xrApplyHapticFeedback(InputModule->GetHapticsOpenXRExtension()->GetOpenXRSession(), &HapticActionInfo, reinterpret_cast<XrHapticBaseHeader*>(&HapticPcmVibration));
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("xrApplyHapticFeedback failed for PCM haptics with result %s"), OpenXRResultToString(result));
|
||||
}
|
||||
|
||||
HapticsDesc->bIsFirstCall = false;
|
||||
HapticBuffer->CurrentPtr = FMath::Min(HapticBuffer->CurrentPtr + SamplesConsumed * 2, static_cast<uint32>(HapticBuffer->BufferLength));
|
||||
HapticBuffer->SamplesSent = FMath::Min(HapticBuffer->SamplesSent + SamplesConsumed * 2, static_cast<uint32>(HapticBuffer->BufferLength));
|
||||
}
|
||||
// UHapticFeedbackEffect_Curve and UHapticFeedbackEffect_Buffer
|
||||
else
|
||||
{
|
||||
SetHapticsByValue(Values.Frequency, Values.Amplitude, Hand, HapticsDesc ? HapticsDesc->Location : EOculusXRHandHapticsLocation::Hand);
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRInputOpenXR::PlayHapticEffect(
|
||||
UHapticFeedbackEffect_Base* HapticEffect, EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location, bool bAppend, float Scale,
|
||||
bool bLoop)
|
||||
{
|
||||
if (!HapticEffect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (Hand)
|
||||
{
|
||||
case EControllerHand::Left:
|
||||
ActiveHapticEffect_Left.Reset();
|
||||
ActiveHapticEffect_Left = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
|
||||
HapticsDesc_Left.Reset();
|
||||
HapticsDesc_Left = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
|
||||
break;
|
||||
case EControllerHand::Right:
|
||||
ActiveHapticEffect_Right.Reset();
|
||||
ActiveHapticEffect_Right = MakeShareable(new FActiveHapticFeedbackEffect(HapticEffect, Scale, bLoop));
|
||||
HapticsDesc_Right.Reset();
|
||||
HapticsDesc_Right = MakeShareable(new FOculusXRHapticsDesc(Location, bAppend));
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Invalid hand specified (%d) for haptic feedback effect %s"), (int32)Hand, *HapticEffect->GetName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRInputOpenXR::PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand,
|
||||
int SamplesCount,
|
||||
void* Samples,
|
||||
int InSampleRate)
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = GetInputModule();
|
||||
if (!InputModule || !InstanceAndSessionAreValid(InputModule))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InputModule->GetHapticsOpenXRExtension()->IsAmplitudeEnvelopeExtensionAvailable())
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Amplitude Envelope extension is not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
const int MaxTimeToSend = GetMaxHapticDuration(Hand);
|
||||
if (MaxTimeToSend == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int SampleRate = InSampleRate > 0 ? InSampleRate : GetControllerSampleRateHz(Hand);
|
||||
if (SamplesCount > XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB || SamplesCount < 1)
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("Sample count should be between 1 and %d which last %d seconds."), XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB, MaxTimeToSend);
|
||||
}
|
||||
|
||||
const int AmplitudesCount = FMath::Min(SamplesCount, static_cast<int>(XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB));
|
||||
|
||||
std::vector<float> AmplitudesToSend(AmplitudesCount);
|
||||
for (int i = 0; i < AmplitudesCount; i++)
|
||||
{
|
||||
float Amplitude = static_cast<uint8_t*>(Samples)[i] / 255.0f;
|
||||
Amplitude = FMath::Min(1.0f, Amplitude);
|
||||
Amplitude = FMath::Max(0.0f, Amplitude);
|
||||
AmplitudesToSend[i] = Amplitude;
|
||||
}
|
||||
|
||||
XrHapticActionInfo HapticActionInfo = { XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
HapticActionInfo.action = InputModule->GetHapticsOpenXRExtension()->GetXrHandHapticVibrationAction();
|
||||
HapticActionInfo.subactionPath = InputModule->GetHapticsOpenXRExtension()->GetXrHandsSubactionPaths()[ControllerHandToHandIndex(Hand)];
|
||||
|
||||
XrHapticAmplitudeEnvelopeVibrationFB HapticAmplitudeEnvelopeVibration = { XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB };
|
||||
HapticAmplitudeEnvelopeVibration.duration = OculusXR::ToXrDuration(static_cast<float>(AmplitudesCount) / SampleRate);
|
||||
HapticAmplitudeEnvelopeVibration.amplitudeCount = static_cast<uint32_t>(AmplitudesCount);
|
||||
HapticAmplitudeEnvelopeVibration.amplitudes = AmplitudesToSend.data();
|
||||
|
||||
const XrResult result = xrApplyHapticFeedback(InputModule->GetHapticsOpenXRExtension()->GetOpenXRSession(), &HapticActionInfo, reinterpret_cast<XrHapticBaseHeader*>(&HapticAmplitudeEnvelopeVibration));
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("xrApplyHapticFeedback failed for amplitude envelope haptics with result %s"), OpenXRResultToString(result));
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRInputOpenXR::SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = GetInputModule();
|
||||
if (!InputModule || !InstanceAndSessionAreValid(InputModule))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int HandIndex = ControllerHandToHandIndex(Hand);
|
||||
if (HandIndex == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
XrHapticActionInfo HapticActionInfo = { XR_TYPE_HAPTIC_ACTION_INFO };
|
||||
HapticActionInfo.action = LocationToXrAction(Location);
|
||||
HapticActionInfo.subactionPath = InputModule->GetHapticsOpenXRExtension()->GetXrHandsSubactionPaths()[ControllerHandToHandIndex(Hand)];
|
||||
|
||||
XrHapticVibration Vibration = { XR_TYPE_HAPTIC_VIBRATION };
|
||||
Vibration.amplitude = Amplitude;
|
||||
Vibration.frequency = Frequency;
|
||||
Vibration.duration = 2000000000; // 2 second duration, this is to give enough
|
||||
// time for a new signal to be received without
|
||||
// stopping the previous vibration
|
||||
|
||||
const XrResult Result = xrApplyHapticFeedback(InputModule->GetHapticsOpenXRExtension()->GetOpenXRSession(), &HapticActionInfo, reinterpret_cast<XrHapticBaseHeader*>(&Vibration));
|
||||
|
||||
if (XR_FAILED(Result))
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("xrApplyHapticFeedback failed with result %s"), OpenXRResultToString(Result));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRInput.h"
|
||||
|
||||
class FOculusXRInputModule;
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusXRInputOpenXR : public IOculusXRInputBase, public FTickableGameObject
|
||||
{
|
||||
public:
|
||||
// IOculusXRInputBase overrides
|
||||
virtual void PlayHapticEffect(UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bAppend = false,
|
||||
float Scale = 1.f,
|
||||
bool bLoop = false) override;
|
||||
virtual void PlayAmplitudeEnvelopeHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1) override;
|
||||
virtual void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand) override;
|
||||
|
||||
virtual float GetControllerSampleRateHz(EControllerHand Hand) const override;
|
||||
virtual int GetMaxHapticDuration(EControllerHand Hand) const override;
|
||||
|
||||
// FTickableGameObject overrides
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual ETickableTickType GetTickableTickType() const override;
|
||||
virtual TStatId GetStatId() const override;
|
||||
|
||||
private:
|
||||
void SetHapticFeedbackValues(EControllerHand Hand, const FHapticFeedbackValues& Values, FOculusXRHapticsDesc* HapticsDesc);
|
||||
|
||||
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Left;
|
||||
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Right;
|
||||
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Left;
|
||||
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Right;
|
||||
};
|
||||
} // namespace OculusXRInput
|
||||
555
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInputState.h
Normal file
555
Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRInputState.h
Normal file
@@ -0,0 +1,555 @@
|
||||
// @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_Trigger_Proximity;
|
||||
static const FKey OculusTouch_Left_ThumbUp;
|
||||
static const FKey OculusTouch_Left_Thumb_Proximity;
|
||||
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_Trigger_Proximity;
|
||||
static const FKey OculusTouch_Right_ThumbUp;
|
||||
static const FKey OculusTouch_Right_Thumb_Proximity;
|
||||
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_Trigger_Proximity;
|
||||
static const FName OculusTouch_Left_ThumbUp;
|
||||
static const FName OculusTouch_Left_Thumb_Proximity;
|
||||
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_Trigger_Proximity;
|
||||
static const FName OculusTouch_Right_ThumbUp;
|
||||
static const FName OculusTouch_Right_Thumb_Proximity;
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
const float DefaultInitialButtonRepeatDelay = 0.2f;
|
||||
const float DefaultButtonRepeatDelay = 0.1f;
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRInputXRFunctions.h"
|
||||
#include "OculusXRInputModule.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
PFN_xrGetDeviceSampleRateFB xrGetDeviceSampleRateFB = nullptr;
|
||||
|
||||
void InitOpenXRFunctions(XrInstance InInstance)
|
||||
{
|
||||
const FOculusXRInputModule* InputModule = static_cast<FOculusXRInputModule*>(&FOculusXRInputModule::Get());
|
||||
if (!InputModule)
|
||||
{
|
||||
UE_LOG(LogOcInput, Error, TEXT("Failed getting Oculus XR input module."));
|
||||
return;
|
||||
}
|
||||
if (InputModule->GetHapticsOpenXRExtension()->IsPCMExtensionAvailable())
|
||||
{
|
||||
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetDeviceSampleRateFB", &xrGetDeviceSampleRateFB);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
extern PFN_xrGetDeviceSampleRateFB xrGetDeviceSampleRateFB;
|
||||
|
||||
void InitOpenXRFunctions(XrInstance InInstance);
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,22 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRLegacyPoseTransformComponent.h"
|
||||
#include "OpenXR/OculusXROpenXRUtilities.h"
|
||||
#include "GameFramework/WorldSettings.h"
|
||||
|
||||
UOculusXRLegacyPoseTransformComponent::UOculusXRLegacyPoseTransformComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetComponentTickEnabled(false);
|
||||
}
|
||||
|
||||
void UOculusXRLegacyPoseTransformComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (OculusXR::IsOpenXRSystem())
|
||||
{
|
||||
AddLocalTransform(FTransform(OculusPoseToGripRotation, OculusPoseToGripPosition * GetWorld()->GetWorldSettings()->WorldToMeters).Inverse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRTouchPlusInputExtensionPlugin.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
bool FTouchPlusInputExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FTouchPlusInputExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
||||
{
|
||||
XrResult Result = xrStringToPath(InInstance, "/interaction_profiles/meta/touch_controller_plus", &InteractionProfile);
|
||||
check(XR_SUCCEEDED(Result));
|
||||
}
|
||||
|
||||
bool FTouchPlusInputExtensionPlugin::GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath, bool& OutHasHaptics)
|
||||
{
|
||||
OutKeyPrefix = "OculusTouchPlus";
|
||||
OutPath = InteractionProfile;
|
||||
OutHasHaptics = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
class FTouchPlusInputExtensionPlugin : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
void RegisterOpenXRExtensionPlugin()
|
||||
{
|
||||
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
#endif
|
||||
}
|
||||
|
||||
// IOpenXRExtensionPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual void PostCreateInstance(XrInstance InInstance) override;
|
||||
virtual bool GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath, bool& OutHasHaptics) override;
|
||||
|
||||
private:
|
||||
XrPath InteractionProfile;
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRTouchProInputExtensionPlugin.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
bool FTouchProInputExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FTouchProInputExtensionPlugin::PostCreateInstance(XrInstance InInstance)
|
||||
{
|
||||
XrResult Result = xrStringToPath(InInstance, "/interaction_profiles/facebook/touch_controller_pro", &InteractionProfile);
|
||||
check(XR_SUCCEEDED(Result));
|
||||
}
|
||||
|
||||
bool FTouchProInputExtensionPlugin::GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath, bool& OutHasHaptics)
|
||||
{
|
||||
OutKeyPrefix = "OculusTouchPro";
|
||||
OutPath = InteractionProfile;
|
||||
OutHasHaptics = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "khronos/openxr/openxr.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
class FTouchProInputExtensionPlugin : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
void RegisterOpenXRExtensionPlugin()
|
||||
{
|
||||
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
RegisterOpenXRExtensionModularFeature();
|
||||
#endif
|
||||
}
|
||||
|
||||
// IOpenXRExtensionPlugin
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
virtual void PostCreateInstance(XrInstance InInstance) override;
|
||||
virtual bool GetInteractionProfile(XrInstance InInstance, FString& OutKeyPrefix, XrPath& OutPath, bool& OutHasHaptics) override;
|
||||
|
||||
private:
|
||||
XrPath InteractionProfile;
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
@@ -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 || PLATFORM_ANDROID_X64)
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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();
|
||||
|
||||
// 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.
|
||||
const FVector PositionOffsets[EOculusXRSideCount][EOculusXRControllerDrivenHandPoseTypesCount]{
|
||||
{
|
||||
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
|
||||
},
|
||||
};
|
||||
const FVector RotationOffsets[EOculusXRSideCount][EOculusXRControllerDrivenHandPoseTypesCount]{
|
||||
{
|
||||
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
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "OculusXRLegacyPoseTransformComponent.h"
|
||||
#include "OculusXRControllerLegacyPoseTransformComponent.generated.h"
|
||||
|
||||
/**
|
||||
* This class is deprecated, please use OculusXRLegacyPoseTransformComponent instead.
|
||||
*/
|
||||
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, DeprecationMessage = "Please use OculusXRLegacyPoseTransformComponent instead."), ClassGroup = OculusHand, DisplayName = "[Deprecated] OculusXR Controller Legacy Pose Transform Component")
|
||||
class OCULUSXRINPUT_API UOculusXRControllerLegacyPoseTransformComponent : public USceneComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Applies the transformation from legacy Oculus pose to OpenXR grip pose onto the parent component.
|
||||
*/
|
||||
virtual void BeginPlay() override;
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
// @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 HandRootFixupRotationOVR = FQuat(-0.5f, -0.5f, 0.5f, 0.5f);
|
||||
static const FQuat LeftHandRootFixupRotationOpenXR = FQuat({ 1.0f, 0.0f, 0.0f }, FMath::DegreesToRadians(90.0));
|
||||
static const FQuat RightHandRootFixupRotationOpenXR = FQuat({ 0.0f, 1.0f, 0.0f }, FMath::DegreesToRadians(180.0)) * FQuat({ 1.0f, 0.0f, 0.0f }, FMath::DegreesToRadians(90.0));
|
||||
|
||||
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(EOculusXRHandType HandType);
|
||||
};
|
||||
@@ -0,0 +1,365 @@
|
||||
// @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 InputLocation = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bInputAppend = false,
|
||||
bool bInputIsFirstCall = true)
|
||||
: Location(InputLocation), bAppend(bInputAppend), bIsFirstCall(bInputIsFirstCall)
|
||||
{
|
||||
}
|
||||
|
||||
void Restart()
|
||||
{
|
||||
Location = EOculusXRHandHapticsLocation::Hand;
|
||||
bAppend = false;
|
||||
bIsFirstCall = true;
|
||||
}
|
||||
EOculusXRHandHapticsLocation Location;
|
||||
bool bAppend;
|
||||
bool bIsFirstCall = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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. Note that HandTracking must be used.
|
||||
*
|
||||
* @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 frequency in Hz 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);
|
||||
|
||||
/**
|
||||
* Get if / how controller inputs are used to build a syntheic hand pose.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Controller")
|
||||
static EOculusXRControllerDrivenHandPoseTypes GetControllerDrivenHandPoses();
|
||||
|
||||
/**
|
||||
* Gets the transformation for transforming the legacy Oculus pose into the OpenXR Grip pose.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Controller")
|
||||
static FTransform GetLegacyOculusPoseTransform(float WorldToMeters);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "OculusXRLegacyPoseTransformComponent.generated.h"
|
||||
|
||||
static const FQuat OculusPoseToGripRotation = FQuat(FVector(0, 1, 0), -FMath::DegreesToRadians(double(60)));
|
||||
static const FVector OculusPoseToGripPosition = FVector(-0.04, 0, -0.03);
|
||||
|
||||
/**
|
||||
* Handles conversion of components created for the legacy Oculus controller pose into
|
||||
* the OpenXR Grip pose. Attach components that need to be transformed under this component.
|
||||
*/
|
||||
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusHand, DisplayName = "OculusXR Legacy Pose Transform Component")
|
||||
class OCULUSXRINPUT_API UOculusXRLegacyPoseTransformComponent : public USceneComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Applies the transformation from legacy Oculus pose to OpenXR grip pose onto the parent component.
|
||||
*/
|
||||
virtual void BeginPlay() override;
|
||||
};
|
||||
Reference in New Issue
Block a user