Started the project... finally...
Started the project. This right now only includes Meta XR and Android setup with VR template. More improvements to come!
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
namespace UnrealBuildTool.Rules
|
||||
{
|
||||
public class OculusXRInput : ModuleRules
|
||||
{
|
||||
public OculusXRInput(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
bUseUnity = true;
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"InputDevice", // For IInputDevice.h
|
||||
"HeadMountedDisplay", // For IMotionController.h
|
||||
"ImageWrapper"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"ApplicationCore",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"HeadMountedDisplay",
|
||||
"OculusXRHMD",
|
||||
"OculusXRMR",
|
||||
"OVRPluginXR",
|
||||
});
|
||||
|
||||
if (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 3))
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"XRBase",
|
||||
});
|
||||
}
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// Relative to Engine\Plugins\Runtime\Oculus\OculusVR\Source
|
||||
"OculusXRHMD/Private",
|
||||
});
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"Runtime/Renderer/Private",
|
||||
"Runtime/Engine/Classes/Components",
|
||||
});
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||
{
|
||||
RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/" + Target.Platform.ToString() + "/OpenXR/OVRPlugin.dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
// A class to render the currently connected controller.
|
||||
// Similar to how hands are tracked.
|
||||
|
||||
#include "OculusXRControllerComponent.h"
|
||||
#include "OculusXRInput.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "OculusXRHandTracking.h"
|
||||
#include <OculusXRInputModule.h>
|
||||
|
||||
UOculusXRControllerComponent::UOculusXRControllerComponent()
|
||||
: Super(),
|
||||
// These position and rotation offsets are needed to correctly position the controller
|
||||
// when using natural or controller based hand positioning.
|
||||
// Why do these need to be hardcoded and not come from the skeleton etc?
|
||||
// It seems like the offset comes from somewhere in unreal in the first place,
|
||||
// not from a bone position, so there's not a place to load the correct orientation from.
|
||||
PositionOffsets{
|
||||
{
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: None
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: Natural
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: Controller
|
||||
},
|
||||
{
|
||||
FVector(0, 0, 0), // Side: Left, Controller Mapping: None
|
||||
FVector(4.278, 9.969, 4.638), // Side: Left, Controller Mapping: Natural
|
||||
FVector(4.278, 9.969, 4.638), // Side: Left, Controller Mapping: Controller
|
||||
},
|
||||
{
|
||||
FVector(0, 0, 0), // Side: Right, Controller Mapping: None
|
||||
FVector(-4.104, -9.993, -4.244), // Side: Right, Controller Mapping: Natural
|
||||
FVector(-4.104, -9.993, -4.244), // Side: Right, Controller Mapping: Controller
|
||||
},
|
||||
}
|
||||
, RotationOffsets{
|
||||
{
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: None
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: Natural
|
||||
FVector(0, 0, 0), // Side: None, Controller Mapping: Controller
|
||||
},
|
||||
{
|
||||
FVector(0, 0, 0), // Side: Left, Controller Mapping: None
|
||||
FVector(90, 166.229, 263.738), // Side: Left, Controller Mapping: Natural
|
||||
FVector(90, 168.515, 259.149), // Side: Left, Controller Mapping: Controller
|
||||
},
|
||||
{
|
||||
FVector(0, 0, 0), // Side: Right, Controller Mapping: None
|
||||
FVector(90, 194.995, 83.863), // Side: Right, Controller Mapping: Natural
|
||||
FVector(90, 191.485, 79.149), // Side: Right, Controller Mapping: Controller
|
||||
},
|
||||
}
|
||||
{
|
||||
_meshLoadingState = MeshLoadingState::None;
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void UOculusXRControllerComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void UOculusXRControllerComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
// If we're in a capsense mode, we need to offset the controller position so that it's correct / consistent with the hand position.
|
||||
if (_cachedControllerHandType != OculusXRInput::FOculusHandTracking::ControllerDrivenHandType)
|
||||
{
|
||||
_cachedControllerHandType = OculusXRInput::FOculusHandTracking::ControllerDrivenHandType;
|
||||
|
||||
const FVector positionOffset = PositionOffsets[static_cast<int>(SkeletonType)][static_cast<int>(_cachedControllerHandType)];
|
||||
const FVector rotationOffset = RotationOffsets[static_cast<int>(SkeletonType)][static_cast<int>(_cachedControllerHandType)];
|
||||
|
||||
SetRelativeLocation(positionOffset);
|
||||
SetRelativeRotation(FQuat::MakeFromEuler(rotationOffset));
|
||||
}
|
||||
|
||||
bool isHandTrackingEnabled = UOculusXRInputFunctionLibrary::IsHandTrackingEnabled();
|
||||
bool shouldHide = isHandTrackingEnabled && !(RenderWhenUsingControllerDrivenHands && OculusXRInput::FOculusHandTracking::ControllerDrivenHandType == EOculusXRControllerDrivenHandPoseTypes::Controller);
|
||||
if (shouldHide && !bHiddenInGame)
|
||||
{
|
||||
SetHiddenInGame(true, false);
|
||||
}
|
||||
if (!shouldHide && bHiddenInGame)
|
||||
{
|
||||
SetHiddenInGame(false, false);
|
||||
}
|
||||
|
||||
if (_meshLoadingState == MeshLoadingState::None || _controllerType != GetControllerType())
|
||||
{
|
||||
InitializeMesh();
|
||||
}
|
||||
}
|
||||
|
||||
EOculusXRControllerType UOculusXRControllerComponent::GetControllerType()
|
||||
{
|
||||
EControllerHand controllerHand = EControllerHand::AnyHand;
|
||||
if (SkeletonType == EOculusXRSide::Left)
|
||||
{
|
||||
controllerHand = EControllerHand::Left;
|
||||
}
|
||||
else if (SkeletonType == EOculusXRSide::Right)
|
||||
{
|
||||
controllerHand = EControllerHand::Right;
|
||||
}
|
||||
return UOculusXRFunctionLibrary::GetControllerType(controllerHand);
|
||||
}
|
||||
|
||||
void UOculusXRControllerComponent::InitializeMesh()
|
||||
{
|
||||
if (_runtimeMesh != nullptr)
|
||||
{
|
||||
SetStaticMesh(nullptr);
|
||||
_streamableManager.Unload(_runtimeMeshPath);
|
||||
_runtimeMesh = nullptr;
|
||||
}
|
||||
|
||||
auto left_controller_path = TEXT("none");
|
||||
auto right_controller_path = TEXT("none");
|
||||
|
||||
_controllerType = GetControllerType();
|
||||
switch (_controllerType)
|
||||
{
|
||||
case EOculusXRControllerType::MetaQuestTouch:
|
||||
left_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/LeftTouchForQuest2.LeftTouchForQuest2'");
|
||||
right_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/RightTouchForQuest2.RightTouchForQuest2'");
|
||||
break;
|
||||
case EOculusXRControllerType::MetaQuestTouchPlus:
|
||||
// We don't currently have a model for the touch plus controller, default to the touch pro.
|
||||
case EOculusXRControllerType::MetaQuestTouchPro:
|
||||
left_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/LeftMetaQuestTouchPro.LeftMetaQuestTouchPro'");
|
||||
right_controller_path = TEXT("/Script/Engine.StaticMesh'/OculusXR/Meshes/RightMetaQuestTouchPro.RightMetaQuestTouchPro'");
|
||||
break;
|
||||
case EOculusXRControllerType::None:
|
||||
case EOculusXRControllerType::Unknown:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
auto controllerPath = left_controller_path;
|
||||
if (SkeletonType == EOculusXRSide::Right)
|
||||
{
|
||||
controllerPath = right_controller_path;
|
||||
}
|
||||
_runtimeMeshPath = FSoftObjectPath(controllerPath);
|
||||
|
||||
_loadAssetHandle = _streamableManager.RequestAsyncLoad(
|
||||
_runtimeMeshPath,
|
||||
FStreamableDelegate::CreateUObject(this, &UOculusXRControllerComponent::MeshLoaded));
|
||||
}
|
||||
|
||||
void UOculusXRControllerComponent::MeshLoaded()
|
||||
{
|
||||
if (_loadAssetHandle.IsValid() && _loadAssetHandle.Get()->HasLoadCompleted())
|
||||
{
|
||||
_runtimeMesh = reinterpret_cast<UStaticMesh*>(_loadAssetHandle.Get()->GetLoadedAsset());
|
||||
_meshLoadingState = MeshLoadingState::Loaded;
|
||||
SetStaticMesh(_runtimeMesh);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRControllerTracking.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "OculusXRInput.h"
|
||||
#include "Misc/CoreDelegates.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "Haptics/HapticFeedbackEffect_Base.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
void FOculusXRControllerTracking::PlayHapticEffect(
|
||||
class UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location,
|
||||
bool bAppend,
|
||||
float Scale,
|
||||
bool bLoop)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
OculusXRInputModule.Get()->PlayHapticEffect(HapticEffect, Hand, Location, bAppend, Scale, bLoop);
|
||||
#endif
|
||||
}
|
||||
|
||||
int FOculusXRControllerTracking::PlayHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate, bool bPCM, bool bAppend)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
return OculusXRInputModule.Get()->PlayHapticEffect(Hand, Amplitudes.Num(), Amplitudes.GetData(), SampleRate, bPCM, bAppend);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FOculusXRControllerTracking::StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
SetHapticsByValue(0.f, 0.f, Hand, Location);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FOculusXRControllerTracking::SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
OculusXRInputModule.Get()->SetHapticsByValue(Frequency, Amplitude, Hand, Location);
|
||||
#endif
|
||||
}
|
||||
|
||||
float FOculusXRControllerTracking::GetControllerSampleRateHz(EControllerHand Hand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
return OculusXRInputModule.Get()->GetControllerSampleRateHz(Hand);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FOculusXRControllerTracking::GetMaxHapticDuration(EControllerHand Hand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
return OculusXRInputModule.Get()->GetMaxHapticDuration(Hand);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,43 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRControllerTracking"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogOcXRControllerTracking, Log, All);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRControllerTracking
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class UHapticFeedbackEffect_Base;
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusXRControllerTracking
|
||||
{
|
||||
public:
|
||||
static void PlayHapticEffect(
|
||||
UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bAppend = false,
|
||||
float Scale = 1.f,
|
||||
bool bLoop = false);
|
||||
|
||||
static int PlayHapticEffect(EControllerHand Hand, TArray<uint8>& Amplitudes, int SampleRate, bool bPCM = false, bool bAppend = false);
|
||||
|
||||
static void StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
|
||||
|
||||
static void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
|
||||
|
||||
static float GetControllerSampleRateHz(EControllerHand Hand);
|
||||
|
||||
static int GetMaxHapticDuration(EControllerHand Hand);
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,221 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#include "OculusXRHandComponent.h"
|
||||
#include "OculusXRInput.h"
|
||||
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Components/InputComponent.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
|
||||
UOculusXRHandComponent::UOculusXRHandComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
PrimaryComponentTick.TickGroup = TG_PrePhysics;
|
||||
|
||||
bHasAuthority = false;
|
||||
bAutoActivate = true;
|
||||
|
||||
bWantsInitializeComponent = true;
|
||||
|
||||
for (uint8 BoneIndex = 0; BoneIndex < (uint8)EOculusXRBone::Bone_Max; BoneIndex++)
|
||||
{
|
||||
BoneNameMappings.Add((EOculusXRBone)BoneIndex, TEXT(""));
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Use custom mesh if a skeletal mesh is already set, else try to load the runtime mesh
|
||||
if (GetSkinnedAsset())
|
||||
{
|
||||
bCustomHandMesh = true;
|
||||
bSkeletalMeshInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
RuntimeSkeletalMesh = NewObject<USkeletalMesh>(this, TEXT("OculusHandMesh"));
|
||||
InitializeSkeletalMesh();
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::InitializeSkeletalMesh()
|
||||
{
|
||||
if (RuntimeSkeletalMesh)
|
||||
{
|
||||
if (UOculusXRInputFunctionLibrary::GetHandSkeletalMesh(RuntimeSkeletalMesh, SkeletonType, MeshType))
|
||||
{
|
||||
SetSkinnedAssetAndUpdate(RuntimeSkeletalMesh, true);
|
||||
if (MaterialOverride)
|
||||
{
|
||||
SetMaterial(0, MaterialOverride);
|
||||
}
|
||||
CachedBaseMaterial = GetMaterial(0);
|
||||
bSkeletalMeshInitialized = true;
|
||||
|
||||
// Initialize physics capsules on the runtime mesh
|
||||
if (bInitializePhysics)
|
||||
{
|
||||
CollisionCapsules = UOculusXRInputFunctionLibrary::InitializeHandPhysics(SkeletonType, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (!bSkeletalMeshInitialized && !bCustomHandMesh)
|
||||
{
|
||||
InitializeSkeletalMesh();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsInGameThread())
|
||||
{
|
||||
// Cache state from the game thread for use on the render thread
|
||||
const AActor* MyOwner = GetOwner();
|
||||
bHasAuthority = MyOwner->HasLocalNetOwner();
|
||||
int i = 0;
|
||||
}
|
||||
|
||||
if (bHasAuthority)
|
||||
{
|
||||
bool bHidden = false;
|
||||
if (UOculusXRInputFunctionLibrary::IsHandTrackingEnabled())
|
||||
{
|
||||
// Update Visibility based on Confidence
|
||||
if (ConfidenceBehavior == EOculusXRConfidenceBehavior::HideActor)
|
||||
{
|
||||
EOculusXRTrackingConfidence TrackingConfidence = UOculusXRInputFunctionLibrary::GetTrackingConfidence(SkeletonType);
|
||||
bHidden |= TrackingConfidence != EOculusXRTrackingConfidence::High;
|
||||
}
|
||||
|
||||
// Update Hand Scale
|
||||
if (bUpdateHandScale)
|
||||
{
|
||||
float NewScale = UOculusXRInputFunctionLibrary::GetHandScale(SkeletonType);
|
||||
SetRelativeScale3D(FVector(NewScale));
|
||||
}
|
||||
|
||||
// Update Bone Pose Rotations
|
||||
if (GetSkinnedAsset())
|
||||
{
|
||||
UpdateBonePose();
|
||||
}
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
// Check for system gesture pressed through player controller
|
||||
if (APawn* Pawn = Cast<APawn>(GetOwner()))
|
||||
{
|
||||
if (APlayerController* PC = Pawn->GetController<APlayerController>())
|
||||
{
|
||||
if (PC->WasInputKeyJustPressed(SkeletonType == EOculusXRHandType::HandLeft ? OculusXRInput::FOculusKey::OculusHand_Left_SystemGesture : OculusXRInput::FOculusKey::OculusHand_Right_SystemGesture))
|
||||
{
|
||||
SystemGesturePressed();
|
||||
}
|
||||
if (PC->WasInputKeyJustReleased(SkeletonType == EOculusXRHandType::HandLeft ? OculusXRInput::FOculusKey::OculusHand_Left_SystemGesture : OculusXRInput::FOculusKey::OculusHand_Right_SystemGesture))
|
||||
{
|
||||
SystemGestureReleased();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
bHidden = true;
|
||||
}
|
||||
|
||||
if (bHidden != bHiddenInGame)
|
||||
{
|
||||
SetHiddenInGame(bHidden);
|
||||
for (int32 i = 0; i < CollisionCapsules.Num(); i++)
|
||||
{
|
||||
CollisionCapsules[i].Capsule->SetCollisionEnabled(bHidden ? ECollisionEnabled::NoCollision : ECollisionEnabled::QueryAndPhysics);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::UpdateBonePose()
|
||||
{
|
||||
if (bCustomHandMesh)
|
||||
{
|
||||
for (auto& BoneElem : BoneNameMappings)
|
||||
{
|
||||
// Set Root Bone Rotaiton
|
||||
if (BoneElem.Key == EOculusXRBone::Wrist_Root)
|
||||
{
|
||||
FQuat RootBoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, EOculusXRBone::Wrist_Root);
|
||||
RootBoneRotation *= HandRootFixupRotation;
|
||||
RootBoneRotation.Normalize();
|
||||
BoneSpaceTransforms[0].SetRotation(RootBoneRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set Remaing Bone Rotations
|
||||
int32 BoneIndex = GetSkinnedAsset()->GetRefSkeleton().FindBoneIndex(BoneElem.Value);
|
||||
if (BoneIndex >= 0)
|
||||
{
|
||||
FQuat BoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, (EOculusXRBone)BoneElem.Key);
|
||||
BoneSpaceTransforms[BoneIndex].SetRotation(BoneRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set Root Bone Rotation
|
||||
FQuat RootBoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, EOculusXRBone::Wrist_Root);
|
||||
RootBoneRotation *= HandRootFixupRotation;
|
||||
RootBoneRotation.Normalize();
|
||||
BoneSpaceTransforms[0].SetRotation(RootBoneRotation);
|
||||
|
||||
// Set Remaining Bone Rotations
|
||||
for (uint32 BoneIndex = 1; BoneIndex < (uint32)GetSkinnedAsset()->GetRefSkeleton().GetNum(); BoneIndex++)
|
||||
{
|
||||
FQuat BoneRotation = UOculusXRInputFunctionLibrary::GetBoneRotation(SkeletonType, (EOculusXRBone)BoneIndex);
|
||||
BoneSpaceTransforms[BoneIndex].SetRotation(BoneRotation);
|
||||
}
|
||||
}
|
||||
MarkRefreshTransformDirty();
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::SystemGesturePressed()
|
||||
{
|
||||
if (SystemGestureBehavior == EOculusXRSystemGestureBehavior::SwapMaterial)
|
||||
{
|
||||
if (SystemGestureMaterial)
|
||||
{
|
||||
SetMaterial(0, SystemGestureMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("System Gesture Behavior was set to Swap Material but no System Gesture Material was provided!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRHandComponent::SystemGestureReleased()
|
||||
{
|
||||
if (SystemGestureBehavior == EOculusXRSystemGestureBehavior::SwapMaterial)
|
||||
{
|
||||
if (CachedBaseMaterial)
|
||||
{
|
||||
SetMaterial(0, CachedBaseMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("System Gesture Behavior was set to Swap Material but no System Gesture Material was provided!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,687 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRHandTracking.h"
|
||||
#include "OculusXRHMD.h"
|
||||
#include "Misc/CoreDelegates.h"
|
||||
#include "IOculusXRInputModule.h"
|
||||
|
||||
#include "Animation/Skeleton.h"
|
||||
#include "BoneWeights.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Rendering/SkeletalMeshLODRenderData.h"
|
||||
#include "Rendering/SkeletalMeshRenderData.h"
|
||||
#include "Rendering/SkeletalMeshLODModel.h"
|
||||
#include "Rendering/SkeletalMeshModel.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInstanceDynamic.h"
|
||||
#include "Engine/SkinnedAssetCommon.h"
|
||||
#include "Model.h"
|
||||
#include "MaterialDomain.h"
|
||||
|
||||
#define OCULUS_TO_UE4_SCALE 100.0f
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
static FInputDeviceId GetDeviceID(int32 ControllerId)
|
||||
{
|
||||
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
|
||||
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
|
||||
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
|
||||
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
|
||||
return InDeviceId;
|
||||
}
|
||||
|
||||
FQuat FOculusHandTracking::GetBoneRotation(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId)
|
||||
{
|
||||
FQuat Rotation = FQuat::Identity;
|
||||
if (BoneId <= EOculusXRBone::Invalid && BoneId >= EOculusXRBone::Bone_Max)
|
||||
{
|
||||
return Rotation;
|
||||
}
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
const FOculusHandControllerState& HandState = HandPair.HandControllerStates[Hand];
|
||||
int32 OvrBoneId = ToOvrBone(BoneId);
|
||||
Rotation = HandState.BoneRotations[OvrBoneId];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return Rotation;
|
||||
}
|
||||
|
||||
float FOculusHandTracking::GetHandScale(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].HandScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
EOculusXRTrackingConfidence FOculusHandTracking::GetTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].TrackingConfidence;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return EOculusXRTrackingConfidence::Low;
|
||||
}
|
||||
|
||||
EOculusXRTrackingConfidence FOculusHandTracking::GetFingerTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusHandAxes Finger)
|
||||
{
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].FingerConfidences[(int)Finger];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EOculusXRTrackingConfidence::Low;
|
||||
}
|
||||
|
||||
FTransform FOculusHandTracking::GetPointerPose(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const float WorldToMeters)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
FTransform PoseTransform = HandPair.HandControllerStates[Hand].PointerPose;
|
||||
PoseTransform.SetLocation(PoseTransform.GetLocation() * WorldToMeters);
|
||||
return PoseTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return FTransform();
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::IsPointerPoseValid(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].bIsPointerPoseValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::IsHandTrackingEnabled()
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
ovrpBool result;
|
||||
FOculusXRHMDModule::GetPluginWrapper().GetHandTrackingEnabled(&result);
|
||||
return result == ovrpBool_True;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::IsHandDominant(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].bIsDominantHand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::IsHandPositionValid(int32 ControllerIndex, EOculusXRHandType DeviceHand)
|
||||
{
|
||||
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
|
||||
if (OculusXRInputModule.IsValid())
|
||||
{
|
||||
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
|
||||
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
|
||||
for (const FOculusControllerPair& HandPair : ControllerPairs)
|
||||
{
|
||||
if (HandPair.DeviceId == InDeviceId)
|
||||
{
|
||||
if (DeviceHand != EOculusXRHandType::None)
|
||||
{
|
||||
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
|
||||
return HandPair.HandControllerStates[Hand].bIsPositionValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, const EOculusXRHandType SkeletonType, const EOculusXRHandType MeshType, const float WorldToMeters)
|
||||
{
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
if (HandSkeletalMesh)
|
||||
{
|
||||
ovrpMesh* OvrMesh = new ovrpMesh();
|
||||
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
|
||||
|
||||
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
|
||||
ovrpMeshType OvrMeshType = (ovrpMeshType)((int32)MeshType - 1);
|
||||
ovrpResult SkelResult = FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton);
|
||||
ovrpResult MeshResult = FOculusXRHMDModule::GetPluginWrapper().GetMesh(OvrMeshType, OvrMesh);
|
||||
if (SkelResult != ovrpSuccess || MeshResult != ovrpSuccess)
|
||||
{
|
||||
#if !WITH_EDITOR
|
||||
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get mesh or skeleton data from Oculus runtime."));
|
||||
#endif
|
||||
delete OvrMesh;
|
||||
delete OvrSkeleton;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create Skeletal Mesh LOD Render Data
|
||||
#if WITH_EDITOR
|
||||
FSkeletalMeshLODModel* LodRenderData = new FSkeletalMeshLODModel();
|
||||
HandSkeletalMesh->GetImportedModel()->LODModels.Add(LodRenderData);
|
||||
#else
|
||||
FSkeletalMeshLODRenderData* LodRenderData = new FSkeletalMeshLODRenderData();
|
||||
HandSkeletalMesh->AllocateResourceForRendering();
|
||||
HandSkeletalMesh->GetResourceForRendering()->LODRenderData.Add(LodRenderData);
|
||||
#endif
|
||||
|
||||
// Set default LOD Info
|
||||
FSkeletalMeshLODInfo& LodInfo = HandSkeletalMesh->AddLODInfo();
|
||||
LodInfo.ScreenSize = 0.3f;
|
||||
LodInfo.LODHysteresis = 0.2f;
|
||||
LodInfo.BuildSettings.bUseFullPrecisionUVs = true;
|
||||
|
||||
InitializeHandSkeleton(HandSkeletalMesh, OvrSkeleton, WorldToMeters);
|
||||
|
||||
// Add default material as backup
|
||||
LodInfo.LODMaterialMap.Add(0);
|
||||
UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface);
|
||||
HandSkeletalMesh->GetMaterials().Add(DefaultMaterial);
|
||||
HandSkeletalMesh->GetMaterials()[0].UVChannelData.bInitialized = true;
|
||||
|
||||
// Set skeletal mesh properties
|
||||
HandSkeletalMesh->SetHasVertexColors(true);
|
||||
HandSkeletalMesh->SetHasBeenSimplified(false);
|
||||
HandSkeletalMesh->SetEnablePerPolyCollision(false);
|
||||
|
||||
InitializeHandMesh(HandSkeletalMesh, OvrMesh, WorldToMeters);
|
||||
|
||||
#if WITH_EDITOR
|
||||
HandSkeletalMesh->InvalidateDeriveDataCacheGUID();
|
||||
HandSkeletalMesh->PostEditChange();
|
||||
#endif
|
||||
|
||||
// Create Skeleton object and merge all bones
|
||||
HandSkeletalMesh->SetSkeleton(NewObject<USkeleton>());
|
||||
HandSkeletalMesh->GetSkeleton()->MergeAllBonesToBoneTree(HandSkeletalMesh);
|
||||
HandSkeletalMesh->PostLoad();
|
||||
|
||||
delete OvrMesh;
|
||||
delete OvrSkeleton;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void FOculusHandTracking::InitializeHandMesh(USkeletalMesh* SkeletalMesh, const ovrpMesh* OvrMesh, const float WorldToMeters)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
|
||||
|
||||
// Initialize mesh section
|
||||
LodRenderData->Sections.SetNumUninitialized(1);
|
||||
new (&LodRenderData->Sections[0]) FSkelMeshSection();
|
||||
auto& MeshSection = LodRenderData->Sections[0];
|
||||
|
||||
// Set default mesh section properties
|
||||
MeshSection.MaterialIndex = 0;
|
||||
MeshSection.BaseIndex = 0;
|
||||
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
|
||||
MeshSection.BaseVertexIndex = 0;
|
||||
MeshSection.MaxBoneInfluences = 4;
|
||||
MeshSection.NumVertices = OvrMesh->NumVertices;
|
||||
|
||||
float MaxDistSq = MIN_flt;
|
||||
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
|
||||
{
|
||||
FSoftSkinVertex SoftVertex;
|
||||
FMemory::Memzero(SoftVertex.InfluenceWeights);
|
||||
FMemory::Memzero(SoftVertex.InfluenceBones);
|
||||
|
||||
// Update vertex data
|
||||
SoftVertex.Color = FColor::White;
|
||||
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
|
||||
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
|
||||
SoftVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
|
||||
SoftVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
|
||||
SoftVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
|
||||
SoftVertex.TangentY = FVector3f(0.0f, 1.0f, 0.0f); // SoftVertex.TangentZ^ SoftVertex.TangentX* SoftVertex.TangentZ.W;
|
||||
SoftVertex.UVs[0] = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
|
||||
|
||||
// Update the Bounds
|
||||
float VertexDistSq = SoftVertex.Position.SizeSquared();
|
||||
if (VertexDistSq > MaxDistSq)
|
||||
MaxDistSq = VertexDistSq;
|
||||
|
||||
// Update blend weights and indices
|
||||
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
|
||||
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
|
||||
|
||||
SoftVertex.InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
|
||||
SoftVertex.InfluenceBones[0] = BlendIndices.x;
|
||||
SoftVertex.InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
|
||||
SoftVertex.InfluenceBones[1] = BlendIndices.y;
|
||||
SoftVertex.InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
|
||||
SoftVertex.InfluenceBones[2] = BlendIndices.z;
|
||||
SoftVertex.InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
|
||||
SoftVertex.InfluenceBones[3] = BlendIndices.w;
|
||||
|
||||
MeshSection.SoftVertices.Add(SoftVertex);
|
||||
}
|
||||
|
||||
// Update bone map
|
||||
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
|
||||
{
|
||||
MeshSection.BoneMap.Add(BoneIndex);
|
||||
}
|
||||
|
||||
// Update LOD render data
|
||||
LodRenderData->NumVertices = OvrMesh->NumVertices;
|
||||
LodRenderData->NumTexCoords = 1;
|
||||
|
||||
// Create index buffer
|
||||
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
|
||||
{
|
||||
LodRenderData->IndexBuffer.Add(OvrMesh->Indices[Index]);
|
||||
}
|
||||
|
||||
// Finalize Bounds
|
||||
float MaxDist = FMath::Sqrt(MaxDistSq);
|
||||
FBoxSphereBounds Bounds;
|
||||
Bounds.Origin = FVector::ZeroVector;
|
||||
Bounds.BoxExtent = FVector(MaxDist);
|
||||
Bounds.SphereRadius = MaxDist;
|
||||
SkeletalMesh->SetImportedBounds(Bounds);
|
||||
|
||||
#else
|
||||
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
|
||||
|
||||
// Initialize Mesh Section
|
||||
LodRenderData->RenderSections.SetNumUninitialized(1);
|
||||
new (&LodRenderData->RenderSections[0]) FSkelMeshRenderSection();
|
||||
auto& MeshSection = LodRenderData->RenderSections[0];
|
||||
|
||||
// Initialize render section properties
|
||||
MeshSection.MaterialIndex = 0;
|
||||
MeshSection.BaseIndex = 0;
|
||||
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
|
||||
MeshSection.BaseVertexIndex = 0;
|
||||
MeshSection.MaxBoneInfluences = 4;
|
||||
MeshSection.NumVertices = OvrMesh->NumVertices;
|
||||
MeshSection.bCastShadow = true;
|
||||
MeshSection.bDisabled = false;
|
||||
MeshSection.bRecomputeTangent = false;
|
||||
|
||||
// Initialize Vertex Buffers
|
||||
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.Init(OvrMesh->NumVertices);
|
||||
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.Init(OvrMesh->NumVertices, 1);
|
||||
LodRenderData->StaticVertexBuffers.ColorVertexBuffer.Init(OvrMesh->NumVertices);
|
||||
|
||||
// Initialize Skin Weights
|
||||
TArray<FSkinWeightInfo> InWeights;
|
||||
InWeights.AddUninitialized(OvrMesh->NumVertices);
|
||||
|
||||
float MaxDistSq = MIN_flt;
|
||||
TMap<int32, TArray<int32>> OverlappingVertices;
|
||||
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
|
||||
{
|
||||
FMemory::Memzero(InWeights[VertexIndex].InfluenceWeights);
|
||||
FMemory::Memzero(InWeights[VertexIndex].InfluenceBones);
|
||||
// Initialize vertex data
|
||||
FModelVertex ModelVertex;
|
||||
|
||||
// Update Model Vertex
|
||||
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
|
||||
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
|
||||
ModelVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
|
||||
ModelVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
|
||||
ModelVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
|
||||
ModelVertex.TexCoord = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
|
||||
|
||||
// Add Model Vertex data to vertex buffer
|
||||
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex) = ModelVertex.Position;
|
||||
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(VertexIndex, ModelVertex.TangentX, ModelVertex.GetTangentY(), ModelVertex.TangentZ);
|
||||
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexUV(VertexIndex, 0, ModelVertex.TexCoord);
|
||||
|
||||
// Update the Bounds
|
||||
float VertexDistSq = ModelVertex.Position.SizeSquared();
|
||||
if (VertexDistSq > MaxDistSq)
|
||||
MaxDistSq = VertexDistSq;
|
||||
|
||||
// Set vertex blend weights and indices
|
||||
TArray<int32> Vertices;
|
||||
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
|
||||
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
|
||||
|
||||
InWeights[VertexIndex].InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
|
||||
InWeights[VertexIndex].InfluenceBones[0] = BlendIndices.x;
|
||||
Vertices.Add(BlendIndices.x);
|
||||
InWeights[VertexIndex].InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
|
||||
InWeights[VertexIndex].InfluenceBones[1] = BlendIndices.y;
|
||||
Vertices.Add(BlendIndices.y);
|
||||
InWeights[VertexIndex].InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
|
||||
InWeights[VertexIndex].InfluenceBones[2] = BlendIndices.z;
|
||||
Vertices.Add(BlendIndices.z);
|
||||
InWeights[VertexIndex].InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
|
||||
InWeights[VertexIndex].InfluenceBones[3] = BlendIndices.w;
|
||||
Vertices.Add(BlendIndices.w);
|
||||
|
||||
OverlappingVertices.Add(VertexIndex, Vertices);
|
||||
}
|
||||
|
||||
// Update bone map for mesh section
|
||||
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
|
||||
{
|
||||
MeshSection.BoneMap.Add(BoneIndex);
|
||||
}
|
||||
|
||||
// Finalize Bounds
|
||||
float MaxDist = FMath::Sqrt(MaxDistSq);
|
||||
FBoxSphereBounds Bounds;
|
||||
Bounds.Origin = FVector::ZeroVector;
|
||||
Bounds.BoxExtent = FVector(MaxDist);
|
||||
Bounds.SphereRadius = MaxDist;
|
||||
SkeletalMesh->SetImportedBounds(Bounds);
|
||||
|
||||
// Assign skin weights to vertex buffer
|
||||
LodRenderData->SkinWeightVertexBuffer = InWeights;
|
||||
MeshSection.DuplicatedVerticesBuffer.Init(OvrMesh->NumVertices, OverlappingVertices);
|
||||
|
||||
// Set index buffer
|
||||
LodRenderData->MultiSizeIndexContainer.CreateIndexBuffer(sizeof(uint16_t));
|
||||
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
|
||||
{
|
||||
LodRenderData->MultiSizeIndexContainer.GetIndexBuffer()->AddItem(OvrMesh->Indices[Index]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FOculusHandTracking::InitializeHandSkeleton(USkeletalMesh* SkeletalMesh, const ovrpSkeleton2* OvrSkeleton, const float WorldToMeters)
|
||||
{
|
||||
SkeletalMesh->GetRefSkeleton().Empty(OvrSkeleton->NumBones);
|
||||
|
||||
#if WITH_EDITOR
|
||||
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
|
||||
#else
|
||||
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
|
||||
#endif
|
||||
SkeletalMesh->SetHasBeenSimplified(false);
|
||||
SkeletalMesh->SetHasVertexColors(true);
|
||||
|
||||
checkf(OvrSkeleton->NumBones <= static_cast<unsigned int>(TNumericLimits<uint8>::Max()), TEXT("Bone indices are stored as uint8 type."));
|
||||
for (uint8 BoneIndex = 0; BoneIndex < static_cast<uint8>(OvrSkeleton->NumBones); BoneIndex++)
|
||||
{
|
||||
LodRenderData->ActiveBoneIndices.Add(BoneIndex);
|
||||
LodRenderData->RequiredBones.Add(BoneIndex);
|
||||
|
||||
FText BoneDisplayName;
|
||||
if (!FindBoneDisplayName(BoneDisplayName, BoneIndex))
|
||||
{
|
||||
UE_LOG(LogOcHandTracking, Error, TEXT("Cannot find bone display name for bone index: %d."), BoneIndex)
|
||||
continue;
|
||||
}
|
||||
FString BoneString = BoneDisplayName.ToString();
|
||||
FName BoneName = FName(*BoneString);
|
||||
|
||||
FTransform Transform = FTransform::Identity;
|
||||
FVector BonePosition = OvrBoneVectorToFVector(OvrSkeleton->Bones[BoneIndex].Pose.Position, WorldToMeters);
|
||||
FQuat BoneRotation = BoneIndex == 0 ? FQuat(-1.0f, 0.0f, 0.0f, 1.0f) : OvrBoneQuatToFQuat(OvrSkeleton->Bones[BoneIndex].Pose.Orientation);
|
||||
Transform.SetLocation(BonePosition);
|
||||
Transform.SetRotation(BoneRotation);
|
||||
|
||||
FReferenceSkeletonModifier Modifier = FReferenceSkeletonModifier(SkeletalMesh->GetRefSkeleton(), nullptr);
|
||||
int32 ParentIndex = -1;
|
||||
if (BoneIndex > 0)
|
||||
{
|
||||
if (OvrSkeleton->Bones[BoneIndex].ParentBoneIndex == ovrpBoneId::ovrpBoneId_Invalid)
|
||||
{
|
||||
ParentIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentIndex = OvrSkeleton->Bones[BoneIndex].ParentBoneIndex;
|
||||
}
|
||||
}
|
||||
Modifier.Add(FMeshBoneInfo(BoneName, BoneString, ParentIndex), Transform);
|
||||
}
|
||||
SkeletalMesh->CalculateInvRefMatrices();
|
||||
}
|
||||
|
||||
TArray<FOculusXRCapsuleCollider> FOculusHandTracking::InitializeHandPhysics(const EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters)
|
||||
{
|
||||
TArray<FOculusXRCapsuleCollider> CollisionCapsules;
|
||||
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
|
||||
if (FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton) != ovrpSuccess)
|
||||
{
|
||||
#if !WITH_EDITOR
|
||||
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get skeleton data from Oculus runtime."));
|
||||
#endif
|
||||
delete OvrSkeleton;
|
||||
return CollisionCapsules;
|
||||
}
|
||||
#endif
|
||||
|
||||
TArray<UPrimitiveComponent*> IgnoreCapsules;
|
||||
CollisionCapsules.AddDefaulted(OvrSkeleton->NumBoneCapsules);
|
||||
for (uint32 CapsuleIndex = 0; CapsuleIndex < OvrSkeleton->NumBoneCapsules; CapsuleIndex++)
|
||||
{
|
||||
ovrpBoneCapsule OvrBoneCapsule = OvrSkeleton->BoneCapsules[CapsuleIndex];
|
||||
|
||||
UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(HandComponent);
|
||||
|
||||
FVector CapsulePointZero = OvrBoneVectorToFVector(OvrBoneCapsule.Points[0], WorldToMeters);
|
||||
FVector CapsulePointOne = OvrBoneVectorToFVector(OvrBoneCapsule.Points[1], WorldToMeters);
|
||||
FVector Delta = (CapsulePointOne - CapsulePointZero);
|
||||
|
||||
FName BoneName = HandComponent->GetSkinnedAsset()->GetRefSkeleton().GetBoneName(OvrBoneCapsule.BoneIndex);
|
||||
|
||||
float CapsuleHeight = Delta.Size();
|
||||
float CapsuleRadius = OvrBoneCapsule.Radius * WorldToMeters;
|
||||
|
||||
Capsule->SetCapsuleRadius(CapsuleRadius);
|
||||
Capsule->SetCapsuleHalfHeight(Delta.Size() / 2 + CapsuleRadius);
|
||||
Capsule->SetupAttachment(HandComponent, BoneName);
|
||||
Capsule->SetCollisionProfileName(HandComponent->GetCollisionProfileName());
|
||||
Capsule->RegisterComponentWithWorld(HandComponent->GetWorld());
|
||||
Capsule->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||||
FRotator CapsuleRotation = FQuat::FindBetweenVectors(FVector::RightVector, Delta).Rotator() + FRotator(0, 0, 90);
|
||||
;
|
||||
|
||||
Capsule->SetRelativeRotation(CapsuleRotation);
|
||||
Capsule->SetRelativeLocation(CapsulePointZero + (Delta / 2));
|
||||
|
||||
CollisionCapsules[CapsuleIndex].Capsule = Capsule;
|
||||
CollisionCapsules[CapsuleIndex].BoneId = (EOculusXRBone)OvrBoneCapsule.BoneIndex;
|
||||
|
||||
IgnoreCapsules.Add(Capsule);
|
||||
}
|
||||
|
||||
for (int32 CapsuleIndex = 0; CapsuleIndex < CollisionCapsules.Num(); CapsuleIndex++)
|
||||
{
|
||||
CollisionCapsules[CapsuleIndex].Capsule->MoveIgnoreComponents = IgnoreCapsules;
|
||||
}
|
||||
|
||||
return CollisionCapsules;
|
||||
}
|
||||
|
||||
ovrpBoneId FOculusHandTracking::ToOvrBone(EOculusXRBone Bone)
|
||||
{
|
||||
if (Bone > EOculusXRBone::Bone_Max)
|
||||
return ovrpBoneId_Invalid;
|
||||
|
||||
return static_cast<ovrpBoneId>(Bone);
|
||||
}
|
||||
|
||||
FString FOculusHandTracking::GetBoneName(const uint8 Bone)
|
||||
{
|
||||
FText DisplayName;
|
||||
if (FindBoneDisplayName(DisplayName, Bone))
|
||||
{
|
||||
return DisplayName.ToString();
|
||||
}
|
||||
if (FindBoneDisplayName(DisplayName, static_cast<uint8>(EOculusXRBone::Invalid)))
|
||||
{
|
||||
return DisplayName.ToString();
|
||||
}
|
||||
return { "Invalid" };
|
||||
}
|
||||
|
||||
bool FOculusHandTracking::FindBoneDisplayName(FText& DisplayName, uint8 Bone)
|
||||
{
|
||||
return StaticEnum<EOculusXRBone>()->FindDisplayNameTextByValue(DisplayName, Bone);
|
||||
}
|
||||
|
||||
EOculusXRTrackingConfidence FOculusHandTracking::ToEOculusXRTrackingConfidence(ovrpTrackingConfidence Confidence)
|
||||
{
|
||||
EOculusXRTrackingConfidence TrackingConfidence = EOculusXRTrackingConfidence::Low;
|
||||
switch (Confidence)
|
||||
{
|
||||
case ovrpTrackingConfidence_Low:
|
||||
TrackingConfidence = EOculusXRTrackingConfidence::Low;
|
||||
break;
|
||||
case ovrpTrackingConfidence_High:
|
||||
TrackingConfidence = EOculusXRTrackingConfidence::High;
|
||||
break;
|
||||
}
|
||||
return TrackingConfidence;
|
||||
}
|
||||
|
||||
FVector FOculusHandTracking::OvrBoneVectorToFVector(ovrpVector3f ovrpVector, float WorldToMeters)
|
||||
{
|
||||
return FVector(ovrpVector.x, -ovrpVector.y, ovrpVector.z) * WorldToMeters;
|
||||
}
|
||||
|
||||
FQuat FOculusHandTracking::OvrBoneQuatToFQuat(ovrpQuatf ovrpQuat)
|
||||
{
|
||||
return FQuat(ovrpQuat.x, -ovrpQuat.y, ovrpQuat.z, -ovrpQuat.w);
|
||||
}
|
||||
|
||||
EOculusXRControllerDrivenHandPoseTypes FOculusHandTracking::ControllerDrivenHandType = EOculusXRControllerDrivenHandPoseTypes::None;
|
||||
|
||||
void FOculusHandTracking::SetControllerDrivenHandPoses(const EOculusXRControllerDrivenHandPoseTypes Type)
|
||||
{
|
||||
FOculusHandTracking::ControllerDrivenHandType = Type;
|
||||
switch (Type)
|
||||
{
|
||||
case EOculusXRControllerDrivenHandPoseTypes::None:
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(false);
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
|
||||
break;
|
||||
case EOculusXRControllerDrivenHandPoseTypes::Natural:
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(true);
|
||||
break;
|
||||
case EOculusXRControllerDrivenHandPoseTypes::Controller:
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
|
||||
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace OculusXRInput
|
||||
@@ -0,0 +1,58 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRHMDModule.h"
|
||||
#include "OculusXRInput.h"
|
||||
#include "Engine/SkeletalMesh.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusHandTracking"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogOcHandTracking, Log, All);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusHandTracking
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusHandTracking
|
||||
{
|
||||
public:
|
||||
// Oculus Hand Tracking
|
||||
static FQuat GetBoneRotation(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId);
|
||||
static float GetHandScale(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
|
||||
static EOculusXRTrackingConfidence GetTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
|
||||
static EOculusXRTrackingConfidence GetFingerTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusHandAxes Finger); // OCULUS STRIKE
|
||||
static FTransform GetPointerPose(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const float WorldToMeters = 100.f);
|
||||
static bool IsPointerPoseValid(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
|
||||
static bool GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, const EOculusXRHandType SkeletonType, const EOculusXRHandType MeshType, const float WorldToMeters = 100.f);
|
||||
static TArray<FOculusXRCapsuleCollider> InitializeHandPhysics(const EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters = 100.f);
|
||||
static EOculusXRTrackingConfidence ToEOculusXRTrackingConfidence(ovrpTrackingConfidence Confidence);
|
||||
static bool IsHandTrackingEnabled();
|
||||
static bool IsHandDominant(const int32 ControllerIndex, const EOculusXRHandType DeviceHand);
|
||||
static bool IsHandPositionValid(int32 ControllerIndex, EOculusXRHandType DeviceHand);
|
||||
static void SetControllerDrivenHandPoses(const EOculusXRControllerDrivenHandPoseTypes Type);
|
||||
|
||||
// Helper functions
|
||||
static ovrpBoneId ToOvrBone(EOculusXRBone Bone);
|
||||
static FString GetBoneName(uint8 Bone);
|
||||
static bool FindBoneDisplayName(FText& DisplayName, uint8 Bone);
|
||||
|
||||
// Converters for converting from ovr bone space (should match up with ovr avatar)
|
||||
static FVector OvrBoneVectorToFVector(ovrpVector3f ovrpVector, float WorldToMeters);
|
||||
static FQuat OvrBoneQuatToFQuat(ovrpQuatf ovrpQuat);
|
||||
|
||||
static EOculusXRControllerDrivenHandPoseTypes ControllerDrivenHandType;
|
||||
|
||||
private:
|
||||
// Initializers for runtime hand assets
|
||||
static void InitializeHandMesh(USkeletalMesh* SkeletalMesh, const ovrpMesh* OvrMesh, const float WorldToMeters);
|
||||
static void InitializeHandSkeleton(USkeletalMesh* SkeletalMesh, const ovrpSkeleton2* OvrSkeleton, const float WorldToMeters);
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "IOculusXRInputModule.h"
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
#include "OculusXRHMDModule.h"
|
||||
#include "GenericPlatform/IInputInterface.h"
|
||||
#include "XRMotionControllerBase.h"
|
||||
#include "IHapticDevice.h"
|
||||
#include "OculusXRInputState.h"
|
||||
|
||||
#if PLATFORM_SUPPORTS_PRAGMA_PACK
|
||||
#pragma pack(push, 8)
|
||||
#endif
|
||||
|
||||
#include "OculusXRPluginWrapper.h"
|
||||
|
||||
#if PLATFORM_SUPPORTS_PRAGMA_PACK
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogOcInput, Log, All);
|
||||
|
||||
class UHapticFeedbackEffect_Base;
|
||||
struct FActiveHapticFeedbackEffect;
|
||||
struct FOculusXRHapticsDesc;
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInput
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
class FOculusXRInput : public IInputDevice, public FXRMotionControllerBase, public IHapticDevice
|
||||
{
|
||||
friend class FOculusHandTracking;
|
||||
|
||||
public:
|
||||
/** Constructor that takes an initial message handler that will receive motion controller events */
|
||||
FOculusXRInput(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler);
|
||||
|
||||
/** Clean everything up */
|
||||
virtual ~FOculusXRInput();
|
||||
|
||||
static void PreInit();
|
||||
|
||||
/** Loads any settings from the config folder that we need */
|
||||
static void LoadConfig();
|
||||
|
||||
// IInputDevice overrides
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual void SendControllerEvents() override;
|
||||
virtual void SetMessageHandler(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
|
||||
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
|
||||
virtual void SetChannelValue(int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override;
|
||||
virtual void SetChannelValues(int32 ControllerId, const FForceFeedbackValues& Values) override;
|
||||
virtual bool SupportsForceFeedback(int32 ControllerId) override;
|
||||
|
||||
// IMotionController overrides
|
||||
virtual FName GetMotionControllerDeviceTypeName() const override;
|
||||
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||||
virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const EControllerHand DeviceHand, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const override;
|
||||
virtual ETrackingStatus GetControllerTrackingStatus(const int32 ControllerIndex, const EControllerHand DeviceHand) const override;
|
||||
#endif
|
||||
virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float WorldToMetersScale) const override;
|
||||
virtual ETrackingStatus GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const override;
|
||||
|
||||
// IHapticDevice overrides
|
||||
IHapticDevice* GetHapticDevice() override { return (IHapticDevice*)this; }
|
||||
virtual void SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values) override;
|
||||
|
||||
void PlayHapticEffect(
|
||||
UHapticFeedbackEffect_Base* HapticEffect,
|
||||
EControllerHand Hand,
|
||||
EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bAppend = false,
|
||||
float Scale = 1.f,
|
||||
bool bLoop = false);
|
||||
int PlayHapticEffect(EControllerHand Hand, int SamplesCount, void* Samples, int SampleRate = -1, bool bPCM = false, bool bAppend = false);
|
||||
void SetHapticsByValue(float Frequency, float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
|
||||
|
||||
virtual void GetHapticFrequencyRange(float& MinFrequency, float& MaxFrequency) const override;
|
||||
virtual float GetHapticAmplitudeScale() const override;
|
||||
|
||||
uint32 GetNumberOfTouchControllers() const;
|
||||
uint32 GetNumberOfHandControllers() const;
|
||||
|
||||
float GetControllerSampleRateHz(EControllerHand Hand);
|
||||
int GetMaxHapticDuration(EControllerHand Hand);
|
||||
|
||||
private:
|
||||
/** Applies force feedback settings to the controller */
|
||||
void UpdateForceFeedback(const FOculusControllerPair& ControllerPair, const EControllerHand Hand);
|
||||
|
||||
bool OnControllerButtonPressed(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat);
|
||||
bool OnControllerButtonReleased(const FOculusButtonState& ButtonState, FPlatformUserId UserId, FInputDeviceId DeviceId, bool IsRepeat);
|
||||
|
||||
void SetHapticFeedbackValues(int32 ControllerId, int32 Hand, const FHapticFeedbackValues& Values, TSharedPtr<FOculusXRHapticsDesc> HapticsDesc);
|
||||
ovrpHapticsLocation GetOVRPHapticsLocation(EOculusXRHandHapticsLocation InLocation);
|
||||
|
||||
void ProcessHaptics(const float DeltaTime);
|
||||
bool GetOvrpHapticsDesc(int Hand);
|
||||
|
||||
private:
|
||||
/** The recipient of motion controller input events */
|
||||
TSharedPtr<FGenericApplicationMessageHandler> MessageHandler;
|
||||
|
||||
/** List of the connected pairs of controllers, with state for each controller device */
|
||||
TArray<FOculusControllerPair> ControllerPairs;
|
||||
|
||||
FOculusRemoteControllerState Remote;
|
||||
|
||||
ovrpHapticsDesc OvrpHapticsDesc;
|
||||
|
||||
int LocalTrackingSpaceRecenterCount;
|
||||
|
||||
// Maintain a cache of resampled raw data so we don't resample it on every play. This is a map of OriginalRawData pointers, used only as a key, to ResampledRawData buffers.
|
||||
// The values are pointers because the map could be reallocated and we cache raw pointers to the uint8 array data elsewhere.
|
||||
TMap<const uint8*, TSharedPtr<TArray<uint8>>> ResampledRawDataCache;
|
||||
|
||||
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Left;
|
||||
TSharedPtr<FActiveHapticFeedbackEffect> ActiveHapticEffect_Right;
|
||||
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Left;
|
||||
TSharedPtr<FOculusXRHapticsDesc> HapticsDesc_Right;
|
||||
double StartTime = 0.0;
|
||||
|
||||
/** Threshold for treating trigger pulls as button presses, from 0.0 to 1.0 */
|
||||
static float TriggerThreshold;
|
||||
|
||||
/** Are Remote keys mapped to gamepad or not. */
|
||||
static bool bRemoteKeysMappedToGamepad;
|
||||
|
||||
/** Repeat key delays, loaded from config */
|
||||
static float InitialButtonRepeatDelay;
|
||||
static float ButtonRepeatDelay;
|
||||
|
||||
static bool bPulledHapticsDesc;
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#endif //OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
@@ -0,0 +1,175 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
#include "OculusXRHandTracking.h"
|
||||
#include "OculusXRControllerTracking.h"
|
||||
#include "Logging/MessageLog.h"
|
||||
#include "Haptics/HapticFeedbackEffect_Buffer.h"
|
||||
#include "Haptics/HapticFeedbackEffect_Curve.h"
|
||||
#include "Haptics/HapticFeedbackEffect_SoundWave.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// UOculusHandTrackingFunctionLibrary
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UOculusXRInputFunctionLibrary::UOculusXRInputFunctionLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
UOculusXRInputFunctionLibrary::FHandMovementFilterDelegate UOculusXRInputFunctionLibrary::HandMovementFilter;
|
||||
|
||||
EOculusXRFinger UOculusXRInputFunctionLibrary::ConvertBoneToFinger(const EOculusXRBone Bone)
|
||||
{
|
||||
switch (Bone)
|
||||
{
|
||||
case EOculusXRBone::Index_1:
|
||||
case EOculusXRBone::Index_2:
|
||||
case EOculusXRBone::Index_3:
|
||||
case EOculusXRBone::Index_Tip:
|
||||
return EOculusXRFinger::Index;
|
||||
case EOculusXRBone::Middle_1:
|
||||
case EOculusXRBone::Middle_2:
|
||||
case EOculusXRBone::Middle_3:
|
||||
case EOculusXRBone::Middle_Tip:
|
||||
return EOculusXRFinger::Middle;
|
||||
case EOculusXRBone::Pinky_0:
|
||||
case EOculusXRBone::Pinky_1:
|
||||
case EOculusXRBone::Pinky_2:
|
||||
case EOculusXRBone::Pinky_3:
|
||||
case EOculusXRBone::Pinky_Tip:
|
||||
return EOculusXRFinger::Pinky;
|
||||
case EOculusXRBone::Ring_1:
|
||||
case EOculusXRBone::Ring_2:
|
||||
case EOculusXRBone::Ring_3:
|
||||
case EOculusXRBone::Ring_Tip:
|
||||
return EOculusXRFinger::Ring;
|
||||
case EOculusXRBone::Thumb_0:
|
||||
case EOculusXRBone::Thumb_1:
|
||||
case EOculusXRBone::Thumb_2:
|
||||
case EOculusXRBone::Thumb_3:
|
||||
case EOculusXRBone::Thumb_Tip:
|
||||
return EOculusXRFinger::Thumb;
|
||||
default:
|
||||
return EOculusXRFinger::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
EOculusXRTrackingConfidence UOculusXRInputFunctionLibrary::GetFingerTrackingConfidence(const EOculusXRHandType DeviceHand, const EOculusXRFinger Finger, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetFingerTrackingConfidence(ControllerIndex, DeviceHand, (OculusXRInput::EOculusHandAxes)(uint8)Finger);
|
||||
}
|
||||
|
||||
bool UOculusXRInputFunctionLibrary::GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, EOculusXRHandType SkeletonType, EOculusXRHandType MeshType, float WorldToMeters)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetHandSkeletalMesh(HandSkeletalMesh, SkeletonType, MeshType, WorldToMeters);
|
||||
}
|
||||
|
||||
TArray<FOculusXRCapsuleCollider> UOculusXRInputFunctionLibrary::InitializeHandPhysics(EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::InitializeHandPhysics(SkeletonType, HandComponent, WorldToMeters);
|
||||
}
|
||||
|
||||
FQuat UOculusXRInputFunctionLibrary::GetBoneRotation(const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetBoneRotation(ControllerIndex, DeviceHand, BoneId);
|
||||
}
|
||||
|
||||
EOculusXRTrackingConfidence UOculusXRInputFunctionLibrary::GetTrackingConfidence(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetTrackingConfidence(ControllerIndex, DeviceHand);
|
||||
}
|
||||
|
||||
FTransform UOculusXRInputFunctionLibrary::GetPointerPose(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetPointerPose(ControllerIndex, DeviceHand);
|
||||
}
|
||||
|
||||
bool UOculusXRInputFunctionLibrary::IsPointerPoseValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::IsPointerPoseValid(ControllerIndex, DeviceHand);
|
||||
}
|
||||
|
||||
float UOculusXRInputFunctionLibrary::GetHandScale(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetHandScale(ControllerIndex, DeviceHand);
|
||||
}
|
||||
|
||||
EOculusXRHandType UOculusXRInputFunctionLibrary::GetDominantHand(const int32 ControllerIndex)
|
||||
{
|
||||
EOculusXRHandType DominantHand = EOculusXRHandType::None;
|
||||
if (OculusXRInput::FOculusHandTracking::IsHandDominant(ControllerIndex, EOculusXRHandType::HandLeft))
|
||||
{
|
||||
DominantHand = EOculusXRHandType::HandLeft;
|
||||
}
|
||||
else if (OculusXRInput::FOculusHandTracking::IsHandDominant(ControllerIndex, EOculusXRHandType::HandRight))
|
||||
{
|
||||
DominantHand = EOculusXRHandType::HandRight;
|
||||
}
|
||||
return DominantHand;
|
||||
}
|
||||
|
||||
bool UOculusXRInputFunctionLibrary::IsHandTrackingEnabled()
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::IsHandTrackingEnabled();
|
||||
}
|
||||
|
||||
bool UOculusXRInputFunctionLibrary::IsHandPositionValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::IsHandPositionValid(ControllerIndex, DeviceHand);
|
||||
}
|
||||
|
||||
FString UOculusXRInputFunctionLibrary::GetBoneName(EOculusXRBone BoneId)
|
||||
{
|
||||
const auto ovrBoneId = OculusXRInput::FOculusHandTracking::ToOvrBone(BoneId);
|
||||
if (ovrBoneId == ovrpBoneId_Invalid)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::GetBoneName(static_cast<uint8>(EOculusXRBone::Invalid));
|
||||
}
|
||||
return OculusXRInput::FOculusHandTracking::GetBoneName(static_cast<uint8>(ovrBoneId));
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::PlayCurveHapticEffect(class UHapticFeedbackEffect_Curve* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location, float Scale, bool bLoop)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, Location, false, Scale, bLoop);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::PlayBufferHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location, float Scale, bool bLoop)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, Location, false, Scale, bLoop);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::PlayAmplitudeEnvelopeHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(Hand, HapticEffect->Amplitudes, HapticEffect->SampleRate, false, false);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::PlaySoundWaveHapticEffect(class UHapticFeedbackEffect_SoundWave* HapticEffect, EControllerHand Hand, bool bAppend, float Scale, bool bLoop)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::PlayHapticEffect(HapticEffect, Hand, EOculusXRHandHapticsLocation::Hand, bAppend, Scale, bLoop);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::StopHapticEffect(Hand, Location);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::SetHapticsByValue(const float Frequency, const float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location)
|
||||
{
|
||||
OculusXRInput::FOculusXRControllerTracking::SetHapticsByValue(Frequency, Amplitude, Hand, Location);
|
||||
}
|
||||
|
||||
float UOculusXRInputFunctionLibrary::GetControllerSampleRateHz(EControllerHand Hand)
|
||||
{
|
||||
return OculusXRInput::FOculusXRControllerTracking::GetControllerSampleRateHz(Hand);
|
||||
}
|
||||
|
||||
int UOculusXRInputFunctionLibrary::GetMaxHapticDuration(EControllerHand Hand)
|
||||
{
|
||||
return OculusXRInput::FOculusXRControllerTracking::GetMaxHapticDuration(Hand);
|
||||
}
|
||||
|
||||
void UOculusXRInputFunctionLibrary::SetControllerDrivenHandPoses(EOculusXRControllerDrivenHandPoseTypes Type)
|
||||
{
|
||||
return OculusXRInput::FOculusHandTracking::SetControllerDrivenHandPoses(Type);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "OculusXRInputModule.h"
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
#include "OculusXRInput.h"
|
||||
#include "OculusXRHMDModule.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRInput"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInputModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
void FOculusXRInputModule::StartupModule()
|
||||
{
|
||||
IInputDeviceModule::StartupModule();
|
||||
OculusXRInput::FOculusXRInput::PreInit();
|
||||
}
|
||||
|
||||
TSharedPtr<class IInputDevice> FOculusXRInputModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
|
||||
{
|
||||
if (IOculusXRHMDModule::IsAvailable())
|
||||
{
|
||||
if (FOculusXRHMDModule::Get().PreInit())
|
||||
{
|
||||
TSharedPtr<OculusXRInput::FOculusXRInput> InputDevice(new OculusXRInput::FOculusXRInput(InMessageHandler));
|
||||
OculusXRInputDevice = InputDevice;
|
||||
return InputDevice;
|
||||
}
|
||||
// else, they may just not have a oculus headset plugged in (which we have to account for - no need for a warning)
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogOcInput, Warning, TEXT("OculusXRInput plugin enabled, but OculusXRHMD plugin is not available."));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 FOculusXRInputModule::GetNumberOfTouchControllers() const
|
||||
{
|
||||
if (OculusXRInputDevice.IsValid())
|
||||
{
|
||||
return OculusXRInputDevice.Pin()->GetNumberOfTouchControllers();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 FOculusXRInputModule::GetNumberOfHandControllers() const
|
||||
{
|
||||
if (OculusXRInputDevice.IsValid())
|
||||
{
|
||||
return OculusXRInputDevice.Pin()->GetNumberOfHandControllers();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TSharedPtr<IInputDevice> FOculusXRInputModule::GetInputDevice() const
|
||||
{
|
||||
if (OculusXRInputDevice.IsValid())
|
||||
{
|
||||
return OculusXRInputDevice.Pin();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
|
||||
IMPLEMENT_MODULE(FOculusXRInputModule, OculusXRInput)
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,53 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "IOculusXRInputModule.h"
|
||||
#include "IInputDevice.h"
|
||||
#include "Templates/SharedPointer.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRInput"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusXRInputModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
class FOculusXRInput;
|
||||
}
|
||||
|
||||
class FOculusXRInputModule : public IOculusXRInputModule
|
||||
{
|
||||
TWeakPtr<OculusXRInput::FOculusXRInput> OculusXRInputDevice;
|
||||
|
||||
// IInputDeviceModule overrides
|
||||
virtual void StartupModule() override;
|
||||
virtual TSharedPtr<class IInputDevice> CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
|
||||
|
||||
// IOculusXRInputModule overrides
|
||||
virtual uint32 GetNumberOfTouchControllers() const override;
|
||||
virtual uint32 GetNumberOfHandControllers() const override;
|
||||
virtual TSharedPtr<IInputDevice> GetInputDevice() const override;
|
||||
};
|
||||
|
||||
#else // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
|
||||
class FOculusXRInputModule : public FDefaultModuleImpl
|
||||
{
|
||||
virtual uint32 GetNumberOfTouchControllers() const
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
virtual uint32 GetNumberOfHandControllers() const
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,544 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "IOculusXRInputModule.h"
|
||||
|
||||
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
#include "IMotionController.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
#include "GenericPlatform/GenericApplicationMessageHandler.h"
|
||||
|
||||
namespace OculusXRInput
|
||||
{
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Button names
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
enum class EOculusTouchControllerButton
|
||||
{
|
||||
// NOTE: The Trigger and Grip digital buttons are synthetic. Oculus hardware doesn't support a digital press for these
|
||||
Trigger,
|
||||
Grip,
|
||||
|
||||
XA,
|
||||
YB,
|
||||
Thumbstick,
|
||||
|
||||
Thumbstick_Up,
|
||||
Thumbstick_Down,
|
||||
Thumbstick_Left,
|
||||
Thumbstick_Right,
|
||||
|
||||
Menu,
|
||||
|
||||
Thumbstick_Touch,
|
||||
Trigger_Touch,
|
||||
XA_Touch,
|
||||
YB_Touch,
|
||||
|
||||
/** Total number of controller buttons */
|
||||
TotalButtonCount
|
||||
};
|
||||
|
||||
enum class EOculusRemoteControllerButton
|
||||
{
|
||||
DPad_Up,
|
||||
DPad_Down,
|
||||
DPad_Left,
|
||||
DPad_Right,
|
||||
|
||||
Enter,
|
||||
Back,
|
||||
|
||||
VolumeUp,
|
||||
VolumeDown,
|
||||
Home,
|
||||
|
||||
/** Total number of controller buttons */
|
||||
TotalButtonCount
|
||||
};
|
||||
|
||||
enum class EOculusTouchCapacitiveAxes
|
||||
{
|
||||
Thumbstick,
|
||||
Trigger,
|
||||
XA,
|
||||
YB,
|
||||
IndexPointing,
|
||||
ThumbUp,
|
||||
ThumbRest,
|
||||
|
||||
/** Total number of capacitive axes */
|
||||
TotalAxisCount
|
||||
};
|
||||
|
||||
enum class EOculusHandButton
|
||||
{
|
||||
Thumb,
|
||||
Index,
|
||||
Middle,
|
||||
Ring,
|
||||
Pinky,
|
||||
System,
|
||||
Menu,
|
||||
TotalButtonCount
|
||||
};
|
||||
|
||||
enum class EOculusHandAxes
|
||||
{
|
||||
Thumb,
|
||||
Index,
|
||||
Middle,
|
||||
Ring,
|
||||
Pinky,
|
||||
TotalAxisCount
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusKey
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusKey
|
||||
{
|
||||
static const FKey OculusTouch_Left_Thumbstick;
|
||||
static const FKey OculusTouch_Left_Trigger;
|
||||
static const FKey OculusTouch_Left_FaceButton1; // X or A
|
||||
static const FKey OculusTouch_Left_FaceButton2; // Y or B
|
||||
static const FKey OculusTouch_Left_IndexPointing;
|
||||
static const FKey OculusTouch_Left_ThumbUp;
|
||||
static const FKey OculusTouch_Left_ThumbRest;
|
||||
static const FKey OculusTouch_Left_ThumbRest_Force;
|
||||
static const FKey OculusTouch_Left_Stylus_Force;
|
||||
static const FKey OculusTouch_Left_IndexTrigger_Curl;
|
||||
static const FKey OculusTouch_Left_IndexTrigger_Slide;
|
||||
static const FKey OculusTouch_Left_IndexTrigger_Force;
|
||||
|
||||
static const FKey OculusTouch_Right_Thumbstick;
|
||||
static const FKey OculusTouch_Right_Trigger;
|
||||
static const FKey OculusTouch_Right_FaceButton1; // X or A
|
||||
static const FKey OculusTouch_Right_FaceButton2; // Y or B
|
||||
static const FKey OculusTouch_Right_IndexPointing;
|
||||
static const FKey OculusTouch_Right_ThumbUp;
|
||||
static const FKey OculusTouch_Right_ThumbRest;
|
||||
static const FKey OculusTouch_Right_ThumbRest_Force;
|
||||
static const FKey OculusTouch_Right_Stylus_Force;
|
||||
static const FKey OculusTouch_Right_IndexTrigger_Curl;
|
||||
static const FKey OculusTouch_Right_IndexTrigger_Slide;
|
||||
static const FKey OculusTouch_Right_IndexTrigger_Force;
|
||||
|
||||
static const FKey OculusRemote_DPad_Up;
|
||||
static const FKey OculusRemote_DPad_Down;
|
||||
static const FKey OculusRemote_DPad_Left;
|
||||
static const FKey OculusRemote_DPad_Right;
|
||||
|
||||
static const FKey OculusRemote_Enter;
|
||||
static const FKey OculusRemote_Back;
|
||||
|
||||
static const FKey OculusRemote_VolumeUp;
|
||||
static const FKey OculusRemote_VolumeDown;
|
||||
static const FKey OculusRemote_Home;
|
||||
|
||||
static const FKey OculusHand_Left_ThumbPinch;
|
||||
static const FKey OculusHand_Left_IndexPinch;
|
||||
static const FKey OculusHand_Left_MiddlePinch;
|
||||
static const FKey OculusHand_Left_RingPinch;
|
||||
static const FKey OculusHand_Left_PinkyPinch;
|
||||
|
||||
static const FKey OculusHand_Right_ThumbPinch;
|
||||
static const FKey OculusHand_Right_IndexPinch;
|
||||
static const FKey OculusHand_Right_MiddlePinch;
|
||||
static const FKey OculusHand_Right_RingPinch;
|
||||
static const FKey OculusHand_Right_PinkyPinch;
|
||||
|
||||
static const FKey OculusHand_Left_SystemGesture;
|
||||
static const FKey OculusHand_Right_SystemGesture;
|
||||
|
||||
static const FKey OculusHand_Left_ThumbPinchStrength;
|
||||
static const FKey OculusHand_Left_IndexPinchStrength;
|
||||
static const FKey OculusHand_Left_MiddlePinchStrength;
|
||||
static const FKey OculusHand_Left_RingPinchStrength;
|
||||
static const FKey OculusHand_Left_PinkyPinchStrength;
|
||||
|
||||
static const FKey OculusHand_Right_ThumbPinchStrength;
|
||||
static const FKey OculusHand_Right_IndexPinchStrength;
|
||||
static const FKey OculusHand_Right_MiddlePinchStrength;
|
||||
static const FKey OculusHand_Right_RingPinchStrength;
|
||||
static const FKey OculusHand_Right_PinkyPinchStrength;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusKeyNames
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusKeyNames
|
||||
{
|
||||
typedef FName Type;
|
||||
|
||||
static const FName OculusTouch_Left_Thumbstick;
|
||||
static const FName OculusTouch_Left_Trigger;
|
||||
static const FName OculusTouch_Left_FaceButton1; // X or A
|
||||
static const FName OculusTouch_Left_FaceButton2; // Y or B
|
||||
static const FName OculusTouch_Left_IndexPointing;
|
||||
static const FName OculusTouch_Left_ThumbUp;
|
||||
static const FName OculusTouch_Left_ThumbRest;
|
||||
|
||||
static const FName OculusTouch_Right_Thumbstick;
|
||||
static const FName OculusTouch_Right_Trigger;
|
||||
static const FName OculusTouch_Right_FaceButton1; // X or A
|
||||
static const FName OculusTouch_Right_FaceButton2; // Y or B
|
||||
static const FName OculusTouch_Right_IndexPointing;
|
||||
static const FName OculusTouch_Right_ThumbUp;
|
||||
static const FName OculusTouch_Right_ThumbRest;
|
||||
|
||||
static const FName OculusRemote_DPad_Up;
|
||||
static const FName OculusRemote_DPad_Down;
|
||||
static const FName OculusRemote_DPad_Left;
|
||||
static const FName OculusRemote_DPad_Right;
|
||||
|
||||
static const FName OculusRemote_Enter;
|
||||
static const FName OculusRemote_Back;
|
||||
|
||||
static const FName OculusRemote_VolumeUp;
|
||||
static const FName OculusRemote_VolumeDown;
|
||||
static const FName OculusRemote_Home;
|
||||
|
||||
static const FName OculusHand_Left_ThumbPinch;
|
||||
static const FName OculusHand_Left_IndexPinch;
|
||||
static const FName OculusHand_Left_MiddlePinch;
|
||||
static const FName OculusHand_Left_RingPinch;
|
||||
static const FName OculusHand_Left_PinkyPinch;
|
||||
|
||||
static const FName OculusHand_Right_ThumbPinch;
|
||||
static const FName OculusHand_Right_IndexPinch;
|
||||
static const FName OculusHand_Right_MiddlePinch;
|
||||
static const FName OculusHand_Right_RingPinch;
|
||||
static const FName OculusHand_Right_PinkyPinch;
|
||||
|
||||
static const FName OculusHand_Left_SystemGesture;
|
||||
static const FName OculusHand_Right_SystemGesture;
|
||||
|
||||
static const FName OculusHand_Left_ThumbPinchStrength;
|
||||
static const FName OculusHand_Left_IndexPinchStrength;
|
||||
static const FName OculusHand_Left_MiddlePinchStrength;
|
||||
static const FName OculusHand_Left_RingPinchStrength;
|
||||
static const FName OculusHand_Left_PinkyPinchStrength;
|
||||
|
||||
static const FName OculusHand_Right_ThumbPinchStrength;
|
||||
static const FName OculusHand_Right_IndexPinchStrength;
|
||||
static const FName OculusHand_Right_MiddlePinchStrength;
|
||||
static const FName OculusHand_Right_RingPinchStrength;
|
||||
static const FName OculusHand_Right_PinkyPinchStrength;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusButtonState - Digital button state
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusButtonState
|
||||
{
|
||||
/** The Unreal button this maps to. Different depending on whether this is the Left or Right hand controller */
|
||||
FName Key;
|
||||
|
||||
/** The Unreal button this maps to. Different depending on whether this is the Left or Right hand controller */
|
||||
FName EmulatedKey;
|
||||
|
||||
/** Whether we're pressed or not. While pressed, we will generate repeat presses on a timer */
|
||||
bool bIsPressed;
|
||||
|
||||
/** Next time a repeat event should be generated for each button */
|
||||
double NextRepeatTime;
|
||||
|
||||
/** Default constructor that just sets sensible defaults */
|
||||
FOculusButtonState()
|
||||
: Key(NAME_None), EmulatedKey(NAME_None), bIsPressed(false), NextRepeatTime(0.0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusTouchCapacitiveState - Capacitive Axis State
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusAxisState
|
||||
{
|
||||
/** The axis that this button state maps to */
|
||||
FName Axis;
|
||||
|
||||
/** How close the finger is to this button, from 0.f to 1.f */
|
||||
float State;
|
||||
|
||||
FOculusAxisState()
|
||||
: Axis(NAME_None)
|
||||
, State(0.f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct FOculusControllerState
|
||||
{
|
||||
/** True if the device is connected, otherwise false */
|
||||
bool bIsConnected;
|
||||
|
||||
/** True if position is being tracked, otherwise false */
|
||||
bool bIsPositionTracked;
|
||||
|
||||
/** True if position is valid (tracked or estimated), otherwise false */
|
||||
bool bIsPositionValid;
|
||||
|
||||
/** True if orientation is being tracked, otherwise false */
|
||||
bool bIsOrientationTracked;
|
||||
|
||||
/** True if orientation is valid (tracked or estimated), otherwise false */
|
||||
bool bIsOrientationValid;
|
||||
|
||||
FOculusControllerState()
|
||||
: bIsConnected(false), bIsPositionTracked(false), bIsPositionValid(false), bIsOrientationTracked(false), bIsOrientationValid(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusTouchControllerState - Input state for an Oculus motion controller
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusTouchControllerState : FOculusControllerState
|
||||
{
|
||||
/** Analog trigger */
|
||||
float TriggerAxis;
|
||||
|
||||
/** Grip trigger */
|
||||
float GripAxis;
|
||||
|
||||
/** Thumbstick */
|
||||
FVector2D ThumbstickAxes;
|
||||
|
||||
/** Thumbstick */
|
||||
FVector2D TouchpadAxes;
|
||||
|
||||
/** Button states */
|
||||
FOculusButtonState Buttons[(int32)EOculusTouchControllerButton::TotalButtonCount];
|
||||
|
||||
/** Capacitive Touch axes */
|
||||
FOculusAxisState CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::TotalAxisCount];
|
||||
|
||||
/** Thumb Rest Force **/
|
||||
float ThumbRestForce;
|
||||
|
||||
/** Stylus tip**/
|
||||
float StylusForce;
|
||||
|
||||
/** Index trigger Curl**/
|
||||
float IndexTriggerCurl;
|
||||
|
||||
/** Index trigger Slide**/
|
||||
float IndexTriggerSlide;
|
||||
|
||||
/** Second stage Index trigger force **/
|
||||
float IndexTriggerForce;
|
||||
|
||||
/** Whether or not we're playing a haptic effect. If true, force feedback calls will be early-outed in favor of the haptic effect */
|
||||
bool bPlayingHapticEffect;
|
||||
|
||||
/** Haptic frequency (zero to disable) */
|
||||
float HapticFrequency;
|
||||
|
||||
/** Haptic amplitude (zero to disable) */
|
||||
float HapticAmplitude;
|
||||
|
||||
/** Force feedback haptic frequency (zero to disable) */
|
||||
float ForceFeedbackHapticFrequency;
|
||||
|
||||
/** Force feedback haptic amplitude (zero to disable) */
|
||||
float ForceFeedbackHapticAmplitude;
|
||||
|
||||
/** Number of times that controller was recentered (for mobile controllers) */
|
||||
int RecenterCount;
|
||||
|
||||
public:
|
||||
FHapticFeedbackBuffer ResampledHapticBuffer;
|
||||
void ResampleHapticBufferData(const FHapticFeedbackBuffer& HapticBuffer, TMap<const uint8*, TSharedPtr<TArray<uint8>>>& ResampledRawDataCache);
|
||||
|
||||
/** Explicit constructor sets up sensible defaults */
|
||||
FOculusTouchControllerState(const EControllerHand Hand)
|
||||
: TriggerAxis(0.0f), GripAxis(0.0f), ThumbstickAxes(FVector2D::ZeroVector), bPlayingHapticEffect(false), HapticFrequency(0.0f), HapticAmplitude(0.0f), ForceFeedbackHapticFrequency(0.0f), ForceFeedbackHapticAmplitude(0.0f), RecenterCount(0)
|
||||
{
|
||||
for (FOculusButtonState& Button : Buttons)
|
||||
{
|
||||
Button.bIsPressed = false;
|
||||
Button.NextRepeatTime = 0.0;
|
||||
}
|
||||
|
||||
Buttons[(int32)EOculusTouchControllerButton::Trigger].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Trigger_Click.GetFName() : EKeys::OculusTouch_Right_Trigger_Click.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Grip].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Grip_Click.GetFName() : EKeys::OculusTouch_Right_Grip_Click.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Click.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Click.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::XA].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_X_Click.GetFName() : EKeys::OculusTouch_Right_A_Click.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::YB].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Y_Click.GetFName() : EKeys::OculusTouch_Right_B_Click.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Up].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Up.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Up.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Down].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Down.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Down.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Left].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Left.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Left.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Right].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Right.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Right.GetFName();
|
||||
|
||||
Buttons[(int32)EOculusTouchControllerButton::Menu].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Menu_Click.GetFName() : FName("OculusTouch_Right_System_Click");
|
||||
|
||||
Buttons[(int32)EOculusTouchControllerButton::Thumbstick_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Thumbstick_Touch.GetFName() : EKeys::OculusTouch_Right_Thumbstick_Touch.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::Trigger_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Trigger_Touch.GetFName() : EKeys::OculusTouch_Right_Trigger_Touch.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::XA_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_X_Touch.GetFName() : EKeys::OculusTouch_Right_A_Touch.GetFName();
|
||||
Buttons[(int32)EOculusTouchControllerButton::YB_Touch].Key = (Hand == EControllerHand::Left) ? EKeys::OculusTouch_Left_Y_Touch.GetFName() : EKeys::OculusTouch_Right_B_Touch.GetFName();
|
||||
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::Thumbstick].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_Thumbstick : FOculusKeyNames::OculusTouch_Right_Thumbstick;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::Trigger].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_Trigger : FOculusKeyNames::OculusTouch_Right_Trigger;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::XA].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_FaceButton1 : FOculusKeyNames::OculusTouch_Right_FaceButton1;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::YB].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_FaceButton2 : FOculusKeyNames::OculusTouch_Right_FaceButton2;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::IndexPointing].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_IndexPointing : FOculusKeyNames::OculusTouch_Right_IndexPointing;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::ThumbUp].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_ThumbUp : FOculusKeyNames::OculusTouch_Right_ThumbUp;
|
||||
CapacitiveAxes[(int32)EOculusTouchCapacitiveAxes::ThumbRest].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusTouch_Left_ThumbRest : FOculusKeyNames::OculusTouch_Right_ThumbRest;
|
||||
}
|
||||
|
||||
/** Default constructor does nothing. Don't use it. This only exists because we cannot initialize an array of objects with no default constructor on non-C++ 11 compliant compilers (VS 2013) */
|
||||
FOculusTouchControllerState()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusHandControllerState - Input state for an Oculus Hands
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusHandControllerState : FOculusControllerState
|
||||
{
|
||||
/** True if the pointer pose for hands is valid */
|
||||
bool bIsPointerPoseValid;
|
||||
|
||||
/** True if the current hand is the dominant hand */
|
||||
bool bIsDominantHand;
|
||||
|
||||
/** Scale of the hand */
|
||||
float HandScale;
|
||||
|
||||
/** Pose of the pointer */
|
||||
FTransform PointerPose;
|
||||
|
||||
/** Tracking confidence of hand tracking */
|
||||
EOculusXRTrackingConfidence TrackingConfidence;
|
||||
|
||||
/** Finger Pinch States **/
|
||||
FOculusButtonState HandButtons[(int32)EOculusHandButton::TotalButtonCount];
|
||||
|
||||
/** Finger Pinch Strength States **/
|
||||
FOculusAxisState HandAxes[(int32)EOculusHandAxes::TotalAxisCount];
|
||||
|
||||
/** Finger Confidences **/
|
||||
EOculusXRTrackingConfidence FingerConfidences[(int32)EOculusHandAxes::TotalAxisCount] = {};
|
||||
|
||||
FQuat BoneRotations[(int32)EOculusXRBone::Bone_Max];
|
||||
|
||||
FOculusHandControllerState(const EControllerHand Hand)
|
||||
{
|
||||
TrackingConfidence = EOculusXRTrackingConfidence::Low;
|
||||
bIsPointerPoseValid = false;
|
||||
bIsDominantHand = false;
|
||||
HandScale = 0.0f;
|
||||
PointerPose = FTransform::Identity;
|
||||
|
||||
for (FOculusButtonState& Button : HandButtons)
|
||||
{
|
||||
Button.bIsPressed = false;
|
||||
Button.NextRepeatTime = 0.0;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < (int32)EOculusXRBone::Bone_Max; i++)
|
||||
{
|
||||
BoneRotations[i] = FQuat::Identity;
|
||||
}
|
||||
|
||||
HandButtons[(int32)EOculusHandButton::Thumb].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_ThumbPinch : FOculusKeyNames::OculusHand_Right_ThumbPinch;
|
||||
HandButtons[(int32)EOculusHandButton::Index].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_IndexPinch : FOculusKeyNames::OculusHand_Right_IndexPinch;
|
||||
HandButtons[(int32)EOculusHandButton::Middle].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_MiddlePinch : FOculusKeyNames::OculusHand_Right_MiddlePinch;
|
||||
HandButtons[(int32)EOculusHandButton::Ring].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_RingPinch : FOculusKeyNames::OculusHand_Right_RingPinch;
|
||||
HandButtons[(int32)EOculusHandButton::Pinky].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_PinkyPinch : FOculusKeyNames::OculusHand_Right_PinkyPinch;
|
||||
HandButtons[(int32)EOculusHandButton::System].Key = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_SystemGesture : FOculusKeyNames::OculusHand_Right_SystemGesture;
|
||||
HandButtons[(int32)EOculusHandButton::Menu].Key = (Hand == EControllerHand::Left) ? FGamepadKeyNames::SpecialLeft : FGamepadKeyNames::SpecialRight;
|
||||
|
||||
HandAxes[(int32)EOculusHandAxes::Thumb].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_ThumbPinchStrength : FOculusKeyNames::OculusHand_Right_ThumbPinchStrength;
|
||||
HandAxes[(int32)EOculusHandAxes::Index].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_IndexPinchStrength : FOculusKeyNames::OculusHand_Right_IndexPinchStrength;
|
||||
HandAxes[(int32)EOculusHandAxes::Middle].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_MiddlePinchStrength : FOculusKeyNames::OculusHand_Right_MiddlePinchStrength;
|
||||
HandAxes[(int32)EOculusHandAxes::Ring].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_RingPinchStrength : FOculusKeyNames::OculusHand_Right_RingPinchStrength;
|
||||
HandAxes[(int32)EOculusHandAxes::Pinky].Axis = (Hand == EControllerHand::Left) ? FOculusKeyNames::OculusHand_Left_PinkyPinchStrength : FOculusKeyNames::OculusHand_Right_PinkyPinchStrength;
|
||||
}
|
||||
|
||||
FOculusHandControllerState()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusRemoteControllerState
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusRemoteControllerState
|
||||
{
|
||||
/** Button states */
|
||||
FOculusButtonState Buttons[(int32)EOculusRemoteControllerButton::TotalButtonCount];
|
||||
|
||||
FOculusRemoteControllerState()
|
||||
{
|
||||
for (FOculusButtonState& Button : Buttons)
|
||||
{
|
||||
Button.bIsPressed = false;
|
||||
Button.NextRepeatTime = 0.0;
|
||||
}
|
||||
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Up].Key = FOculusKeyNames::OculusRemote_DPad_Up;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Down].Key = FOculusKeyNames::OculusRemote_DPad_Down;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Left].Key = FOculusKeyNames::OculusRemote_DPad_Left;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Right].Key = FOculusKeyNames::OculusRemote_DPad_Right;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::Enter].Key = FOculusKeyNames::OculusRemote_Enter;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::Back].Key = FOculusKeyNames::OculusRemote_Back;
|
||||
|
||||
Buttons[(int32)EOculusRemoteControllerButton::VolumeUp].Key = FOculusKeyNames::OculusRemote_VolumeUp;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::VolumeDown].Key = FOculusKeyNames::OculusRemote_VolumeDown;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::Home].Key = FOculusKeyNames::OculusRemote_Home;
|
||||
}
|
||||
|
||||
void MapKeysToGamepad()
|
||||
{
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Up].EmulatedKey = FGamepadKeyNames::DPadUp;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Down].EmulatedKey = FGamepadKeyNames::DPadDown;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Left].EmulatedKey = FGamepadKeyNames::DPadLeft;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::DPad_Right].EmulatedKey = FGamepadKeyNames::DPadRight;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::Enter].EmulatedKey = FGamepadKeyNames::SpecialRight;
|
||||
Buttons[(int32)EOculusRemoteControllerButton::Back].EmulatedKey = FGamepadKeyNames::SpecialLeft;
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// FOculusControllerPair - A pair of Oculus controllers, hand/touch, one for either hand
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
struct FOculusControllerPair
|
||||
{
|
||||
/** The input device ID for this oculus controller */
|
||||
FInputDeviceId DeviceId;
|
||||
|
||||
/** Current device state for either hand */
|
||||
FOculusTouchControllerState TouchControllerStates[2];
|
||||
|
||||
FOculusHandControllerState HandControllerStates[2];
|
||||
|
||||
FOculusControllerPair()
|
||||
: DeviceId(INPUTDEVICEID_NONE), TouchControllerStates(), HandControllerStates()
|
||||
{
|
||||
TouchControllerStates[(int32)EControllerHand::Left] = FOculusTouchControllerState(EControllerHand::Left);
|
||||
TouchControllerStates[(int32)EControllerHand::Right] = FOculusTouchControllerState(EControllerHand::Right);
|
||||
|
||||
HandControllerStates[(int32)EControllerHand::Left] = FOculusHandControllerState(EControllerHand::Left);
|
||||
HandControllerStates[(int32)EControllerHand::Right] = FOculusHandControllerState(EControllerHand::Right);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace OculusXRInput
|
||||
|
||||
#endif // OCULUS_INPUT_SUPPORTED_PLATFORMS
|
||||
@@ -0,0 +1,54 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "IInputDeviceModule.h"
|
||||
|
||||
#define OCULUS_INPUT_SUPPORTED_PLATFORMS (PLATFORM_WINDOWS && WINVER > 0x0502) || (PLATFORM_ANDROID_ARM || PLATFORM_ANDROID_ARM64)
|
||||
|
||||
/**
|
||||
* The public interface to this module. In most cases, this interface is only public to sibling modules
|
||||
* within this plugin.
|
||||
*/
|
||||
class IOculusXRInputModule : public IInputDeviceModule
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline IOculusXRInputModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<IOculusXRInputModule>("OculusXRInput");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static inline bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("OculusXRInput");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of Touch controllers that are active, so that games that require them can check to make sure they're present
|
||||
*
|
||||
* @return The number of Touch controllers that are active (but not necessarily tracked)
|
||||
*/
|
||||
virtual uint32 GetNumberOfTouchControllers() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the number of hands that are active, so that games that require them can check to make sure they're present
|
||||
*
|
||||
* @return The number of Hands that are active (but not necessarily tracked)
|
||||
*/
|
||||
virtual uint32 GetNumberOfHandControllers() const = 0;
|
||||
|
||||
virtual TSharedPtr<IInputDevice> GetInputDevice() const = 0;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
// A class to render the currently connected controller.
|
||||
// Similar to how hands are tracked.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
#include "OculusXRFunctionLibrary.h"
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include <Engine/StreamableManager.h>
|
||||
|
||||
// Must always be the last include.
|
||||
#include "OculusXRControllerComponent.generated.h"
|
||||
|
||||
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusHand)
|
||||
class UOculusXRControllerComponent : public UStaticMeshComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UOculusXRControllerComponent();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/** The skeleton that will be loaded */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Properties")
|
||||
EOculusXRSide SkeletonType;
|
||||
|
||||
/** Should this controller be rendered when using controller driven hand poses */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Properties")
|
||||
bool RenderWhenUsingControllerDrivenHands;
|
||||
|
||||
private:
|
||||
enum MeshLoadingState
|
||||
{
|
||||
None,
|
||||
Loading,
|
||||
Loaded
|
||||
};
|
||||
|
||||
UStaticMesh* _runtimeMesh;
|
||||
MeshLoadingState _meshLoadingState;
|
||||
TSharedPtr<FStreamableHandle> _loadAssetHandle;
|
||||
FStreamableManager _streamableManager;
|
||||
EOculusXRControllerType _controllerType;
|
||||
FSoftObjectPath _runtimeMeshPath;
|
||||
EOculusXRControllerDrivenHandPoseTypes _cachedControllerHandType;
|
||||
|
||||
void InitializeMesh();
|
||||
void MeshLoaded();
|
||||
EOculusXRControllerType GetControllerType();
|
||||
|
||||
const FVector PositionOffsets
|
||||
[EOculusXRSideCount]
|
||||
[EOculusXRControllerDrivenHandPoseTypesCount];
|
||||
const FVector RotationOffsets
|
||||
[EOculusXRSideCount]
|
||||
[EOculusXRControllerDrivenHandPoseTypesCount];
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRInputFunctionLibrary.h"
|
||||
#include "Components/PoseableMeshComponent.h"
|
||||
#include "OculusXRHandComponent.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRConfidenceBehavior : uint8
|
||||
{
|
||||
None,
|
||||
HideActor
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRSystemGestureBehavior : uint8
|
||||
{
|
||||
None,
|
||||
SwapMaterial
|
||||
};
|
||||
|
||||
static const FQuat HandRootFixupRotation = FQuat(-0.5f, -0.5f, 0.5f, 0.5f);
|
||||
|
||||
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusHand)
|
||||
class OCULUSXRINPUT_API UOculusXRHandComponent : public UPoseableMeshComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/** The hand skeleton that will be loaded */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
EOculusXRHandType SkeletonType;
|
||||
|
||||
/** The hand mesh that will be applied to the skeleton */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
EOculusXRHandType MeshType;
|
||||
|
||||
/** Behavior for when hand tracking loses high confidence tracking */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
EOculusXRConfidenceBehavior ConfidenceBehavior = EOculusXRConfidenceBehavior::HideActor;
|
||||
|
||||
/** Behavior for when the system gesture is actived */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
EOculusXRSystemGestureBehavior SystemGestureBehavior = EOculusXRSystemGestureBehavior::SwapMaterial;
|
||||
|
||||
/** Material that gets applied to the hands when the system gesture is active */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
class UMaterialInterface* SystemGestureMaterial;
|
||||
|
||||
/** Whether or not to initialize physics capsules on the skeletal mesh */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
bool bInitializePhysics;
|
||||
|
||||
/** Whether or not the hand scale should update based on values from the runtime to match the users hand scale */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
bool bUpdateHandScale;
|
||||
|
||||
/** Material override for the runtime skeletal mesh */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "HandProperties")
|
||||
class UMaterialInterface* MaterialOverride;
|
||||
|
||||
/** Bone mapping for custom hand skeletal meshes */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CustomSkeletalMesh")
|
||||
TMap<EOculusXRBone, FName> BoneNameMappings;
|
||||
|
||||
/** List of capsule colliders created for the skeletal mesh */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "HandProperties")
|
||||
TArray<FOculusXRCapsuleCollider> CollisionCapsules;
|
||||
|
||||
/** Whether or not the runtime skeletal mesh has been loaded and initialized */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "HandProperties")
|
||||
bool bSkeletalMeshInitialized = false;
|
||||
|
||||
protected:
|
||||
virtual void SystemGesturePressed();
|
||||
virtual void SystemGestureReleased();
|
||||
|
||||
private:
|
||||
/** Whether or not this component has authority within the frame */
|
||||
bool bHasAuthority;
|
||||
|
||||
/** Whether or not a custom hand mesh is being used */
|
||||
bool bCustomHandMesh = false;
|
||||
|
||||
/** Whether or not the physics capsules have been initialized */
|
||||
bool bInitializedPhysics = false;
|
||||
|
||||
USkeletalMesh* RuntimeSkeletalMesh;
|
||||
|
||||
UMaterialInterface* CachedBaseMaterial;
|
||||
|
||||
void InitializeSkeletalMesh();
|
||||
|
||||
void UpdateBonePose();
|
||||
};
|
||||
@@ -0,0 +1,350 @@
|
||||
// @lint-ignore-every LICENSELINT
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "OculusXRInputFunctionLibrary.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRHandType : uint8
|
||||
{
|
||||
None,
|
||||
HandLeft,
|
||||
HandRight,
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRSide : uint8
|
||||
{
|
||||
None = 0,
|
||||
Left = 1,
|
||||
Right = 2,
|
||||
};
|
||||
|
||||
const int EOculusXRSideCount = 3;
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRTrackingConfidence : uint8
|
||||
{
|
||||
Low,
|
||||
High
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRFinger : uint8
|
||||
{
|
||||
Thumb,
|
||||
Index,
|
||||
Middle,
|
||||
Ring,
|
||||
Pinky,
|
||||
Invalid
|
||||
};
|
||||
|
||||
/**
|
||||
* EOculusXRBone is enum representing the Bone Ids that come from the Oculus Runtime.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRBone : uint8
|
||||
{
|
||||
Wrist_Root UMETA(DisplayName = "Wrist Root"),
|
||||
Hand_Start = Wrist_Root UMETA(DisplayName = "Hand Start"),
|
||||
Forearm_Stub UMETA(DisplayName = "Forearm Stub"),
|
||||
Thumb_0 UMETA(DisplayName = "Thumb0"),
|
||||
Thumb_1 UMETA(DisplayName = "Thumb1"),
|
||||
Thumb_2 UMETA(DisplayName = "Thumb2"),
|
||||
Thumb_3 UMETA(DisplayName = "Thumb3"),
|
||||
Index_1 UMETA(DisplayName = "Index1"),
|
||||
Index_2 UMETA(DisplayName = "Index2"),
|
||||
Index_3 UMETA(DisplayName = "Index3"),
|
||||
Middle_1 UMETA(DisplayName = "Middle1"),
|
||||
Middle_2 UMETA(DisplayName = "Middle2"),
|
||||
Middle_3 UMETA(DisplayName = "Middle3"),
|
||||
Ring_1 UMETA(DisplayName = "Ring1"),
|
||||
Ring_2 UMETA(DisplayName = "Ring2"),
|
||||
Ring_3 UMETA(DisplayName = "Ring3"),
|
||||
Pinky_0 UMETA(DisplayName = "Pinky0"),
|
||||
Pinky_1 UMETA(DisplayName = "Pinky1"),
|
||||
Pinky_2 UMETA(DisplayName = "Pinky2"),
|
||||
Pinky_3 UMETA(DisplayName = "Pinky3"),
|
||||
Thumb_Tip UMETA(DisplayName = "Thumb Tip"),
|
||||
Max_Skinnable = Thumb_Tip UMETA(DisplayName = "Max Skinnable"),
|
||||
Index_Tip UMETA(DisplayName = "Index Tip"),
|
||||
Middle_Tip UMETA(DisplayName = "Middle Tip"),
|
||||
Ring_Tip UMETA(DisplayName = "Ring Tip"),
|
||||
Pinky_Tip UMETA(DisplayName = "Pinky Tip"),
|
||||
Hand_End UMETA(DisplayName = "Hand End"),
|
||||
Bone_Max = Hand_End UMETA(DisplayName = "Hand Max"),
|
||||
Invalid UMETA(DisplayName = "Invalid")
|
||||
};
|
||||
|
||||
/** Defines the haptics location of controller hands for tracking. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRHandHapticsLocation : uint8
|
||||
{
|
||||
Hand = 0, // Haptics is applied to the whole controller
|
||||
Thumb, // Haptics is applied to the thumb finger location
|
||||
Index, // Haptics is applied to the index finger location
|
||||
|
||||
HandHapticsLocation_Count UMETA(Hidden, DisplayName = "<INVALID>"),
|
||||
};
|
||||
|
||||
/** Define how a controllers button touches will be used to generate a hand pose. */
|
||||
UENUM(BlueprintType)
|
||||
enum class EOculusXRControllerDrivenHandPoseTypes : uint8
|
||||
{
|
||||
None = 0, // Controllers do not generate any hand poses.
|
||||
Natural, // Controller button inputs will be used to generate a normal hand pose.
|
||||
Controller, // Controller button inputs will be used to generate a hand pose holding a controller.
|
||||
};
|
||||
|
||||
const int EOculusXRControllerDrivenHandPoseTypesCount = 3;
|
||||
|
||||
struct FOculusXRHapticsDesc
|
||||
{
|
||||
FOculusXRHapticsDesc(
|
||||
EOculusXRHandHapticsLocation ILocation = EOculusXRHandHapticsLocation::Hand,
|
||||
bool bIAppend = false)
|
||||
: Location(ILocation), bAppend(bIAppend)
|
||||
{
|
||||
}
|
||||
|
||||
void Restart()
|
||||
{
|
||||
Location = EOculusXRHandHapticsLocation::Hand;
|
||||
bAppend = false;
|
||||
}
|
||||
EOculusXRHandHapticsLocation Location;
|
||||
bool bAppend;
|
||||
};
|
||||
|
||||
/**
|
||||
* FOculusXRCapsuleCollider is a struct that contains information on the physics/collider capsules created by the runtime for hands.
|
||||
*
|
||||
* @var Capsule The UCapsuleComponent that is the collision capsule on the bone. Use this to register for overlap/collision events
|
||||
* @var BoneIndex The Bone that this collision capsule is parented to. Corresponds to the EOculusXRBone enum.
|
||||
*
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct OCULUSXRINPUT_API FOculusXRCapsuleCollider
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "OculusLibrary|HandTracking")
|
||||
UCapsuleComponent* Capsule = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "OculusLibrary|HandTracking")
|
||||
EOculusXRBone BoneId = EOculusXRBone::Wrist_Root;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class OCULUSXRINPUT_API UOculusXRInputFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
|
||||
static EOculusXRFinger ConvertBoneToFinger(const EOculusXRBone Bone);
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE_FourParams(FHandMovementFilterDelegate, EControllerHand, FVector*, FRotator*, bool*);
|
||||
static FHandMovementFilterDelegate HandMovementFilter; /// Called to modify Hand position and orientation whenever it is queried
|
||||
|
||||
/**
|
||||
* Creates a new runtime hand skeletal mesh.
|
||||
*
|
||||
* @param HandSkeletalMesh (out) Skeletal Mesh object that will be used for the runtime hand mesh
|
||||
* @param SkeletonType (in) The skeleton type that will be used for generating the hand bones
|
||||
* @param MeshType (in) The mesh type that will be used for generating the hand mesh
|
||||
* @param WorldTometers (in) Optional change to the world to meters conversion value
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
|
||||
static bool GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, EOculusXRHandType SkeletonType, EOculusXRHandType MeshType, const float WorldToMeters = 100.0f);
|
||||
|
||||
/**
|
||||
* Initializes physics capsules for collision and physics on the runtime mesh
|
||||
*
|
||||
* @param SkeletonType (in) The skeleton type that will be used to generated the capsules
|
||||
* @param HandComponent (in) The skinned mesh component that the capsules will be attached to
|
||||
* @param WorldTometers (in) Optional change to the world to meters conversion value
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|HandTracking")
|
||||
static TArray<FOculusXRCapsuleCollider> InitializeHandPhysics(EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters = 100.0f);
|
||||
|
||||
/**
|
||||
* Get the rotation of a specific bone
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get the rotations from
|
||||
* @param BoneId (in) The specific bone to get the rotation from
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static FQuat GetBoneRotation(const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the pointer pose
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get the pointer pose from
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static FTransform GetPointerPose(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Check if the pointer pose is a valid pose
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get the pointer status from
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static bool IsPointerPoseValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the tracking confidence of the hand
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get tracking confidence of
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static EOculusXRTrackingConfidence GetTrackingConfidence(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the tracking confidence of a finger
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get tracking confidence of
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
* @param Finger (in) The finger to get tracking confidence of
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static EOculusXRTrackingConfidence GetFingerTrackingConfidence(const EOculusXRHandType DeviceHand, const EOculusXRFinger Finger, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the scale of the hand
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get scale of
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static float GetHandScale(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the user's dominant hand
|
||||
*
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static EOculusXRHandType GetDominantHand(const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Check if hand tracking is enabled currently
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static bool IsHandTrackingEnabled();
|
||||
|
||||
/**
|
||||
* Check if the hand position is valid
|
||||
*
|
||||
* @param DeviceHand (in) The hand to get the position from
|
||||
* @param ControllerIndex (in) Optional different controller index
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static bool IsHandPositionValid(const EOculusXRHandType DeviceHand, const int32 ControllerIndex = 0);
|
||||
|
||||
/**
|
||||
* Get the bone name from the bone index
|
||||
*
|
||||
* @param BoneIndex (in) Bone index to get the name of
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "OculusLibrary|HandTracking")
|
||||
static FString GetBoneName(EOculusXRBone BoneId);
|
||||
|
||||
/**
|
||||
* Play a haptic feedback curve on the player's controller with location support.
|
||||
* The curve data will be sampled and sent to controller to vibrate a specific location at each frame.
|
||||
* @param HapticEffect The haptic effect to play
|
||||
* @param Hand Which hand to play the effect on
|
||||
* @param Location Which hand location to play the effect on
|
||||
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void PlayCurveHapticEffect(class UHapticFeedbackEffect_Curve* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand, float Scale = 1.f, bool bLoop = false);
|
||||
|
||||
/**
|
||||
* Play a haptic feedback buffer on the player's controller with location support.
|
||||
* In each frame, the buffer data will be sampled and the individual sampled data will be sent to controller to vibrate a specific location.
|
||||
* @param HapticEffect The haptic effect to play
|
||||
* @param Hand Which hand to play the effect on
|
||||
* @param Location Which hand location to play the effect on
|
||||
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void PlayBufferHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand, float Scale = 1.f, bool bLoop = false);
|
||||
|
||||
/**
|
||||
* Play a haptic feedback buffer on the player's controller.
|
||||
* All buffer data will be sent to controller together in one frame.
|
||||
* Data duration should be no greater than controller's maximum haptics duration which can be queried with GetMaxHapticDuration.
|
||||
* @param HapticEffect The haptic effect to play
|
||||
* @param Hand Which hand to play the effect on
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void PlayAmplitudeEnvelopeHapticEffect(class UHapticFeedbackEffect_Buffer* HapticEffect, EControllerHand Hand);
|
||||
|
||||
/**
|
||||
* Play a haptic feedback soundwave on the player's controller.
|
||||
* In each frame, the soundwave data will be split into a batch of data and sent to controller.
|
||||
* The data duration of each frame is equal to controller's maximum haptics duration which can be queried with GetMaxHapticDuration.
|
||||
* @param HapticEffect The haptic effect to play
|
||||
* @param Hand Which hand to play the effect on
|
||||
* @param bAppend False: any existing samples will be cleared and a new haptic effect will begin; True: samples will be appended to the currently playing effect
|
||||
* @param Scale Scale between 0.0 and 1.0 on the intensity of playback
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void PlaySoundWaveHapticEffect(class UHapticFeedbackEffect_SoundWave* HapticEffect, EControllerHand Hand, bool bAppend = false, float Scale = 1.f, bool bLoop = false);
|
||||
|
||||
/**
|
||||
* Stops a playing haptic feedback curve at a specific location.
|
||||
* @param HapticEffect The haptic effect to stop
|
||||
* @param Hand Which hand to stop the effect for
|
||||
* @param Location Which hand location to play the effect on
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void StopHapticEffect(EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
|
||||
|
||||
/**
|
||||
* Set the value of the haptics for the specified hand and location directly, using frequency and amplitude. NOTE: If a curve is already
|
||||
* playing for this hand, it will be cancelled in favour of the specified values.
|
||||
*
|
||||
* @param Frequency The normalized frequency [0.0, 1.0] to play through the haptics system
|
||||
* @param Amplitude The normalized amplitude [0.0, 1.0] to set the haptic feedback to
|
||||
* @param Hand Which hand to play the effect on
|
||||
* @param Location Which hand location to play the effect on
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void SetHapticsByValue(const float Frequency, const float Amplitude, EControllerHand Hand, EOculusXRHandHapticsLocation Location = EOculusXRHandHapticsLocation::Hand);
|
||||
|
||||
/**
|
||||
* Get the controller haptics sample rate.
|
||||
* @param Hand Which hand to play the effect on
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static float GetControllerSampleRateHz(EControllerHand Hand);
|
||||
|
||||
/**
|
||||
* Get the maximum duration (in seconds) that the controller haptics can handle each time.
|
||||
* @param Hand Which hand to play the effect on
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static int GetMaxHapticDuration(EControllerHand Hand);
|
||||
|
||||
/**
|
||||
* Set if / how controller inputs are used to build a syntheic hand pose.
|
||||
* @param Type How the hand should be posed.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Controller")
|
||||
static void SetControllerDrivenHandPoses(EOculusXRControllerDrivenHandPoseTypes Type);
|
||||
};
|
||||
Reference in New Issue
Block a user