From 366996a7dfecf8c72114ee6a8d8797c2bb6585fe Mon Sep 17 00:00:00 2001 From: Mamadou Babaei Date: Fri, 3 May 2024 11:29:17 +0200 Subject: [PATCH] implement the OpenXR-compatible hand tracking (XR_EXT_hand_tracking) support --- CHANGELOG.md | 10 +- README.md | 6 +- SenseGlove.uplugin | 22 + .../Components/SGVirtualHandComponent.cpp | 112 +- .../Components/SGWristTrackerComponent.cpp | 85 +- .../SenseGlove/GameFramework/SGPawn.cpp | 2 - .../Components/SGVirtualHandComponent.h | 10 +- .../Components/SGWristTrackerComponent.h | 3 - Source/SenseGlove/SenseGlove.Build.cs | 3 +- .../SGSettings/SGDebugGizmoSettings.cpp | 23 +- .../SGSettings/SGDebugVirtualHandSettings.cpp | 19 +- .../SGSettings/SGGloveTrackingSettings.cpp | 47 + .../SGSettings/SGHMDTrackingSettings.cpp | 47 + .../SGSettings/SGHandTrackingSettings.cpp | 50 + .../Private/SGSettings/SGSettings.cpp | 20 +- .../SGSettings/SGTrackingSettings.cpp} | 53 +- .../SGSettings/SGWristTrackingSettings.cpp | 12 +- .../Public/SGSettings/SGDebugGizmoSettings.h | 18 +- .../SGSettings/SGDebugVirtualHandSettings.h | 59 +- .../SGSettings/SGGloveTrackingSettings.h | 62 + .../Public/SGSettings/SGHMDTrackingSettings.h | 65 + .../SGSettings/SGHandTrackingSettings.h | 72 + .../Public/SGSettings/SGSettings.h | 15 +- .../Public/SGSettings/SGTrackingSettings.h | 85 + .../SGSettings/SGWristTrackingSettings.h | 18 +- .../SGSettingsKismetLibrary.cpp | 12 +- .../SGSettingsKismetLibrary.h | 7 +- .../Private/SGTracking/SGGloveTracker.cpp | 279 +++ .../Private/SGTracking/SGHMDTracker.cpp} | 48 +- .../Private/SGTracking/SGXRHandState.cpp | 70 + .../Private/SGTracking/SGXRTracker.cpp | 1771 +++++++++++++++++ .../Private/SenseGloveTracking.cpp | 42 + .../Private/SenseGloveTracking.h | 38 + .../Private/SenseGloveTrackingModule.cpp | 181 ++ .../Private/SenseGloveTrackingModule.h | 116 ++ .../Public/SGTracking/SGGloveTracker.h | 117 ++ .../Public/SGTracking/SGHMDTracker.h} | 6 +- .../Public/SGTracking/SGXRHandState.h | 68 + .../Public/SGTracking/SGXRTracker.h | 165 ++ .../SenseGloveTracking.Build.cs | 93 + .../SGGloveTrackerKismetLibrary.cpp | 79 + .../SGHMDTrackerKismetLibrary.cpp | 78 + .../Private/SenseGloveTrackingKismet.cpp | 40 + .../Private/SenseGloveTrackingKismet.h | 38 + .../SenseGloveTrackingKismetModule.cpp | 62 + .../Private/SenseGloveTrackingKismetModule.h | 51 + .../SGGloveTrackerKismetLibrary.h | 75 + .../SGHMDTrackerKismetLibrary.h} | 26 +- .../SenseGloveTrackingKismet.Build.cs | 59 + .../{SGTypes.cpp => SGTrackingTypes.cpp} | 2 +- .../SGTypes/{SGTypes.h => SGTrackingTypes.h} | 19 +- 51 files changed, 4152 insertions(+), 308 deletions(-) create mode 100644 Source/SenseGloveSettings/Private/SGSettings/SGGloveTrackingSettings.cpp create mode 100644 Source/SenseGloveSettings/Private/SGSettings/SGHMDTrackingSettings.cpp create mode 100644 Source/SenseGloveSettings/Private/SGSettings/SGHandTrackingSettings.cpp rename Source/{SenseGloveKismet/Private/SGKismet/SGHeadMountedDisplayKismetLibrary.cpp => SenseGloveSettings/Private/SGSettings/SGTrackingSettings.cpp} (57%) create mode 100644 Source/SenseGloveSettings/Public/SGSettings/SGGloveTrackingSettings.h create mode 100644 Source/SenseGloveSettings/Public/SGSettings/SGHMDTrackingSettings.h create mode 100644 Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h create mode 100644 Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h create mode 100644 Source/SenseGloveTracking/Private/SGTracking/SGGloveTracker.cpp rename Source/{SenseGlove/Private/SenseGlove/SGHeadMountedDisplay.cpp => SenseGloveTracking/Private/SGTracking/SGHMDTracker.cpp} (90%) create mode 100644 Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp create mode 100644 Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp create mode 100644 Source/SenseGloveTracking/Private/SenseGloveTracking.cpp create mode 100644 Source/SenseGloveTracking/Private/SenseGloveTracking.h create mode 100644 Source/SenseGloveTracking/Private/SenseGloveTrackingModule.cpp create mode 100644 Source/SenseGloveTracking/Private/SenseGloveTrackingModule.h create mode 100644 Source/SenseGloveTracking/Public/SGTracking/SGGloveTracker.h rename Source/{SenseGlove/Public/SenseGlove/SGHeadMountedDisplay.h => SenseGloveTracking/Public/SGTracking/SGHMDTracker.h} (95%) create mode 100644 Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h create mode 100644 Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h create mode 100644 Source/SenseGloveTracking/SenseGloveTracking.Build.cs create mode 100644 Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGGloveTrackerKismetLibrary.cpp create mode 100644 Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGHMDTrackerKismetLibrary.cpp create mode 100644 Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.cpp create mode 100644 Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.h create mode 100644 Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.cpp create mode 100644 Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.h create mode 100644 Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGGloveTrackerKismetLibrary.h rename Source/{SenseGloveKismet/Public/SGKismet/SGHeadMountedDisplayKismetLibrary.h => SenseGloveTrackingKismet/Public/SGTrackingKismet/SGHMDTrackerKismetLibrary.h} (74%) create mode 100644 Source/SenseGloveTrackingKismet/SenseGloveTrackingKismet.Build.cs rename Source/SenseGloveTypes/Private/SGTypes/{SGTypes.cpp => SGTrackingTypes.cpp} (97%) rename Source/SenseGloveTypes/Public/SGTypes/{SGTypes.h => SGTrackingTypes.h} (83%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739df324..a102b9b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.0.8] - 2024-07-15 -This is a bugfix release that contains a somewhat important bugfix backported from the next release of the plugin as documented below. +This is a minor release focusing mainly on bringing OpenXR-compatible hand tracking support (XR_EXT_hand_tracking) and Head-mounted Display automatic detection for adjusting wrist tracker offsets automatically at runtime. +### Added + +- Added SenseGloveTracking and SenseGloveTrackingKismet modules which provide OpenXR-compatible hand tracking by implementing XR_EXT_hand_tracking support, HMD auto-detection, and SenseGlove device tracking. - A fallback to HMD and wrist tracker hardware auto-detection mechanism has been added to be triggered in situations when automatic detection of the wrist tracker hardware is desired, e.g., either by not setting it explicitly, or setting it to the default None value. Please note that this is still highly experimental and HTC VIVE Focus 3 and HTC XR Elite cannot be distinguished in the current iteration. Though, since the tracker devices and offsets for both headsets are the same in the end it does not make a difference if both headsets are detected as each other. -- Added the SGHeadMountedDisplay utility class, exposed to both C++ and Blueprint, in order to easily gather information about the HMD at runtime. -- Added the SGTypes header to the SenseGloveTypes module in order to define and share SenseGlove module types through this header across the plugin modules. +- Added the SGHMDTracker utility class, exposed to both C++ and Blueprint, in order to easily gather information about the HMD device at runtime. +- Added the SGTrackingTypes header to the SenseGloveTypes module in order to define and share SenseGloveTracking module types through this header across the plugin modules. - Added ESGHeadMountedDisplayDevice enum with supported HMDs list. +- Added the SGGloveTracer utility class, exposed to both C++ and Blueprint, in order to easily check left or right glove connectivity or retrieve the connected glove instances. ### Changed diff --git a/README.md b/README.md index 98b3cd08..7015b910 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Please note that modules postfixed with **Impl** are intended for interal use an ### Upcoming features planned for the v2.1.0 release -- [ ] Hand tracking compatible with OpenXR (or other generic hand tracking format). +- [X] OpenXR-compatible hand tracking (XR_EXT_hand_tracking) support. - [X] A fallback to HMD and wrist tracker hardware auto-detection mechanism when automatic detection of the wrist tracker hardware is desired. ### Planned features long-term @@ -146,6 +146,10 @@ Please note that modules postfixed with **Impl** are intended for interal use an │ ├── SenseGloveSettingsKismet (exposes Blueprints-specific functionality from the SenseGloveSettings module) │ + ├── SenseGloveTracking (provides XR_EXT_hand_tracking support, HMD auto-detection, and SenseGlove device tracking) + │ + ├── SenseGloveTrackingKismet (exposes Blueprints-specific functionality from the SenseGloveTracking module) + │ ├── SenseGloveTypes (exposes various enums from the backend libraries and also types from the SenseGlove module) │ ├── SenseGloveUtils (the internal utility module) diff --git a/SenseGlove.uplugin b/SenseGlove.uplugin index f2d3b8d3..bd1db194 100644 --- a/SenseGlove.uplugin +++ b/SenseGlove.uplugin @@ -217,6 +217,28 @@ "Win64" ] }, + { + "Name": "SenseGloveTracking", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "WhitelistPlatforms": [ + "Android", + "Linux", + "LinuxArm64", + "Win64" + ] + }, + { + "Name": "SenseGloveTrackingKismet", + "Type": "Runtime", + "LoadingPhase": "PostConfigInit", + "WhitelistPlatforms": [ + "Android", + "Linux", + "LinuxArm64", + "Win64" + ] + }, { "Name": "SenseGloveTypes", "Type": "Runtime", diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp index d5ecd945..be0dc669 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp @@ -43,11 +43,11 @@ #include "SenseGlove/Animation/SGVirtualHandAnimInstance.h" #include "SenseGlove/Components/SGWristTrackerComponent.h" -#include "SGBackend/SGBackend.h" #include "SGCore/SGHandPose.h" #include "SGCore/SGHapticGlove.h" #include "SGDebug/SGDebugVirtualHand.h" #include "SGLog/SGLog.h" +#include "SGTracking/SGGloveTracker.h" struct USGVirtualHandComponent::FImpl { @@ -119,7 +119,7 @@ struct USGVirtualHandComponent::FImpl USkeleton* GetSkeleton() const; const FReferenceSkeleton& GetRefSkeleton() const; - bool CheckGlove() const; + void UpdateGloveVisibility() const; FORCEINLINE void SetVisibility(const bool bInVisible) const { @@ -227,21 +227,10 @@ USGVirtualHandComponent::USGVirtualHandComponent(const FObjectInitializer& Objec RingFingertipTouchSocketName = FName{TEXT("TouchRingFingertip")}; PinkyFingertipTouchSocketName = FName{TEXT("TouchPinkyFingertip")}; - DebugVirtualHandSettings = FSGDebugVirtualHandSettings{ - false, - ESGDebugVirtualHandDrawingMode::None, - FVector(0.5f, 0.5f, 0.5f), - FColor(255, 0, 0, 255), - 0.1f, 1.0f, 1.0f, - FColor(255, 0, 0, 255), - FColor(0, 255, 0, 255), - FColor(0, 0, 255, 255), - false, 1.1f, 0 - }; + DebugVirtualHandSettings = FSGDebugVirtualHandSettings{}; Super::SetSkeletalMesh(nullptr, true); - Glove = nullptr; WristTracker = nullptr; } @@ -302,13 +291,15 @@ void USGVirtualHandComponent::InitializeComponent() Pimpl->SetDefaultMesh(); } - (void) Pimpl->CheckGlove(); + Pimpl->UpdateGloveVisibility(); } void USGVirtualHandComponent::UninitializeComponent() { Super::UninitializeComponent(); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; if (IsValid(Glove) && AutoStopsAllHapticsOnEndPlay()) { Glove->StopHaptics(); @@ -319,13 +310,15 @@ void USGVirtualHandComponent::BeginPlay() { Super::BeginPlay(); - (void) Pimpl->CheckGlove(); + Pimpl->UpdateGloveVisibility(); } void USGVirtualHandComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; if (IsValid(Glove) && AutoStopsAllHapticsOnEndPlay()) { Glove->StopHaptics(); @@ -345,8 +338,10 @@ void USGVirtualHandComponent::TickComponent( Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - (void) Pimpl->CheckGlove(); + Pimpl->UpdateGloveVisibility(); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; if (IsValid(Glove) && DebugVirtualHandSettings.DrawingMode != ESGDebugVirtualHandDrawingMode::None) { Pimpl->DrawDebugVirtualHand(); @@ -368,8 +363,10 @@ void USGVirtualHandComponent::SetSkeletalMesh(USkeletalMesh* NewMesh, const bool void USGVirtualHandComponent::EditorTick( const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { - (void) Pimpl->CheckGlove(); + Pimpl->UpdateGloveVisibility(); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; if (IsValid(Glove) && DebugVirtualHandSettings.DrawingMode != ESGDebugVirtualHandDrawingMode::None) { Pimpl->DrawDebugVirtualHand(); @@ -378,13 +375,24 @@ void USGVirtualHandComponent::EditorTick( bool USGVirtualHandComponent::IsGloveConnected() const { - return Pimpl->CheckGlove(); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + const USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; + return IsValid(Glove); +} + +USGHapticGlove* USGVirtualHandComponent::GetConnectedGlove() const +{ + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; + return Glove; } USGHandPose* USGVirtualHandComponent::GetHandPose() { USGHandPose* HandPose{nullptr}; + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr}; if (IsValid(Glove)) { Glove->GetHandPose(this, HandPose); @@ -587,62 +595,38 @@ const FReferenceSkeleton& USGVirtualHandComponent::FImpl::GetRefSkeleton() const return Mesh->GetRefSkeleton(); } -bool USGVirtualHandComponent::FImpl::CheckGlove() const +void USGVirtualHandComponent::FImpl::UpdateGloveVisibility() const { - if (!USGBackend::GetInstance()->IsBackendInitialized()) - { - const bool bSucceeded = USGBackend::GetInstance()->InitializeBackend(); - if (!bSucceeded) - { - return false; - } - } + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + const USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(Owner->IsRight()) : nullptr}; - bool bGloveConnected; - if (!IsValid(Owner->Glove) || !Owner->Glove->IsConnected()) - { - bGloveConnected = USGHapticGlove::GetGlove(Owner, Owner->IsRight(), Owner->Glove); - if (!bGloveConnected) - { - Owner->Glove = nullptr; - } - } - else - { - bGloveConnected = true; - } + const bool bConnected = IsValid(Glove); + const bool bIsVisible = Owner->IsVisible(); - const bool bIsEditor = IsEditor(); - if (!bIsEditor) + if (Owner->bHiddenInGameIfNoGloveDetected) { - const bool bIsVisible = Owner->IsVisible(); - if (Owner->bHiddenInGameIfNoGloveDetected) - { - if (bGloveConnected) - { - if (!bIsVisible) - { - SetVisibility(true); - } - } - else - { - if (bIsVisible) - { - SetVisibility(false); - } - } - } - else + if (bConnected) { if (!bIsVisible) { SetVisibility(true); } } + else + { + if (bIsVisible) + { + SetVisibility(false); + } + } + } + else + { + if (!bIsVisible) + { + SetVisibility(true); + } } - - return bGloveConnected; } void USGVirtualHandComponent::FImpl::DrawDebugVirtualHand() const diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp index 41a1de95..1001c3b1 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp @@ -40,12 +40,12 @@ #include "Engine/SkeletalMesh.h" #include "UObject/ConstructorHelpers.h" -#include "SenseGlove/SGHeadMountedDisplay.h" -#include "SGBackend/SGBackend.h" #include "SGCore/SGHapticGlove.h" #include "SGDebug/SGDebugGizmo.h" #include "SGLog/SGLog.h" #include "SGSettings/SGSettings.h" +#include "SGTracking/SGGloveTracker.h" +#include "SGTracking/SGHMDTracker.h" struct USGWristTrackerComponent::FImpl { @@ -79,11 +79,9 @@ struct USGWristTrackerComponent::FImpl bool IsEditor() const; - bool CheckGlove() const; - void UpdateMotionSource() const; void UpdateWristTrackingData() const; - void DrawDebugGizmo(); + void DrawDebugGizmo() const; }; USGWristTrackerComponent::USGWristTrackerComponent(const FObjectInitializer& ObjectInitializer) @@ -105,27 +103,10 @@ USGWristTrackerComponent::USGWristTrackerComponent(const FObjectInitializer& Obj SetCanEverAffectNavigation(false); bOverridePluginWristTrackingSettings = false; - WristTrackingSettings = FSGWristTrackingSettings( - ESGPositionalTrackingHardware::None, - FVector::ZeroVector, - FVector::ZeroVector, - FRotator::ZeroRotator, - FRotator::ZeroRotator, - EControllerHand::Left, - EControllerHand::Right, - false, - FSGDebugGizmoSettings( - 4.0f, 1.0f, - FColor(255, 0, 0, 255), - FColor(0, 255, 0, 255), - FColor(0, 0, 255, 255), - false, 1.1f, 0 - ) - ); + WristTrackingSettings = FSGWristTrackingSettings{}; bRight = true; - Glove = nullptr; WristLocation = FVector::ZeroVector; WristRotation = FRotator::ZeroRotator; } @@ -149,7 +130,7 @@ const FSGWristTrackingSettings& USGWristTrackerComponent::GetWristTrackingSettin return WristTrackingSettings; } - return Settings->GetWristTrackingSettings(); + return Settings->GetTrackingSettings().WristTrackingSettings; } void USGWristTrackerComponent::SetWristTrackingSettings(const FSGWristTrackingSettings& InWristTrackingSettings) @@ -180,7 +161,7 @@ void USGWristTrackerComponent::InitializeComponent() { Super::InitializeComponent(); - (void) Pimpl->CheckGlove(); + Pimpl->UpdateWristTrackingData(); Pimpl->UpdateMotionSource(); } @@ -189,7 +170,7 @@ void USGWristTrackerComponent::BeginPlay() { Super::BeginPlay(); - (void) Pimpl->CheckGlove(); + Pimpl->UpdateWristTrackingData(); Pimpl->UpdateMotionSource(); } @@ -207,19 +188,19 @@ void USGWristTrackerComponent::TickComponent( Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - if (!!WristTrackingSettings.bDrawWristTrackerDebugGizmo) + if (!!WristTrackingSettings.bDrawDebugGizmo) { Pimpl->DrawDebugGizmo(); } - (void) Pimpl->CheckGlove(); + Pimpl->UpdateWristTrackingData(); } void USGWristTrackerComponent::EditorTick( const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { - (void) Pimpl->CheckGlove(); + Pimpl->UpdateWristTrackingData(); } USGWristTrackerComponent::FImpl::FImpl(USGWristTrackerComponent* InOwner) @@ -240,35 +221,6 @@ bool USGWristTrackerComponent::FImpl::IsEditor() const return false; } -bool USGWristTrackerComponent::FImpl::CheckGlove() const -{ - if (!USGBackend::GetInstance()->IsBackendInitialized()) - { - const bool bSucceeded = USGBackend::GetInstance()->InitializeBackend(); - if (!bSucceeded) - { - return false; - } - } - - bool bGloveConnected; - if (!IsValid(Owner->Glove) || !Owner->Glove->IsConnected()) - { - bGloveConnected = USGHapticGlove::GetGlove(Owner, Owner->IsRight(), Owner->Glove); - if (!bGloveConnected) - { - Owner->Glove = nullptr; - } - } - else - { - bGloveConnected = true; - UpdateWristTrackingData(); - } - - return bGloveConnected; -} - void USGWristTrackerComponent::FImpl::UpdateMotionSource() const { if (GEngine) @@ -281,7 +233,12 @@ void USGWristTrackerComponent::FImpl::UpdateMotionSource() const void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const { - ensureAlwaysMsgf(Owner->Glove, TEXT("Invalid Glove!")); + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + const USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(Owner->IsRight()) : nullptr}; + if (!IsValid(Glove)) + { + return; + } FVector TrackerLocation{Owner->GetComponentLocation()}; FRotator TrackerRotation{Owner->GetComponentRotation()}; @@ -289,7 +246,7 @@ void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const ESGPositionalTrackingHardware TrackerHardware = Owner->GetWristTrackingSettings().TrackingHardware; if (TrackerHardware == ESGPositionalTrackingHardware::None) { - const ESGHeadMountedDisplayDevice HmdDevice = FSGHeadMountedDisplay::GetDeviceType(); + const ESGHeadMountedDisplayDevice HmdDevice = FSGHMDTracker::GetDeviceType(); switch (HmdDevice) { case ESGHeadMountedDisplayDevice::MetaQuest2: @@ -330,8 +287,8 @@ void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const FVector NewWristLocation; FRotator NewWristRotation; - Owner->Glove->GetWristLocation(TrackerLocation, TrackerRotation, TrackerHardware, - NewWristLocation, NewWristRotation); + Glove->GetWristLocation(TrackerLocation, TrackerRotation, TrackerHardware, + NewWristLocation, NewWristRotation); const bool bLocationUpdated = !Owner->WristLocation.Equals(NewWristLocation, SMALL_NUMBER); const bool bRotationUpdated = !Owner->WristRotation.Equals(NewWristRotation, SMALL_NUMBER); @@ -362,12 +319,12 @@ void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const } } -void USGWristTrackerComponent::FImpl::DrawDebugGizmo() +void USGWristTrackerComponent::FImpl::DrawDebugGizmo() const { const UWorld* World{Owner->GetWorld()}; const FVector& Location{Owner->GetComponentLocation()}; const FRotator& Rotation{Owner->GetComponentRotation()}; - const FSGDebugGizmoSettings& DebugGizmoSettings{Owner->GetWristTrackingSettings().WristTrackerDebugGizmoSettings}; + const FSGDebugGizmoSettings& DebugGizmoSettings{Owner->GetWristTrackingSettings().DebugGizmoSettings}; FSGDebugGizmo::Draw(World, Location, Rotation, DebugGizmoSettings); } diff --git a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp index 4ff6239d..8ad2c7f3 100644 --- a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp +++ b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp @@ -236,7 +236,6 @@ ASGPawn::ASGPawn(const FObjectInitializer& ObjectInitializer) FSGDebugVirtualHandSettings RealHandLeftDebugSettings; RealHandLeftDebugSettings.bDrawDebugVirtualHand = true; RealHandLeftDebugSettings.DrawingMode = ESGDebugVirtualHandDrawingMode::GizmoJoints; - RealHandLeftDebugSettings.GizmoThickness = 0.5f; RealHandLeft->SetDebugVirtualHandSettings(MoveTemp(RealHandLeftDebugSettings)); LeftThumbFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject( @@ -463,7 +462,6 @@ ASGPawn::ASGPawn(const FObjectInitializer& ObjectInitializer) FSGDebugVirtualHandSettings RealHandRightDebugSettings; RealHandRightDebugSettings.bDrawDebugVirtualHand = true; RealHandRightDebugSettings.DrawingMode = ESGDebugVirtualHandDrawingMode::GizmoJoints; - RealHandRightDebugSettings.GizmoThickness = 0.5f; RealHandRight->SetDebugVirtualHandSettings(MoveTemp(RealHandRightDebugSettings)); RightThumbFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject( diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h index 30da6f3b..c54b1919 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h @@ -127,9 +127,6 @@ private: FSGDebugVirtualHandSettings DebugVirtualHandSettings; private: - UPROPERTY(Transient) - USGHapticGlove* Glove; - UPROPERTY(Transient) USGWristTrackerComponent* WristTracker; @@ -311,12 +308,7 @@ protected: public: bool IsGloveConnected() const; - - FORCEINLINE USGHapticGlove* GetConnectedGlove() const - { - return Glove; - } - + USGHapticGlove* GetConnectedGlove() const; USGHandPose* GetHandPose(); public: diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h index f76e1bd0..1c502480 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h @@ -111,9 +111,6 @@ private: FSGWristTrackingSettings WristTrackingSettings; private: - UPROPERTY(Transient) - USGHapticGlove* Glove; - UPROPERTY(Transient) FVector WristLocation; diff --git a/Source/SenseGlove/SenseGlove.Build.cs b/Source/SenseGlove/SenseGlove.Build.cs index c42dd816..b9acc8cf 100644 --- a/Source/SenseGlove/SenseGlove.Build.cs +++ b/Source/SenseGlove/SenseGlove.Build.cs @@ -58,12 +58,11 @@ public class SenseGlove : ModuleRules #if UE_5_3_OR_LATER "XRBase", #endif - "SenseGloveBackend", - "SenseGloveConnect", "SenseGloveCore", "SenseGloveDebug", "SenseGloveLog", "SenseGloveSettings", + "SenseGloveTracking", "SenseGloveTypes", } ); diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGDebugGizmoSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGDebugGizmoSettings.cpp index 1f809feb..af907c43 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGDebugGizmoSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGDebugGizmoSettings.cpp @@ -35,10 +35,27 @@ #include "SGSettings/SGDebugGizmoSettings.h" +FSGDebugGizmoSettings::FSGDebugGizmoSettings() + : Length(4.0f), + Thickness(0.4f), + XAxisColor(255, 0, 0, 255), + YAxisColor(0, 255, 0, 255), + ZAxisColor(0, 0, 255, 255), + bPersistentLines(false), + LifeTimeModifier(1.1), + DepthPriority(0.0f) +{ +} + FSGDebugGizmoSettings::FSGDebugGizmoSettings( - const float InLength, const float InThickness, - const FColor& InXAxisColor, const FColor& InYAxisColor, const FColor& InZAxisColor, - const bool bInPersistentLines, const float InLifeTimeModifier, const uint8 InDepthPriority) + const float InLength, + const float InThickness, + const FColor& InXAxisColor, + const FColor& InYAxisColor, + const FColor& InZAxisColor, + const bool bInPersistentLines, + const float InLifeTimeModifier, + const uint8 InDepthPriority) : Length(InLength), Thickness(InThickness), XAxisColor(InXAxisColor), diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGDebugVirtualHandSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGDebugVirtualHandSettings.cpp index 7d91290d..b37a3523 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGDebugVirtualHandSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGDebugVirtualHandSettings.cpp @@ -40,9 +40,9 @@ FSGDebugVirtualHandSettings::FSGDebugVirtualHandSettings() DrawingMode(ESGDebugVirtualHandDrawingMode::None), CubeExtent(FVector(0.5f, 0.5f, 0.5f)), CubeColor(255, 0, 0, 255), - CubeThickness(0.1f), + CubeThickness(0.2f), GizmoLength(1.0f), - GizmoThickness(1.0f), + GizmoThickness(0.25f), GizmoXAxisColor(255, 0, 0, 255), GizmoYAxisColor(0, 255, 0, 255), GizmoZAxisColor(0, 0, 255, 255), @@ -55,10 +55,17 @@ FSGDebugVirtualHandSettings::FSGDebugVirtualHandSettings() FSGDebugVirtualHandSettings::FSGDebugVirtualHandSettings( const bool bInDrawDebugVirtualHand, const ESGDebugVirtualHandDrawingMode InDrawingMode, - const FVector& InCubeExtent, const FColor& InCubeColor, const float InCubeThickness, - const float InGizmoLength, const float InGizmoThickness, - const FColor& InGizmoXAxisColor, const FColor& InGizmoYAxisColor, const FColor& InGizmoZAxisColor, - const bool bInPersistentLines, const float InLifeTimeModifier, const uint8 InDepthPriority) + const FVector& InCubeExtent, + const FColor& InCubeColor, + const float InCubeThickness, + const float InGizmoLength, + const float InGizmoThickness, + const FColor& InGizmoXAxisColor, + const FColor& InGizmoYAxisColor, + const FColor& InGizmoZAxisColor, + const bool bInPersistentLines, + const float InLifeTimeModifier, + const uint8 InDepthPriority) : bDrawDebugVirtualHand(bInDrawDebugVirtualHand), DrawingMode(InDrawingMode), CubeExtent(InCubeExtent), diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGGloveTrackingSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGGloveTrackingSettings.cpp new file mode 100644 index 00000000..e4c73548 --- /dev/null +++ b/Source/SenseGloveSettings/Private/SGSettings/SGGloveTrackingSettings.cpp @@ -0,0 +1,47 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGSettings/SGGloveTrackingSettings.h" + +FSGGloveTrackingSettings::FSGGloveTrackingSettings() + : GloveConnectivityCheckInterval(0.125f) +{ +} + +FSGGloveTrackingSettings::FSGGloveTrackingSettings( + const float InGloveConnectivityCheckInterval) + : GloveConnectivityCheckInterval(InGloveConnectivityCheckInterval) +{ +} \ No newline at end of file diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGHMDTrackingSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGHMDTrackingSettings.cpp new file mode 100644 index 00000000..bb2c28fb --- /dev/null +++ b/Source/SenseGloveSettings/Private/SGSettings/SGHMDTrackingSettings.cpp @@ -0,0 +1,47 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGSettings/SGHMDTrackingSettings.h" + +FSGHMDTrackingSettings::FSGHMDTrackingSettings() + : ViveHMDDetectionPriority(ESGViveHMDDetectionPriority::HtcViveFocus3First) +{ +} + +FSGHMDTrackingSettings::FSGHMDTrackingSettings( + const ESGViveHMDDetectionPriority InViveHmdDetectionPriority) + : ViveHMDDetectionPriority(InViveHmdDetectionPriority) +{ +} \ No newline at end of file diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGHandTrackingSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGHandTrackingSettings.cpp new file mode 100644 index 00000000..9c1e817d --- /dev/null +++ b/Source/SenseGloveSettings/Private/SGSettings/SGHandTrackingSettings.cpp @@ -0,0 +1,50 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGSettings/SGHandTrackingSettings.h" + +FSGHandTrackingSettings::FSGHandTrackingSettings() + : bUseMoreSpecificMotionSourceNames(false), + bSupportLegacyControllerMotionSources(true) +{ +} + +FSGHandTrackingSettings::FSGHandTrackingSettings( + const bool bInUseMoreSpecificMotionSourceNames, + const bool bInSupportLegacyControllerMotionSources) + : bUseMoreSpecificMotionSourceNames(bInUseMoreSpecificMotionSourceNames), + bSupportLegacyControllerMotionSources(bInSupportLegacyControllerMotionSources) +{ +} \ No newline at end of file diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGSettings.cpp index a5865e5c..1bf3f53d 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGSettings.cpp @@ -77,23 +77,7 @@ USGSettings* USGSettings::GetInstance() USGSettings::USGSettings() : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) { - WristTrackingSettings = FSGWristTrackingSettings( - ESGPositionalTrackingHardware::None, - FVector::ZeroVector, - FVector::ZeroVector, - FRotator::ZeroRotator, - FRotator::ZeroRotator, - EControllerHand::Left, - EControllerHand::Right, - false, - FSGDebugGizmoSettings( - 4.0f, 1.0f, - FColor(255, 0, 0, 255), - FColor(0, 255, 0, 255), - FColor(0, 0, 255, 255), - false, 1.1f, 0 - ) - ); + TrackingSettings = FSGTrackingSettings{}; } bool USGSettings::ShouldCreateSubsystem(UObject* Outer) const @@ -116,7 +100,7 @@ void USGSettings::Deinitialize() Super::Deinitialize(); - (void)Pimpl.Release(); + (void) Pimpl.Release(); SGLOG("The SenseGlove settings singleton has been successfully de-initialized!"); } diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGHeadMountedDisplayKismetLibrary.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGTrackingSettings.cpp similarity index 57% rename from Source/SenseGloveKismet/Private/SGKismet/SGHeadMountedDisplayKismetLibrary.cpp rename to Source/SenseGloveSettings/Private/SGSettings/SGTrackingSettings.cpp index d88c1130..7cceced5 100644 --- a/Source/SenseGloveKismet/Private/SGKismet/SGHeadMountedDisplayKismetLibrary.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGTrackingSettings.cpp @@ -1,4 +1,4 @@ -/** +/** * @file * * @author Mamadou Babaei @@ -33,46 +33,23 @@ */ -#include "SGKismet/SGHeadMountedDisplayKismetLibrary.h" +#include "SGSettings/SGTrackingSettings.h" -#include "SenseGlove/SGHeadMountedDisplay.h" - -FName UHeadMountedDisplayKismetLibrary::GetDeviceName() +FSGTrackingSettings::FSGTrackingSettings() + : bFallbackToHandTrackingIfNoGloveDetected(true) { - return FSGHeadMountedDisplay::GetDeviceName(); } -bool UHeadMountedDisplayKismetLibrary::IsMetaQuest2() +FSGTrackingSettings::FSGTrackingSettings( + const bool bInFallbackToHandTrackingIfNoGloveDetected, + const FSGGloveTrackingSettings& InGloveTrackingSettings, + const FSGHandTrackingSettings& InHandTrackingSettings, + const FSGHMDTrackingSettings& InHmdTrackingSettings, + const FSGWristTrackingSettings& InWristTrackingSettings) + : bFallbackToHandTrackingIfNoGloveDetected(bInFallbackToHandTrackingIfNoGloveDetected),\ + GloveTrackingSettings(InGloveTrackingSettings), + HandTrackingSettings(InHandTrackingSettings), + HMDTrackingSettings(InHmdTrackingSettings), + WristTrackingSettings(InWristTrackingSettings) { - return FSGHeadMountedDisplay::IsMetaQuest2(); -} - -bool UHeadMountedDisplayKismetLibrary::IsMetaQuestPro() -{ - return FSGHeadMountedDisplay::IsMetaQuestPro(); -} - -bool UHeadMountedDisplayKismetLibrary::IsMetaQuest3() -{ - return FSGHeadMountedDisplay::IsMetaQuest3(); -} - -bool UHeadMountedDisplayKismetLibrary::IsHtcVivePro() -{ - return FSGHeadMountedDisplay::IsHtcVivePro(); -} - -bool UHeadMountedDisplayKismetLibrary::IsHtcViveFocus3() -{ - return FSGHeadMountedDisplay::IsHtcViveFocus3(); -} - -bool UHeadMountedDisplayKismetLibrary::IsHtcViveXRElite() -{ - return FSGHeadMountedDisplay::IsHtcViveXRElite(); -} - -ESGHeadMountedDisplayDevice UHeadMountedDisplayKismetLibrary::GetDeviceType() -{ - return FSGHeadMountedDisplay::GetDeviceType(); } \ No newline at end of file diff --git a/Source/SenseGloveSettings/Private/SGSettings/SGWristTrackingSettings.cpp b/Source/SenseGloveSettings/Private/SGSettings/SGWristTrackingSettings.cpp index bbcd88fb..149c394b 100644 --- a/Source/SenseGloveSettings/Private/SGSettings/SGWristTrackingSettings.cpp +++ b/Source/SenseGloveSettings/Private/SGSettings/SGWristTrackingSettings.cpp @@ -43,8 +43,8 @@ FSGWristTrackingSettings::FSGWristTrackingSettings() TrackingHardwareRotationOffsetRightHand(FRotator::ZeroRotator), LeftHandMotionSource(EControllerHand::Left), RightHandMotionSource(EControllerHand::Right), - bDrawWristTrackerDebugGizmo(false), - WristTrackerDebugGizmoSettings(FSGDebugGizmoSettings()) + bDrawDebugGizmo(false), + DebugGizmoSettings(FSGDebugGizmoSettings{}) { } @@ -56,8 +56,8 @@ FSGWristTrackingSettings::FSGWristTrackingSettings( const FRotator& InTrackingHardwareRotationOffsetRightHand, const EControllerHand InLeftHandMotionSource, const EControllerHand InRightHandMotionSource, - const bool bInDrawWristTrackerDebugGizmo, - const FSGDebugGizmoSettings& InWristTrackerDebugGizmoSettings) + const bool bInDrawDebugGizmo, + const FSGDebugGizmoSettings& InDebugGizmoSettings) : TrackingHardware(InTrackingHardware), TrackingHardwareLocationOffsetLeftHand(InTrackingHardwareLocationOffsetLeftHand), TrackingHardwareLocationOffsetRightHand(InTrackingHardwareLocationOffsetRightHand), @@ -65,7 +65,7 @@ FSGWristTrackingSettings::FSGWristTrackingSettings( TrackingHardwareRotationOffsetRightHand(InTrackingHardwareRotationOffsetRightHand), LeftHandMotionSource(InLeftHandMotionSource), RightHandMotionSource(InRightHandMotionSource), - bDrawWristTrackerDebugGizmo(bInDrawWristTrackerDebugGizmo), - WristTrackerDebugGizmoSettings(InWristTrackerDebugGizmoSettings) + bDrawDebugGizmo(bInDrawDebugGizmo), + DebugGizmoSettings(InDebugGizmoSettings) { } \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h index 2017bb8e..c53c683a 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGDebugGizmoSettings.h @@ -70,12 +70,14 @@ public: uint8 DepthPriority; public: - explicit FSGDebugGizmoSettings(float InLength = 1.0f, - float InThickness = 1.0f, - const FColor& InXAxisColor = FColor(255, 0, 0, 255), - const FColor& InYAxisColor = FColor(0, 255, 0, 255), - const FColor& InZAxisColor = FColor(0, 0, 255, 255), - bool bInPersistentLines = false, - float InLifeTimeModifier = 1.1f, - uint8 InDepthPriority = 0); + FSGDebugGizmoSettings(); + + FSGDebugGizmoSettings(float InLength, + float InThickness, + const FColor& InXAxisColor, + const FColor& InYAxisColor, + const FColor& InZAxisColor, + bool bInPersistentLines, + float InLifeTimeModifier, + uint8 InDepthPriority); }; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGDebugVirtualHandSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGDebugVirtualHandSettings.h index e6f0056f..d4f67bc8 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGDebugVirtualHandSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGDebugVirtualHandSettings.h @@ -55,64 +55,75 @@ public: ESGDebugVirtualHandDrawingMode DrawingMode; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", + EditConditionHides)) FVector CubeExtent; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", + EditConditionHides)) FColor CubeColor; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints", + EditConditionHides)) float CubeThickness; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) float GizmoLength; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) float GizmoThickness; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) FColor GizmoXAxisColor; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) FColor GizmoYAxisColor; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) FColor GizmoZAxisColor; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) uint8 bPersistentLines : 1; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) float LifeTimeModifier; UPROPERTY(EditAnywhere, Category = "Debug Virtual Hand Settings", - meta=(EditCondition="DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", EditConditionHides)) + meta=(EditCondition="bDrawDebugVirtualHand && DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints", + EditConditionHides)) uint8 DepthPriority; public: FSGDebugVirtualHandSettings(); - explicit FSGDebugVirtualHandSettings( + FSGDebugVirtualHandSettings( bool bInDrawDebugVirtualHand, - ESGDebugVirtualHandDrawingMode InDrawingMode = ESGDebugVirtualHandDrawingMode::None, - const FVector& InCubeExtent = FVector(0.5f, 0.5f, 0.5f), - const FColor& InCubeColor = FColor(255, 0, 0, 255), - float InCubeThickness = 0.1f, - float InGizmoLength = 1.0f, - float InGizmoThickness = 0.2f, - const FColor& InGizmoXAxisColor = FColor(255, 0, 0, 255), - const FColor& InGizmoYAxisColor = FColor(0, 255, 0, 255), - const FColor& InGizmoZAxisColor = FColor(0, 0, 255, 255), - bool bInPersistentLines = false, - float InLifeTimeModifier = 1.1f, - uint8 InDepthPriority = 0); + ESGDebugVirtualHandDrawingMode InDrawingMode, + const FVector& InCubeExtent, + const FColor& InCubeColor, + float InCubeThickness, + float InGizmoLength, + float InGizmoThickness, + const FColor& InGizmoXAxisColor, + const FColor& InGizmoYAxisColor, + const FColor& InGizmoZAxisColor, + bool bInPersistentLines, + float InLifeTimeModifier, + uint8 InDepthPriority); }; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGGloveTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGGloveTrackingSettings.h new file mode 100644 index 00000000..f3adb818 --- /dev/null +++ b/Source/SenseGloveSettings/Public/SGSettings/SGGloveTrackingSettings.h @@ -0,0 +1,62 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "UObject/ObjectMacros.h" + +#include "SGGloveTrackingSettings.generated.h" + +/** +* Implements the glove tracking settings for the SenseGlove Tracking module. +*/ +USTRUCT(BlueprintType) +struct SENSEGLOVESETTINGS_API FSGGloveTrackingSettings +{ + GENERATED_USTRUCT_BODY() + +public: + /** + * The interval to check for glove connectivity. The default is 0.125f which means 8 times in each second. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="Glove Tracking") + float GloveConnectivityCheckInterval; + +public: + FSGGloveTrackingSettings(); + + explicit FSGGloveTrackingSettings( + float InGloveConnectivityCheckInterval); +}; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGHMDTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGHMDTrackingSettings.h new file mode 100644 index 00000000..c8672cf2 --- /dev/null +++ b/Source/SenseGloveSettings/Public/SGSettings/SGHMDTrackingSettings.h @@ -0,0 +1,65 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "UObject/ObjectMacros.h" + +#include "SGTypes/SGTrackingTypes.h" + +#include "SGHMDTrackingSettings.generated.h" + +/** +* Implements the hand tracking settings for the SenseGlove Tracking module. +*/ +USTRUCT(BlueprintType) +struct SENSEGLOVESETTINGS_API FSGHMDTrackingSettings +{ + GENERATED_USTRUCT_BODY() + +public: + /** + * Used to determine which VIVE HMD should we try to detect first, since we cannot distinguish between + * HTC VIVE Focus 3 and HTC VIVE XR Elite. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="HMD Tracking") + ESGViveHMDDetectionPriority ViveHMDDetectionPriority; + +public: + FSGHMDTrackingSettings(); + + explicit FSGHMDTrackingSettings( + ESGViveHMDDetectionPriority InViveHmdDetectionPriority); +}; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h new file mode 100644 index 00000000..701c3731 --- /dev/null +++ b/Source/SenseGloveSettings/Public/SGSettings/SGHandTrackingSettings.h @@ -0,0 +1,72 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "UObject/ObjectMacros.h" + +#include "SGHandTrackingSettings.generated.h" + +/** +* Implements the hand tracking settings for the SenseGlove Tracking module. +*/ +USTRUCT(BlueprintType) +struct SENSEGLOVESETTINGS_API FSGHandTrackingSettings +{ + GENERATED_USTRUCT_BODY() + +public: + /** + * If false (the default) the motion sources for hand tracking will be of the form '[Left|Right][Keypoint]'. If + * true they will be of the form 'HandTracking[Left|Right][Keypoint]'. True is recommended to avoid collisions + * between motion sources from different device types. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="Hand Tracking") + uint8 bUseMoreSpecificMotionSourceNames : 1; + + /** + * If true hand tracking supports the 'Left' and 'Right' legacy motion sources. If false it does not. False is + * recommended unless you need legacy compatibility in an older unreal projects. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="Hand Tracking") + uint8 bSupportLegacyControllerMotionSources : 1; + +public: + FSGHandTrackingSettings(); + + FSGHandTrackingSettings( + bool bInUseMoreSpecificMotionSourceNames, + bool bInSupportLegacyControllerMotionSources); +}; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGSettings.h index 76d521a4..40a47033 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGSettings.h @@ -42,10 +42,13 @@ #include "Templates/UniquePtr.h" #include "UObject/ObjectMacros.h" -#include "SGSettings/SGWristTrackingSettings.h" +#include "SGSettings/SGTrackingSettings.h" #include "SGSettings.generated.h" +/** +* Implements the settings for the SenseGlove Unreal Engine Plugin. +*/ UCLASS(config = SenseGloveSettings) class SENSEGLOVESETTINGS_API USGSettings : public UEngineSubsystem { @@ -67,20 +70,20 @@ private: private: UPROPERTY(Config, EditDefaultsOnly, Category = "SenseGlove") - FSGWristTrackingSettings WristTrackingSettings; + FSGTrackingSettings TrackingSettings; private: USGSettings(); public: - FORCEINLINE const FSGWristTrackingSettings& GetWristTrackingSettings() const + FORCEINLINE const FSGTrackingSettings& GetTrackingSettings() const { - return WristTrackingSettings; + return TrackingSettings; } - void SetWristTrackingSettings(const FSGWristTrackingSettings& InWristTrackingSettings) + void SetTrackingSettings(const FSGTrackingSettings& InTrackingSettings) { - WristTrackingSettings = InWristTrackingSettings; + TrackingSettings = InTrackingSettings; } public: diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h new file mode 100644 index 00000000..4bb1a5a2 --- /dev/null +++ b/Source/SenseGloveSettings/Public/SGSettings/SGTrackingSettings.h @@ -0,0 +1,85 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "UObject/ObjectMacros.h" + +#include "SGSettings/SGGloveTrackingSettings.h" +#include "SGSettings/SGHandTrackingSettings.h" +#include "SGSettings/SGHMDTrackingSettings.h" +#include "SGSettings/SGWristTrackingSettings.h" + +#include "SGTrackingSettings.generated.h" + +/** +* Implements the settings for the SenseGlove Tracking module. +*/ +USTRUCT(BlueprintType) +struct SENSEGLOVESETTINGS_API FSGTrackingSettings +{ + GENERATED_USTRUCT_BODY() + +public: + /** + * If false, only a real glove will be tracked, otherwise will fall back to hand tracking if available and + * supported by the HMD device. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="Tracking") + uint8 bFallbackToHandTrackingIfNoGloveDetected : 1; + + UPROPERTY(Config, EditDefaultsOnly, Category="Tracking") + FSGGloveTrackingSettings GloveTrackingSettings; + + UPROPERTY(Config, EditDefaultsOnly, Category="Tracking", + meta=(EditCondition="bFallbackToHandTrackingIfNoGloveDetected", EditConditionHides)) + FSGHandTrackingSettings HandTrackingSettings; + + UPROPERTY(Config, EditDefaultsOnly, Category="Tracking") + FSGHMDTrackingSettings HMDTrackingSettings; + + UPROPERTY(Config, EditDefaultsOnly, Category="Tracking") + FSGWristTrackingSettings WristTrackingSettings; + +public: + FSGTrackingSettings(); + + FSGTrackingSettings( + bool bInFallbackToHandTrackingIfNoGloveDetected, + const FSGGloveTrackingSettings& InGloveTrackingSettings, + const FSGHandTrackingSettings& InHandTrackingSettings, + const FSGHMDTrackingSettings& InHmdTrackingSettings, + const FSGWristTrackingSettings& InWristTrackingSettings); +}; \ No newline at end of file diff --git a/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h b/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h index dec3a58a..af9aa0d5 100644 --- a/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h +++ b/Source/SenseGloveSettings/Public/SGSettings/SGWristTrackingSettings.h @@ -100,20 +100,20 @@ public: /** * If enabled, draws debug Wrist trackers where possible. */ - UPROPERTY(Config, EditDefaultsOnly, Category = "Debug Wrist Tracking") - uint8 bDrawWristTrackerDebugGizmo : 1; + UPROPERTY(Config, EditDefaultsOnly, Category = "Wrist Tracking | Debug") + uint8 bDrawDebugGizmo : 1; /** - * Valid only if bDrawDebugWristTrackerGizmo is enabled. + * Valid only if bDrawDebugGizmo is enabled. */ - UPROPERTY(Config, EditDefaultsOnly, Category = "Debug Wrist Tracking", - meta=(EditCondition="bDrawWristTrackerDebugGizmo", EditConditionHides)) - FSGDebugGizmoSettings WristTrackerDebugGizmoSettings; + UPROPERTY(Config, EditDefaultsOnly, Category = "Wrist Tracking | Debug", + meta=(EditCondition="bDrawDebugGizmo", EditConditionHides)) + FSGDebugGizmoSettings DebugGizmoSettings; public: FSGWristTrackingSettings(); - explicit FSGWristTrackingSettings( + FSGWristTrackingSettings( ESGPositionalTrackingHardware InTrackingHardware, const FVector& InTrackingHardwareLocationOffsetLeftHand, const FVector& InTrackingHardwareLocationOffsetRightHand, @@ -121,6 +121,6 @@ public: const FRotator& InTrackingHardwareRotationOffsetRightHand, EControllerHand InLeftHandMotionSource, EControllerHand InRightHandMotionSource, - bool bInDrawWristTrackerDebugGizmo, - const FSGDebugGizmoSettings& InWristTrackerDebugGizmoSettings); + bool bInDrawDebugGizmo, + const FSGDebugGizmoSettings& InDebugGizmoSettings); }; \ No newline at end of file diff --git a/Source/SenseGloveSettingsKismet/Private/SGSettingsKismet/SGSettingsKismetLibrary.cpp b/Source/SenseGloveSettingsKismet/Private/SGSettingsKismet/SGSettingsKismetLibrary.cpp index 9c69beb8..464ca9fc 100644 --- a/Source/SenseGloveSettingsKismet/Private/SGSettingsKismet/SGSettingsKismetLibrary.cpp +++ b/Source/SenseGloveSettingsKismet/Private/SGSettingsKismet/SGSettingsKismetLibrary.cpp @@ -42,13 +42,13 @@ USGSettings* USGSettingsKismetLibrary::GetInstance() return USGSettings::GetInstance(); } -const FSGWristTrackingSettings& USGSettingsKismetLibrary::GetWristTrackingSettings(const USGSettings* Settings) +const FSGTrackingSettings& USGSettingsKismetLibrary::GetTrackingSettings(const USGSettings* Settings) { - return Settings->GetWristTrackingSettings(); + return Settings->GetTrackingSettings(); } -void USGSettingsKismetLibrary::SetWristTrackingSettings( - USGSettings* Settings, const FSGWristTrackingSettings& InWristTrackingSettings) +void USGSettingsKismetLibrary::SetTrackingSettings( + USGSettings* Settings, const FSGTrackingSettings& InTrackingSettings) { - Settings->SetWristTrackingSettings(InWristTrackingSettings); -} \ No newline at end of file + Settings->SetTrackingSettings(InTrackingSettings); +} diff --git a/Source/SenseGloveSettingsKismet/Public/SGSettingsKismet/SGSettingsKismetLibrary.h b/Source/SenseGloveSettingsKismet/Public/SGSettingsKismet/SGSettingsKismetLibrary.h index 9f975fec..d0c03382 100644 --- a/Source/SenseGloveSettingsKismet/Public/SGSettingsKismet/SGSettingsKismetLibrary.h +++ b/Source/SenseGloveSettingsKismet/Public/SGSettingsKismet/SGSettingsKismetLibrary.h @@ -37,6 +37,7 @@ #include "SGKismet/SGBlueprintFunctionLibrary.h" #include "SGSettings/SGWristTrackingSettings.h" +#include "SGSettings/SGTrackingSettings.h" #include "SGSettingsKismetLibrary.generated.h" @@ -52,9 +53,9 @@ public: static USGSettings* GetInstance(); UFUNCTION(BlueprintPure, Category="SenseGlove | Settings") - static const FSGWristTrackingSettings& GetWristTrackingSettings(const USGSettings* Settings); + static const FSGTrackingSettings& GetTrackingSettings(const USGSettings* Settings); UFUNCTION(BlueprintCallable, Category="SenseGlove | Settings") - static void SetWristTrackingSettings(UPARAM(ref) USGSettings* Settings, - const FSGWristTrackingSettings& InWristTrackingSettings); + static void SetTrackingSettings(UPARAM(ref) USGSettings* Settings, + const FSGTrackingSettings& InTrackingSettings); }; \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGGloveTracker.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGGloveTracker.cpp new file mode 100644 index 00000000..374adb74 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SGTracking/SGGloveTracker.cpp @@ -0,0 +1,279 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGTracking/SGGloveTracker.h" + +#if WITH_EDITOR +#include "Editor.h" +#endif /* WITH_EDITOR */ +#include "Engine/Engine.h" +#include "Engine/TimerHandle.h" +#include "Engine/World.h" +#include "Subsystems/UnrealEditorSubsystem.h" +#include "TimerManager.h" + +#include "SGBackend/SGBackend.h" +#include "SGBuildHacks/SGPlatform.h" +#include "SGCore/SGHapticGlove.h" +#include "SGLog/SGLog.h" +#include "SGSettings/SGSettings.h" +#include "SGSettings/SGTrackingSettings.h" + +struct USGGloveTracker::FImpl +{ + /************************ + * Static methods + ************************/ + + static UWorld* GetWorld(); + + FORCEINLINE static float GetGloveConnectivityCheckInterval() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + return Settings->GetTrackingSettings().GloveTrackingSettings.GloveConnectivityCheckInterval; + } + + /************************ + * Member variables + ************************/ + + FTimerHandle GloveConnectivityCheckTimer; + + /************************ + * Owner object + ************************/ + + USGGloveTracker* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(USGGloveTracker* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = default; + FImpl& operator=(const FImpl& Rhs) = default; + + /************************ + * Methods + ************************/ + + void UpdateGloves() const; +}; + +UWorld* USGGloveTracker::FImpl::GetWorld() +{ + UWorld* World{nullptr}; + +#if WITH_EDITOR + if (GIsEditor) + { + if (IsValid(GEditor)) + { + const bool bIsPIE = GPlayInEditorID != -1; + if (!bIsPIE) + { + const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(1)}; + if (WorldContext) + { + World = WorldContext->World(); + } + } + else + { + const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(GPlayInEditorID)}; + if (WorldContext) + { + World = WorldContext->World(); + } + } + + if (!IsValid(World)) + { + UUnrealEditorSubsystem* UnrealEditorSubsystem{GEditor->GetEditorSubsystem()}; + if (IsValid(UnrealEditorSubsystem)) + { + World = UnrealEditorSubsystem->GetEditorWorld(); + } + } + } + + if (!IsValid(World) && IsValid(GEngine)) + { + const UGameViewportClient* Viewport{GEngine->GameViewport}; + if (IsValid(Viewport)) + { + World = Viewport->GetWorld(); + } + } + } + else + { + if (IsValid(GEngine)) + { + World = GEngine->GetCurrentPlayWorld(nullptr); + } + } +#else /* WITH_EDITOR */ + if (IsValid(GEngine)) + { + World = GEngine->GetCurrentPlayWorld(nullptr); + } +#endif /* WITH_EDITOR */ + + if (!IsValid(World) && IsValid(GEngine)) + { + UEngineSubsystem* EngineSubsystem{GEngine->GetEngineSubsystem()}; + if (IsValid(EngineSubsystem)) + { + World = EngineSubsystem->GetWorld(); + } + } + + return World; +} + +USGGloveTracker* USGGloveTracker::GetInstance() +{ + UWorld* World{FImpl::GetWorld()}; + USGGloveTracker* Instance{IsValid(World) ? World->GetSubsystem() : nullptr}; + return Instance; +} + +USGGloveTracker::USGGloveTracker() + : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)), + LeftGlove(nullptr), + RightGlove(nullptr) +{ +} + +bool USGGloveTracker::ShouldCreateSubsystem(UObject* Outer) const +{ + return true; +} + +void USGGloveTracker::Initialize(FSubsystemCollectionBase& Collection) +{ + SGLOG("Initializing the SenseGlove tracking singleton with the world lifetime..."); + + Super::Initialize(Collection); + + const UWorld* World{GetWorld()}; + if (ensureAlwaysMsgf(World, TEXT("Invalid world!"))) + { + SGLOG("Starting the glove connectivity check timer..."); + + const float GloveConnectivityCheckInterval = FImpl::GetGloveConnectivityCheckInterval(); + + FTimerDelegate GloveConnectivityCheckHandler; + GloveConnectivityCheckHandler.BindLambda([= SG_CAPTURE_THIS]()-> void + { + Pimpl->UpdateGloves(); + }); + + FTimerManager& TimerManager = World->GetTimerManager(); + TimerManager.SetTimer( + Pimpl->GloveConnectivityCheckTimer, GloveConnectivityCheckHandler, GloveConnectivityCheckInterval, + true, -1.0f); + + SGLOG("The glove connectivity check timer has been started successfully!"); + } + + SGLOG("The SenseGlove tracking singleton has been successfully initialized with the world lifetime!"); +} + +void USGGloveTracker::Deinitialize() +{ + SGLOG("De-initializing the SenseGlove tracking singleton due to the world lifetime expiration..."); + + Super::Deinitialize(); + + const UWorld* World{GetWorld()}; + if (ensureAlwaysMsgf(World, TEXT("Invalid world!"))) + { + SGLOG("Stopping the glove connectivity check timer..."); + + FTimerManager& TimerManager = World->GetTimerManager(); + TimerManager.ClearTimer(Pimpl->GloveConnectivityCheckTimer); + + SGLOG("The glove connectivity check timer has been stopped successfully!"); + } + + (void) Pimpl.Release(); + + SGLOG("The SenseGlove tracking singleton has been successfully de-initialized!"); +} + +USGGloveTracker::FImpl::FImpl(USGGloveTracker* InOwner) + : Owner(InOwner) +{ +} + +USGGloveTracker::FImpl::~FImpl() = default; + +void USGGloveTracker::FImpl::UpdateGloves() const +{ + if (!USGBackend::GetInstance()->IsBackendInitialized()) + { + const bool bSucceeded = USGBackend::GetInstance()->InitializeBackend(); + if (!bSucceeded) + { + return; + } + } + + const bool bIsLeftGloveConnected = USGHapticGlove::GetGlove(Owner, false, Owner->LeftGlove); + if (!bIsLeftGloveConnected) + { + Owner->LeftGlove = nullptr; + } + + const bool bIsRightGloveConnected = USGHapticGlove::GetGlove(Owner, true, Owner->RightGlove); + if (!bIsRightGloveConnected) + { + Owner->RightGlove = nullptr; + } +} + +void USGGloveTracker::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} \ No newline at end of file diff --git a/Source/SenseGlove/Private/SenseGlove/SGHeadMountedDisplay.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGHMDTracker.cpp similarity index 90% rename from Source/SenseGlove/Private/SenseGlove/SGHeadMountedDisplay.cpp rename to Source/SenseGloveTracking/Private/SGTracking/SGHMDTracker.cpp index 64bb5330..e4a1ec73 100644 --- a/Source/SenseGlove/Private/SenseGlove/SGHeadMountedDisplay.cpp +++ b/Source/SenseGloveTracking/Private/SGTracking/SGHMDTracker.cpp @@ -33,13 +33,15 @@ */ -#include "SenseGlove/SGHeadMountedDisplay.h" +#include "SGTracking/SGHMDTracker.h" #include "Engine/Engine.h" #include "IHeadMountedDisplay.h" #include "IXRTrackingSystem.h" -FName FSGHeadMountedDisplay::GetDeviceName() +#include "SGSettings/SGSettings.h" + +FName FSGHMDTracker::GetDeviceName() { if (GEngine && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed()) { @@ -53,7 +55,7 @@ FName FSGHeadMountedDisplay::GetDeviceName() return NAME_None; } -bool FSGHeadMountedDisplay::IsMetaQuest2() +bool FSGHMDTracker::IsMetaQuest2() { static const FName MetaQuest2DeviceIdentifier{TEXT("Meta Quest 2")}; static const FName OculusQuest2DeviceIdentifier{TEXT("Oculus Quest2")}; @@ -72,7 +74,7 @@ bool FSGHeadMountedDisplay::IsMetaQuest2() return bMatched; } -bool FSGHeadMountedDisplay::IsMetaQuestPro() +bool FSGHMDTracker::IsMetaQuestPro() { static const FName MetaQuestProDeviceIdentifier{TEXT("Meta Quest Pro")}; static const FName OculusQuestProDeviceIdentifier{TEXT("Oculus QuestPro")}; @@ -91,7 +93,7 @@ bool FSGHeadMountedDisplay::IsMetaQuestPro() return bMatched; } -bool FSGHeadMountedDisplay::IsMetaQuest3() +bool FSGHMDTracker::IsMetaQuest3() { static const FName MetaQuest3DeviceIdentifier{TEXT("Meta Quest 3")}; static const FName OculusQuest3DeviceIdentifier{TEXT("Oculus Quest3")}; @@ -110,7 +112,7 @@ bool FSGHeadMountedDisplay::IsMetaQuest3() return bMatched; } -bool FSGHeadMountedDisplay::IsHtcVivePro() +bool FSGHMDTracker::IsHtcVivePro() { #if PLATFORM_ANDROID return false; @@ -165,7 +167,7 @@ bool FSGHeadMountedDisplay::IsHtcVivePro() #endif /* PLATFORM_ANDROID */ } -bool FSGHeadMountedDisplay::IsHtcViveFocus3() +bool FSGHMDTracker::IsHtcViveFocus3() { static const FName DeviceIdentifier{TEXT("Vive OpenXR: Vive SRanipal")}; static const FString ViveDeviceIdentifier{TEXT("Vive")}; @@ -225,7 +227,7 @@ bool FSGHeadMountedDisplay::IsHtcViveFocus3() return bMatched; } -bool FSGHeadMountedDisplay::IsHtcViveXRElite() +bool FSGHMDTracker::IsHtcViveXRElite() { static const FName DeviceIdentifier{TEXT("Vive OpenXR: Vive SRanipal")}; static const FString ViveDeviceIdentifier{TEXT("Vive")}; @@ -285,7 +287,7 @@ bool FSGHeadMountedDisplay::IsHtcViveXRElite() return bMatched; } -ESGHeadMountedDisplayDevice FSGHeadMountedDisplay::GetDeviceType() +ESGHeadMountedDisplayDevice FSGHMDTracker::GetDeviceType() { if (IsMetaQuest2()) { @@ -307,14 +309,32 @@ ESGHeadMountedDisplayDevice FSGHeadMountedDisplay::GetDeviceType() return ESGHeadMountedDisplayDevice::HtcVivePro; } - if (IsHtcViveFocus3()) + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + if (Settings->GetTrackingSettings().HMDTrackingSettings.ViveHMDDetectionPriority == + ESGViveHMDDetectionPriority::HtcViveFocus3First) { - return ESGHeadMountedDisplayDevice::HtcViveFocus3; - } + if (IsHtcViveFocus3()) + { + return ESGHeadMountedDisplayDevice::HtcViveFocus3; + } - if (IsHtcViveXRElite()) + if (IsHtcViveXRElite()) + { + return ESGHeadMountedDisplayDevice::HtcViveXRElite; + } + } + else { - return ESGHeadMountedDisplayDevice::HtcViveXRElite; + if (IsHtcViveXRElite()) + { + return ESGHeadMountedDisplayDevice::HtcViveXRElite; + } + + if (IsHtcViveFocus3()) + { + return ESGHeadMountedDisplayDevice::HtcViveFocus3; + } } return ESGHeadMountedDisplayDevice::None; diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp new file mode 100644 index 00000000..58e28342 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp @@ -0,0 +1,70 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGTracking/SGXRHandState.h" + +FSGXRHandState::FSGXRHandState() + : HandTracker{}, + JointLocations{}, + JointVelocities{}, + Velocities{XR_TYPE_HAND_JOINT_VELOCITIES_EXT}, + Locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}, + KeypointTransforms{}, + Radii{}, + bReceivedJointPoses(false) +{ + Velocities.jointCount = XR_HAND_JOINT_COUNT_EXT; + Velocities.jointVelocities = JointVelocities; + + Locations.next = &Velocities; + Locations.jointCount = XR_HAND_JOINT_COUNT_EXT; + Locations.jointLocations = JointLocations; +} + +FSGXRHandState::~FSGXRHandState() = default; + +bool FSGXRHandState::GetTransform(const EHandKeypoint Keypoint, FTransform& OutTransform) const +{ + check(static_cast(Keypoint) < EHandKeypointCount); + OutTransform = KeypointTransforms[static_cast(Keypoint)]; + + return !!bReceivedJointPoses; +} + +const FTransform& FSGXRHandState::GetTransform(const EHandKeypoint Keypoint) const +{ + check(static_cast(Keypoint) < EHandKeypointCount); + return KeypointTransforms[static_cast(Keypoint)]; +} \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp new file mode 100644 index 00000000..6016af39 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp @@ -0,0 +1,1771 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGTracking/SGXRTracker.h" + +#include "Containers/Array.h" +#include "Containers/Map.h" +#if WITH_EDITOR +#include "Editor.h" +#endif /* WITH_EDITOR */ +#include "Engine/Engine.h" +#include "Engine/World.h" +#include "GameFramework/WorldSettings.h" +#include "IOpenXRHMDModule.h" +#include "IXRTrackingSystem.h" +#include "OpenXRCore.h" +#include "OpenXRHMD/Private/OpenXRHMD.h" +#include "Runtime/Launch/Resources/Version.h" +#include "Subsystems/UnrealEditorSubsystem.h" + +#include "SGBackend/SGBackend.h" +#include "SGCore/SGHapticGlove.h" +#include "SGLog/SGLog.h" +#include "SGSettings/SGSettings.h" +#include "SGTracking/SGGloveTracker.h" +#include "SGTracking/SGHMDTracker.h" +#include "SGTracking/SGXRHandState.h" +#include "SGTypes/SGCoreTypes.h" +#include "SGTypes/SGTrackingTypes.h" + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION <= 3 +void EnumerateOpenXRApiLayers(TArray& OutProperties) +{ + uint32_t Count = 0; + XR_ENSURE(xrEnumerateApiLayerProperties(0, &Count, nullptr)); + OutProperties.SetNum(Count); + for (auto& Prop: OutProperties) + { + Prop = XrApiLayerProperties{XR_TYPE_API_LAYER_PROPERTIES}; + } + XR_ENSURE(xrEnumerateApiLayerProperties(Count, &Count, OutProperties.GetData())); +} +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION <= 3 */ + +struct FSGXRTracker::FImpl +{ + /************************ + * Typedefs + ************************/ + + // bool true == left, false == right + typedef TPair FSGMotionSourceInfo; + + /************************ + * Static methods + ************************/ + + static UWorld* GetWorld(); + + FORCEINLINE static bool ShouldFallbackToHandTrackingIfNoGloveDetected() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + const bool bFallbackToHandTrackingIfNoGloveDetected = + !!Settings->GetTrackingSettings().bFallbackToHandTrackingIfNoGloveDetected; + return bFallbackToHandTrackingIfNoGloveDetected; + } + + FORCEINLINE static bool ShouldUseMoreSpecificMotionSourceNames() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + const bool bFallbackToHandTrackingIfNoGloveDetected = ShouldFallbackToHandTrackingIfNoGloveDetected(); + const bool bUseMoreSpecificMotionSourceNames = + bFallbackToHandTrackingIfNoGloveDetected + ? !!Settings->GetTrackingSettings().HandTrackingSettings.bUseMoreSpecificMotionSourceNames + : false; + return bUseMoreSpecificMotionSourceNames; + } + + FORCEINLINE static bool ShouldSupportLegacyControllerMotionSources() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + const bool bFallbackToHandTrackingIfNoGloveDetected = ShouldFallbackToHandTrackingIfNoGloveDetected(); + const bool bSupportLegacyControllerMotionSources = + bFallbackToHandTrackingIfNoGloveDetected + ? !!Settings->GetTrackingSettings().HandTrackingSettings.bSupportLegacyControllerMotionSources + : false; + return bSupportLegacyControllerMotionSources; + } + + static const FSGWristTrackingSettings& GetWristTrackingSettings() + { + const USGSettings* Settings{USGSettings::GetInstance()}; + ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!")); + return Settings->GetTrackingSettings().WristTrackingSettings; + } + + static ESGPositionalTrackingHardware GetPositionalTrackingHardware(); + + FORCEINLINE static USGHapticGlove* GetGlove(const EControllerHand Hand) + { + const bool bIsRightHand = Hand == EControllerHand::Right; + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->GetGlove(bIsRightHand) : nullptr; + } + + FORCEINLINE static USGHapticGlove* GetGlove(const bool bRight) + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->GetGlove(bRight) : nullptr; + } + + FORCEINLINE static USGHapticGlove* GetLeftGlove() + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->GetLeftGlove() : nullptr; + } + + FORCEINLINE static USGHapticGlove* GetRightGlove() + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->GetRightGlove() : nullptr; + } + + FORCEINLINE static bool IsGloveConnected(const EControllerHand Hand) + { + const bool bIsRightHand = Hand == EControllerHand::Right; + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->IsGloveConnected(bIsRightHand) : false; + } + + FORCEINLINE static bool IsGloveConnected(const bool bRight) + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->IsGloveConnected(bRight) : false; + } + + FORCEINLINE static bool IsLeftGloveConnected() + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->IsLeftGloveConnected() : false; + } + + FORCEINLINE static bool IsRightGloveConnected() + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->IsRightGloveConnected() : false; + } + + FORCEINLINE static bool IsAnyGloveConnected() + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance()}; + return IsValid(GloveTracker) ? GloveTracker->IsAnyGloveConnected() : false; + } + + /************************ + * Member variables + ************************/ + + uint8 bHandTrackingAvailable : 1; + + PFN_xrCreateHandTrackerEXT XRCreateHandTrackerEXT; + PFN_xrDestroyHandTrackerEXT XRDestroyHandTrackerEXT; + PFN_xrLocateHandJointsEXT XRLocateHandJointsEXT; + + IXRTrackingSystem* XRTrackingSystem; + + TSharedPtr MessageHandler; + int32 DeviceIndex; + + int32 CurrentHandTrackingDataIndex; + + TArray BoneParents; + TArray BoneKeypoints; + TMap MotionSourceToKeypointMap; + + FSGXRHandState HandStates[2]; + + TArray LeftAnimationTransforms; + TArray RightAnimationTransforms; + + /************************ + * Owner object + ************************/ + + FSGXRTracker* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(FSGXRTracker* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = delete; + FImpl& operator=(const FImpl& Rhs) = delete; + + /************************ + * Methods + ************************/ + + FSGXRHandState& GetLeftHandState(); + FSGXRHandState& GetRightHandState(); + + void AddKeys(); + void BuildMotionSourceToKeypointMap(); + + bool CanOutputTrackingData(EControllerHand Hand) const; + + bool UpdateXRHandTrackingData(FSGXRHandState& OutHandState, const XrHandJointsLocateInfoEXT& LocateInfo) const; + + bool GetOpenXRHandTrackingWristLocationAndRotation(EControllerHand DeviceHand, + FVector& OutWristLocation, FQuat& OutWristRotation) const; + bool GetOpenXRWristLocationAndRotation(const EControllerHand DeviceHand, + const IMotionController* MotionController, const FName& MotionSource, + FVector& OutWristLocation, FQuat& OutWristRotation) const; + bool GetWristLocationAndRotation(EControllerHand DeviceHand, + FVector& OutWristLocation, FQuat& OutWristRotation, + bool& bOutMotionSourceHandTracking) const; + + bool UpdateSGJointData(FSGXRHandState& OutHandState, const EHandKeypoint Joint, + const FVector& JointLocation, const FQuat& JointRotation, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGWristJointData(FSGXRHandState& OutHandState, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGPalmJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGThumbJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGIndexJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGMiddleJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGRingJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGLittleJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + bool bMotionSourceHandTracking) const; + + bool UpdateSGHandState(const EControllerHand DeviceHand, FSGXRHandState& OutHandState) const; +}; + +UWorld* FSGXRTracker::FImpl::GetWorld() +{ + UWorld* World{nullptr}; + +#if WITH_EDITOR + if (GIsEditor) + { + if (IsValid(GEditor)) + { + const bool bIsPIE = GPlayInEditorID != -1; + if (!bIsPIE) + { + const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(1)}; + if (WorldContext) + { + World = WorldContext->World(); + } + } + else + { + const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(GPlayInEditorID)}; + if (WorldContext) + { + World = WorldContext->World(); + } + } + + if (!IsValid(World)) + { + UUnrealEditorSubsystem* UnrealEditorSubsystem{GEditor->GetEditorSubsystem()}; + if (IsValid(UnrealEditorSubsystem)) + { + World = UnrealEditorSubsystem->GetEditorWorld(); + } + } + } + + if (!IsValid(World) && IsValid(GEngine)) + { + const UGameViewportClient* Viewport{GEngine->GameViewport}; + if (IsValid(Viewport)) + { + World = Viewport->GetWorld(); + } + } + } + else + { + if (IsValid(GEngine)) + { + World = GEngine->GetCurrentPlayWorld(nullptr); + } + } +#else /* WITH_EDITOR */ + if (IsValid(GEngine)) + { + World = GEngine->GetCurrentPlayWorld(nullptr); + } +#endif /* WITH_EDITOR */ + + if (!IsValid(World) && IsValid(GEngine)) + { + UEngineSubsystem* EngineSubsystem{GEngine->GetEngineSubsystem()}; + if (IsValid(EngineSubsystem)) + { + World = EngineSubsystem->GetWorld(); + } + } + + return World; +} + +ESGPositionalTrackingHardware FSGXRTracker::FImpl::GetPositionalTrackingHardware() +{ + ESGPositionalTrackingHardware TrackerHardware = GetWristTrackingSettings().TrackingHardware; + + if (TrackerHardware == ESGPositionalTrackingHardware::None) + { + const ESGHeadMountedDisplayDevice HmdDevice = FSGHMDTracker::GetDeviceType(); + switch (HmdDevice) + { + case ESGHeadMountedDisplayDevice::MetaQuest2: + TrackerHardware = ESGPositionalTrackingHardware::Quest2Controller; + break; + case ESGHeadMountedDisplayDevice::MetaQuestPro: + TrackerHardware = ESGPositionalTrackingHardware::QuestProController; + break; + case ESGHeadMountedDisplayDevice::MetaQuest3: + TrackerHardware = ESGPositionalTrackingHardware::Quest3Controller; + break; + case ESGHeadMountedDisplayDevice::HtcVivePro: + TrackerHardware = ESGPositionalTrackingHardware::ViveTracker; + break; + case ESGHeadMountedDisplayDevice::HtcViveFocus3: + // Fall through + case ESGHeadMountedDisplayDevice::HtcViveXRElite: + TrackerHardware = ESGPositionalTrackingHardware::ViveFocus3WristTracker; + break; + default: + TrackerHardware = ESGPositionalTrackingHardware::Custom; + break; + } + } + + return TrackerHardware; +} + +FName FSGXRTracker::ParseEOpenXRHandKeypointEnumName(const FName& EnumName) +{ + static const int32 EnumNameLength = FString{TEXT("EHandKeypoint::")}.Len(); + const FString EnumString{EnumName.ToString()}; + const FName ParsedName{*EnumString.Right(EnumString.Len() - EnumNameLength)}; + return ParsedName; +} + +FSGXRTracker::FSGXRTracker(const TSharedRef& InMessageHandler) + : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) +{ + Pimpl->MessageHandler = InMessageHandler; + + // Register the modular features manually. + IModularFeatures::Get().RegisterModularFeature(IHandTracker::GetModularFeatureName(), + static_cast(this)); + IModularFeatures::Get().RegisterModularFeature(IMotionController::GetModularFeatureName(), + static_cast(this)); + IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), + static_cast(this)); + Pimpl->AddKeys(); + + // We're implicitly requiring that the OpenXRPlugin has been loaded and initialized at this point. + if (!IOpenXRHMDModule::Get().IsAvailable()) + { + SGLOG_FATAL("The OpenXRHMD plugin isn't available!"); + ensureAlwaysMsgf(false, TEXT("The OpenXRHMD plugin isn't available!")); + } +} + +FSGXRTracker::FSGXRTracker(FSGXRTracker&& Rhs) SG_NOEXCEPT = default; + +FSGXRTracker::~FSGXRTracker() = default; + +FSGXRTracker& FSGXRTracker::operator=(FSGXRTracker&& Rhs) SG_NOEXCEPT = default; + +bool FSGXRTracker::IsHandTrackingSupportedByDevice() const +{ + const bool bFallbackToHandTrackingIfNoGloveDetected = FImpl::ShouldFallbackToHandTrackingIfNoGloveDetected(); + + const bool bAnyGloveConnected = FImpl::IsAnyGloveConnected(); + const bool bHandTrackingAvailable = !!Pimpl->bHandTrackingAvailable; + + const bool bHandTrackingSupportedByDevice = + bAnyGloveConnected || (bFallbackToHandTrackingIfNoGloveDetected && bHandTrackingAvailable); + + return bHandTrackingSupportedByDevice; +} + +const FSGXRHandState& FSGXRTracker::GetLeftHandState() const +{ + return Pimpl->GetLeftHandState(); +} + +const FSGXRHandState& FSGXRTracker::GetRightHandState() const +{ + return Pimpl->GetRightHandState(); +} + +FString FSGXRTracker::GetDisplayName() +{ + static const FString Name{TEXT("SenseGloveTracking")}; + return Name; +} + +bool FSGXRTracker::GetRequiredExtensions(TArray& OutExtensions) +{ + TArray Properties; + EnumerateOpenXRApiLayers(Properties); + + // Some API layers can crash the loader when enabled, if they're present we shouldn't enable the extension. + for (const XrApiLayerProperties& Layer: Properties) + { + if (FCStringAnsi::Strstr(Layer.layerName, "XR_APILAYER_VIVE_hand_tracking") + && Layer.layerVersion <= 1) + { + return false; + } + } + + OutExtensions.Add("XR_EXT_hand_tracking"); + + return true; +} + +const void* FSGXRTracker::OnGetSystem(const XrInstance InInstance, const void* InNext) +{ + // Store the extension open xr calls to member function pointers for convenient use. + XR_ENSURE(xrGetInstanceProcAddr(InInstance, + "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)&Pimpl->XRCreateHandTrackerEXT)); + XR_ENSURE(xrGetInstanceProcAddr(InInstance, + "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)&Pimpl->XRDestroyHandTrackerEXT)); + XR_ENSURE(xrGetInstanceProcAddr(InInstance, + "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)&Pimpl->XRLocateHandJointsEXT)); + + return InNext; +} + +const void* FSGXRTracker::OnCreateSession(const XrInstance InInstance, const XrSystemId InSystem, const void* InNext) +{ + // Need to wait until the EHandKeypoint enum has been loaded, so we do this here rather than in the constructor + // which runs too early. + Pimpl->BuildMotionSourceToKeypointMap(); + + XrSystemHandTrackingPropertiesEXT HandTrackingSystemProperties{ + XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}; + XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES, + &HandTrackingSystemProperties}; + XR_ENSURE(xrGetSystemProperties(InInstance, InSystem, &systemProperties)); + + Pimpl->bHandTrackingAvailable = HandTrackingSystemProperties.supportsHandTracking == XR_TRUE; + + return InNext; +} + +const void* FSGXRTracker::OnBeginSession(const XrSession InSession, const void* InNext) +{ + const bool bHandTrackingAvailable = !!Pimpl->bHandTrackingAvailable; + + if (bHandTrackingAvailable) + { + static FName SystemName(TEXT("OpenXR")); + if (IsValid(GEngine) && GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName)) + { + Pimpl->XRTrackingSystem = GEngine->XRSystem.Get(); + } + + // Create a hand tracker for the left hand that tracks default set of hand joints. + FSGXRHandState& LeftHandState{Pimpl->GetLeftHandState()}; + { + XrHandTrackerCreateInfoEXT CreateInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + CreateInfo.hand = XR_HAND_LEFT_EXT; + CreateInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + XR_ENSURE(Pimpl->XRCreateHandTrackerEXT(InSession, &CreateInfo, &LeftHandState.HandTracker)); + } + + // Create a hand tracker for the right hand that tracks default set of hand joints. + FSGXRHandState& RightHandState{Pimpl->GetRightHandState()}; + { + XrHandTrackerCreateInfoEXT CreateInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + CreateInfo.hand = XR_HAND_RIGHT_EXT; + CreateInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + XR_ENSURE(Pimpl->XRCreateHandTrackerEXT(InSession, &CreateInfo, &RightHandState.HandTracker)); + } + } + + return InNext; +} + +void FSGXRTracker::UpdateDeviceLocations( + const XrSession InSession, const XrTime DisplayTime, const XrSpace TrackingSpace) +{ + if (!IsHandTrackingStateValid()) + { + return; + } + + XrHandJointsLocateInfoEXT LocateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + LocateInfo.baseSpace = TrackingSpace; + LocateInfo.time = DisplayTime; + + const bool bLeftGloveConnected = FImpl::IsLeftGloveConnected(); + if (bLeftGloveConnected) + { + /// NOTE + /// It's necessary to call the UpdateXRHandTrackingData first for the wrist tracking to work properly when the + /// motion controllers are not active and we rely on the OpenXRHandTracking data for wrist tracking. + /// See the NOTE inside the FSGXRTracker::FImpl::GetWristLocationAndRotation method. + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[0], LocateInfo); + Pimpl->UpdateSGHandState(EControllerHand::Left, Pimpl->HandStates[0]); + } + else + { + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[0], LocateInfo); + } + + const bool bRightGloveConnected = FImpl::IsRightGloveConnected(); + if (bRightGloveConnected) + { + /// NOTE + /// It's necessary to call the UpdateXRHandTrackingData first for the wrist tracking to work properly when the + /// motion controllers are not active and we rely on the OpenXRHandTracking data for wrist tracking. + /// See the NOTE inside the FSGXRTracker::FImpl::GetWristLocationAndRotation method. + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[1], LocateInfo); + Pimpl->UpdateSGHandState(EControllerHand::Right, Pimpl->HandStates[1]); + } + else + { + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[1], LocateInfo); + } +} + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 +bool FSGXRTracker::GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, + FRotator& OutOrientation, FVector& OutPosition, + const float WorldToMetersScale) const +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ +bool FSGXRTracker::GetControllerOrientationAndPosition(const int32 ControllerIndex, const EControllerHand DeviceHand, + FRotator& OutOrientation, FVector& OutPosition, + const float WorldToMetersScale) const +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ +{ + if (!IsHandTrackingStateValid()) + { + return false; + } + + // Hand tracking currently does not support late update. Current hand tracking systems have latency that make it + // pointless. + if (!IsInGameThread()) + { + return false; + } + + bool bTracked = false; + if (ControllerIndex == Pimpl->DeviceIndex) + { + FTransform ControllerTransform{FTransform::Identity}; + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + const FImpl::FSGMotionSourceInfo* KeyPointInfoPtr{Pimpl->MotionSourceToKeypointMap.Find(MotionSource)}; + if (KeyPointInfoPtr) +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + if (GetControllerTrackingStatus(ControllerIndex, DeviceHand) != ETrackingStatus::NotTracked) +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + { +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + const EHandKeypoint KeyPoint = KeyPointInfoPtr->Key; + const bool bIsLeft = KeyPointInfoPtr->Value; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + const bool bIsLeft = DeviceHand == EControllerHand::Left; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + if (bIsLeft) + { +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + ControllerTransform = GetLeftHandState().GetTransform(KeyPoint); +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + ControllerTransform = GetLeftHandState().GetTransform(EHandKeypoint::Palm); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + bTracked = !!GetLeftHandState().bReceivedJointPoses; + } + else + { +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + ControllerTransform = GetRightHandState().GetTransform(KeyPoint); +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + ControllerTransform = GetRightHandState().GetTransform(EHandKeypoint::Palm); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + bTracked = !!GetRightHandState().bReceivedJointPoses; + } + } + + if (bTracked) + { + OutPosition = ControllerTransform.GetLocation(); + OutOrientation = ControllerTransform.GetRotation().Rotator(); + } + else + { + static const FName OpenXRName{TEXT("OpenXR")}; + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION <= 2 + const bool bUseMoreSpecificMotionSourceNames = FImpl::ShouldUseMoreSpecificMotionSourceNames(); + + static const FName + LeftName(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left")); + static const FName RightName(bUseMoreSpecificMotionSourceNames + ? TEXT("HandTrackingRight") + : TEXT("Right")); + + FName MotionSource; + if (DeviceHand == EControllerHand::Left) + { + MotionSource = LeftName; + } + else if (DeviceHand == EControllerHand::Right) + { + MotionSource = RightName; + } +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION <= 2 */ + + const TArray MotionControllers{ + IModularFeatures::Get().GetModularFeatureImplementations( + IMotionController::GetModularFeatureName()) + }; + + for (const IMotionController* MotionController: MotionControllers) + { + if (!MotionController) + { + continue; + } + + const FName DeviceTypeName{MotionController->GetMotionControllerDeviceTypeName()}; + if (DeviceTypeName != OpenXRName) + { + continue; + } + + const ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus(0, MotionSource); + if (TrackingStatus != ETrackingStatus::Tracked) + { + continue; + } + + const bool bGotTransform = MotionController->GetControllerOrientationAndPosition( + ControllerIndex, MotionSource, OutOrientation, OutPosition, WorldToMetersScale); + if (bGotTransform) + { + bTracked = true; + break; + } + } + } + } + + return bTracked; +} + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 +ETrackingStatus FSGXRTracker::GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ +ETrackingStatus FSGXRTracker::GetControllerTrackingStatus(const int32 ControllerIndex, + const EControllerHand DeviceHand) const +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ +{ + if (!IsHandTrackingStateValid()) + { + return ETrackingStatus::NotTracked; + } + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + const FImpl::FSGMotionSourceInfo* KeyPointInfoPtr{Pimpl->MotionSourceToKeypointMap.Find(MotionSource)}; + if (KeyPointInfoPtr) + { + const bool bIsLeft = KeyPointInfoPtr->Value; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + const bool bIsLeft = DeviceHand == EControllerHand::Left; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + + if ((bIsLeft && FImpl::IsLeftGloveConnected()) || (!bIsLeft && FImpl::IsRightGloveConnected())) + { + return ETrackingStatus::Tracked; + } + + const FSGXRHandState& HandState{bIsLeft ? GetLeftHandState() : GetRightHandState()}; + return !!HandState.bReceivedJointPoses ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked; +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + } +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + + return ETrackingStatus::NotTracked; +} + +FName FSGXRTracker::GetMotionControllerDeviceTypeName() const +{ + const static FName DefaultName(TEXT("OpenXRHandTracking")); + return DefaultName; +} + +void FSGXRTracker::EnumerateSources(TArray& SourcesOut) const +{ + ensureAlwaysMsgf(IsInGameThread(), TEXT("Is not in the game thread!")); + + const bool bUseMoreSpecificMotionSourceNames = FImpl::ShouldUseMoreSpecificMotionSourceNames(); + + SourcesOut.Reserve(SourcesOut.Num() + (EHandKeypointCount * 2)); + + const UEnum* EnumPtr{ + FindObject(nullptr, TEXT("/Script/HeadMountedDisplay.EHandKeypoint"), true) + }; + ensureAlwaysMsgf(EnumPtr, TEXT("Failed to find the HMD hand keypoint!")); + + const FString Left(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left")); + const FString Right(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingRight") : TEXT("Right")); + + for (int32 Keypoint = 0; Keypoint < EHandKeypointCount; Keypoint++) + { + const FString EnumString{ParseEOpenXRHandKeypointEnumName(EnumPtr->GetNameByValue(Keypoint)).ToString()}; + const FString StringLeft{FString::Printf(TEXT("%s%s"), *Left, *EnumString)}; + const FString StringRight{FString::Printf(TEXT("%s%s"), *Right, *EnumString)}; + FName SourceL(*(StringLeft)); + FName SourceR(*(StringRight)); + SourcesOut.Add(MoveTemp(SourceL)); + SourcesOut.Add(MoveTemp(SourceR)); + } +} + +void FSGXRTracker::Tick(const float DeltaTime) +{ +} + +void FSGXRTracker::SendControllerEvents() +{ +} + +void FSGXRTracker::SetMessageHandler(const TSharedRef& InMessageHandler) +{ + Pimpl->MessageHandler = InMessageHandler; +} + +bool FSGXRTracker::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) +{ + return false; +} + +void FSGXRTracker::SetChannelValue( + const int32 ControllerId, const FForceFeedbackChannelType ChannelType, const float Value) +{ +} + +void FSGXRTracker::SetChannelValues(const int32 ControllerId, const FForceFeedbackValues& Values) +{ +} + +bool FSGXRTracker::SupportsForceFeedback(const int32 ControllerId) +{ + // FIXME + // For now it's true, but when we fall back to the hand tracking we might return false. + (void) ControllerId; + return true; +} + +bool FSGXRTracker::IsGamepadAttached() const +{ + return false; +} + +FName FSGXRTracker::GetHandTrackerDeviceTypeName() const +{ + return GetMotionControllerDeviceTypeName(); +} + +bool FSGXRTracker::IsHandTrackingStateValid() const +{ + const bool bFallbackToHandTrackingIfNoGloveDetected = FImpl::ShouldFallbackToHandTrackingIfNoGloveDetected(); + + const bool bHandTrackingAvailable = !!Pimpl->bHandTrackingAvailable; + const bool bAnyGloveConnected = FImpl::IsAnyGloveConnected(); + + const bool bHandTrackingStateValid = + bAnyGloveConnected || (bFallbackToHandTrackingIfNoGloveDetected && bHandTrackingAvailable); + + return bHandTrackingStateValid; +} + +bool FSGXRTracker::GetKeypointState( + const EControllerHand Hand, const EHandKeypoint Keypoint, FTransform& OutTransform, float& OutRadius) const +{ + if (!IsHandTrackingStateValid()) + { + return false; + } + + if (!Pimpl->CanOutputTrackingData(Hand)) + { + return false; + } + + bool bGotTransform = false; + + /// NOTE + // Currently there is no OpenXR input simulation implementation. Maybe we will do that soon though? Leaving this + // for reference for now. + //#if WITH_INPUT_SIMULATION + // if (auto* InputSim = UOpenXRInputSimulationEngineSubsystem::GetInputSimulationIfEnabled()) + // { + // bGotTransform = InputSim->GetHandJointTransform(Hand, Keypoint, OutTransform); + // OutRadius = FSGXRHandState.Radii[static_cast(Keypoint)]; + // } + // else + //#endif + { + const FSGXRHandState& HandState{ + Hand == EControllerHand::Left ? GetLeftHandState() : GetRightHandState() + }; + bGotTransform = HandState.GetTransform(Keypoint, OutTransform); + OutRadius = HandState.Radii[static_cast(Keypoint)]; + } + + if (bGotTransform) + { + if (!ensureAlwaysMsgf(Pimpl->XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + // Convert to UE world space. + const FTransform& TrackingToWorldTransform{Pimpl->XRTrackingSystem->GetTrackingToWorldTransform()}; + OutTransform *= TrackingToWorldTransform; + } + + return bGotTransform; +} + +bool FSGXRTracker::GetAllKeypointStates( + const EControllerHand Hand, + TArray& OutPositions, TArray& OutRotations, TArray& OutRadii) const +{ + if (!IsHandTrackingStateValid()) + { + return false; + } + + if (Hand != EControllerHand::Left && Hand != EControllerHand::Right) + { + return false; + } + + if (!Pimpl->CanOutputTrackingData(Hand)) + { + return false; + } + + const FSGXRHandState& HandState{ + Hand == EControllerHand::Left ? GetLeftHandState() : GetRightHandState() + }; + + if (!HandState.bReceivedJointPoses) + { + return false; + } + + OutPositions.Empty(EHandKeypointCount); + OutRotations.Empty(EHandKeypointCount); + OutRadii.Empty(EHandKeypointCount); + + if (!ensureAlwaysMsgf(Pimpl->XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + const FTransform& TrackingToWorldTransform{Pimpl->XRTrackingSystem->GetTrackingToWorldTransform()}; + + for (int i = 0; i < EHandKeypointCount; ++i) + { + FTransform KeypointWorldTransform{HandState.KeypointTransforms[i] * TrackingToWorldTransform}; + OutPositions.Add(KeypointWorldTransform.GetLocation()); + OutRotations.Add(KeypointWorldTransform.GetRotation()); + } + + for (int i = 0; i < EHandKeypointCount; ++i) + { + OutRadii.Add(HandState.Radii[i]); + } + + return true; +} + +FSGXRTracker::FImpl::FImpl(FSGXRTracker* InOwner) + : bHandTrackingAvailable(false), + XRCreateHandTrackerEXT(nullptr), + XRDestroyHandTrackerEXT(nullptr), + XRLocateHandJointsEXT(nullptr), + XRTrackingSystem(nullptr), + DeviceIndex(0), + CurrentHandTrackingDataIndex(0), + Owner(InOwner) +{ +} + +FSGXRTracker::FImpl::~FImpl() = default; + +FSGXRHandState& FSGXRTracker::FImpl::GetLeftHandState() +{ + return HandStates[0]; +} + +FSGXRHandState& FSGXRTracker::FImpl::GetRightHandState() +{ + return HandStates[1]; +} + +void FSGXRTracker::FImpl::AddKeys() +{ +} + +void FSGXRTracker::FImpl::BuildMotionSourceToKeypointMap() +{ + if (!MotionSourceToKeypointMap.IsEmpty()) + { + return; + } + + const bool bUseMoreSpecificMotionSourceNames = ShouldUseMoreSpecificMotionSourceNames(); + const bool bSupportLegacyControllerMotionSources = ShouldSupportLegacyControllerMotionSources(); + + // There is a motion source that corresponds to each hand keypoint of the form [Left|Right][Keypoint]. + // Build a map so we can quickly translate from motion source FName to hand bone EHandKeypoint value. + // We also have the option of using more specific motion sources of the form HandTracking[Left|Right][Keypoint]. + // This is useful if one wishes to use hand tracking and controllers simultaneously. + // We also may support more generic legacy motion sources, by default we do support this. + const UEnum* EnumPtr{ + FindObject(nullptr, TEXT("/Script/HeadMountedDisplay.EHandKeypoint"), true) + }; + ensureAlwaysMsgf(EnumPtr, TEXT("Failed to find the HMD hand keypoint!")); + + ensureAlwaysMsgf(IsInGameThread(), TEXT("Is not in the game thread!")); + + const FString Left(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left")); + const FString Right(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingRight") : TEXT("Right")); + + for (int64 e = 0; e < EHandKeypointCount; ++e) + { + const EHandKeypoint EnumValue = static_cast(e); + const FString EnumName{EnumPtr->GetNameStringByValue(e)}; + + FName LeftName(Left + EnumName); + FName RightName(Right + EnumName); + MotionSourceToKeypointMap.Add(MoveTemp(LeftName), FSGMotionSourceInfo(EnumValue, true)); + MotionSourceToKeypointMap.Add(MoveTemp(RightName), FSGMotionSourceInfo(EnumValue, false)); + } + + if (bSupportLegacyControllerMotionSources) + { + MotionSourceToKeypointMap.Add(FName("Left"), FSGMotionSourceInfo(EHandKeypoint::Palm, true)); + MotionSourceToKeypointMap.Add(FName("Right"), FSGMotionSourceInfo(EHandKeypoint::Palm, false)); + } +} + +bool FSGXRTracker::FImpl::CanOutputTrackingData(const EControllerHand Hand) const +{ + const bool bFallbackToHandTrackingIfNoGloveDetected = ShouldFallbackToHandTrackingIfNoGloveDetected(); + + const bool bIsHandTrackingAvailable = !!bHandTrackingAvailable; + + bool bGloveConnected = false; + if (Hand == EControllerHand::Left) + { + bGloveConnected = IsLeftGloveConnected(); + } + else if (Hand == EControllerHand::Right) + { + bGloveConnected = IsRightGloveConnected(); + } + + const bool bCanOutputTrackingData = + bGloveConnected || (bFallbackToHandTrackingIfNoGloveDetected && bIsHandTrackingAvailable); + + return bCanOutputTrackingData; +} + +bool FSGXRTracker::FImpl::UpdateXRHandTrackingData( + FSGXRHandState& OutHandState, const XrHandJointsLocateInfoEXT& LocateInfo) const +{ + if (!ensureAlwaysMsgf(XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + const float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale(); + + XR_ENSURE(XRLocateHandJointsEXT(OutHandState.HandTracker, &LocateInfo, &OutHandState.Locations)); + + OutHandState.bReceivedJointPoses = OutHandState.Locations.isActive == XR_TRUE; + if (!!OutHandState.bReceivedJointPoses) + { + static_assert( + XR_HAND_JOINT_PALM_EXT == 0 && XR_HAND_JOINT_LITTLE_TIP_EXT == XR_HAND_JOINT_COUNT_EXT - 1, + "XrHandJointEXT enum is not as assumed for the following loop!"); + + for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j) + { + const XrHandJointLocationEXT& JointLocation{OutHandState.JointLocations[j]}; + const XrPosef& Pose{JointLocation.pose}; + FTransform Transform{ToFTransform(Pose, WorldToMetersScale)}; + OutHandState.KeypointTransforms[j] = MoveTemp(Transform); + OutHandState.Radii[j] = JointLocation.radius * WorldToMetersScale; + // velocities would go here + } + + return true; + } + + return false; +} + +bool FSGXRTracker::FImpl::GetOpenXRHandTrackingWristLocationAndRotation( + const EControllerHand DeviceHand, FVector& OutWristLocation, FQuat& OutWristRotation) const +{ + if (!ensureAlwaysMsgf(XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + UWorld* World{GetWorld()}; + + FXRMotionControllerData MotionControllerData; + XRTrackingSystem->GetMotionControllerData(World, DeviceHand, MotionControllerData); + if (MotionControllerData.bValid) + { + static constexpr int32_t WristJointIndex = static_cast(EHandKeypoint::Wrist); + OutWristLocation = MotionControllerData.HandKeyPositions[WristJointIndex]; + OutWristRotation = MotionControllerData.HandKeyRotations[WristJointIndex]; + return true; + } + + return false; +} + +bool FSGXRTracker::FImpl::GetOpenXRWristLocationAndRotation( + const EControllerHand DeviceHand, + const IMotionController* MotionController, const FName& MotionSource, + FVector& OutWristLocation, FQuat& OutWristRotation) const +{ + static constexpr int32 CurrentPlayerControllerIndex = 0; + + const UWorld* World{GetWorld()}; + const float WorldToMetersScale = World ? World->GetWorldSettings()->WorldToMeters : 100.0f; + + FVector TrackerLocation; + FRotator TrackerRotation; + const bool bGotTransform = MotionController->GetControllerOrientationAndPosition( + CurrentPlayerControllerIndex, MotionSource, TrackerRotation, TrackerLocation, WorldToMetersScale); + if (bGotTransform) + { + const bool bIsRight = DeviceHand == EControllerHand::Right; + + const ESGPositionalTrackingHardware TrackingHardware = GetPositionalTrackingHardware(); + if (TrackingHardware == ESGPositionalTrackingHardware::Custom) + { + FSGWristTrackingSettings Settings{GetWristTrackingSettings()}; + if (bIsRight) + { + TrackerLocation += Settings.TrackingHardwareLocationOffsetRightHand; + TrackerRotation += Settings.TrackingHardwareRotationOffsetRightHand; + } + else + { + TrackerLocation += Settings.TrackingHardwareLocationOffsetLeftHand; + TrackerRotation += Settings.TrackingHardwareRotationOffsetLeftHand; + } + } + + FVector WristLocation; + FRotator WristRotation; + + USGHapticGlove* Glove = GetGlove(bIsRight); + if (!IsValid(Glove)) + { + return false; + } + + Glove->GetWristLocation(TrackerLocation, TrackerRotation, TrackingHardware, + WristLocation, WristRotation); + + OutWristLocation = MoveTemp(WristLocation); + OutWristRotation = FQuat(MoveTemp(WristRotation)); + + return true; + } + + return false; +} + +bool FSGXRTracker::FImpl::GetWristLocationAndRotation( + const EControllerHand DeviceHand, FVector& OutWristLocation, FQuat& OutWristRotation, + bool& bOutMotionSourceHandTracking) const +{ + static const FName OpenXRName{TEXT("OpenXR")}; + static const FName OpenXRHandTrackingName{TEXT("OpenXRHandTracking")}; + + const bool bUseMoreSpecificMotionSourceNames = ShouldUseMoreSpecificMotionSourceNames(); + + static const FName LeftName(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingLeft") : TEXT("Left")); + static const FName RightName(bUseMoreSpecificMotionSourceNames ? TEXT("HandTrackingRight") : TEXT("Right")); + + FName MotionSource; + if (DeviceHand == EControllerHand::Left) + { + MotionSource = LeftName; + } + else if (DeviceHand == EControllerHand::Right) + { + MotionSource = RightName; + } + + static constexpr int32 CurrentPlayerControllerIndex = 0; + + TArray MotionControllers{IModularFeatures::Get().GetModularFeatureImplementations< + IMotionController>(IMotionController::GetModularFeatureName())}; + + for (const IMotionController* MotionController: MotionControllers) + { + if (!MotionController) + { + continue; + } + + ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus( + CurrentPlayerControllerIndex, MotionSource); + if (TrackingStatus != ETrackingStatus::Tracked) + { + continue; + } + + FName DeviceTypeName{MotionController->GetMotionControllerDeviceTypeName()}; + + /// NOTE + /// We have to always check the wrist tracking data from the OpenXRHandTracking system instead of OpenXR, + /// otherwise even if the motion controllers become deactivated their tracking status sometimes mysteriously + /// reported as Tracked. + + if (DeviceTypeName == OpenXRHandTrackingName) + { + bOutMotionSourceHandTracking = true; + return GetOpenXRHandTrackingWristLocationAndRotation(DeviceHand, OutWristLocation, OutWristRotation); + } + + if (DeviceTypeName == OpenXRName) + { + bOutMotionSourceHandTracking = false; + return GetOpenXRWristLocationAndRotation(DeviceHand, MotionController, MotionSource, + OutWristLocation, OutWristRotation); + } + } + + return false; +} + +bool FSGXRTracker::FImpl::UpdateSGJointData( + FSGXRHandState& OutHandState, const EHandKeypoint Joint, + const FVector& JointLocation, const FQuat& JointRotation, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + if (!ensureAlwaysMsgf(XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + const FTransform& TrackingToWorldTransform{XRTrackingSystem->GetTrackingToWorldTransform()}; + const FTransform& WorldToTrackingTransform{TrackingToWorldTransform.Inverse()}; + const float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale(); + + const int32_t JointIndex = static_cast(Joint); + + FTransform Transform; + if (bMotionSourceHandTracking) + { + XrPosef Pose; + Pose.orientation = ToXrQuat(WristRotation * JointRotation); + Pose.position = ToXrVector(WristLocation + (WristRotation * JointLocation), WorldToMetersScale); + Transform = ToFTransform(MoveTemp(Pose), WorldToMetersScale); + Transform *= WorldToTrackingTransform; + } + else + { + Transform.SetLocation(WristLocation + (WristRotation * JointLocation)); + Transform.SetRotation(WristRotation * JointRotation); + } + + OutHandState.KeypointTransforms[JointIndex] = MoveTemp(Transform); + OutHandState.Radii[JointIndex] = 0.01f * WorldToMetersScale; + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGWristJointData( + FSGXRHandState& OutHandState, const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + if (!ensureAlwaysMsgf(XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + const FTransform& TrackingToWorldTransform{XRTrackingSystem->GetTrackingToWorldTransform()}; + const FTransform& WorldToTrackingTransform{TrackingToWorldTransform.Inverse()}; + const float WorldToMetersScale = XRTrackingSystem->GetWorldToMetersScale(); + + static constexpr EHandKeypoint Joint = EHandKeypoint::Wrist; + static constexpr int32_t JointIndex = static_cast(Joint); + + FTransform Transform; + if (bMotionSourceHandTracking) + { + XrPosef Pose; + Pose.orientation = ToXrQuat(WristRotation); + Pose.position = ToXrVector(WristLocation, WorldToMetersScale); + Transform = ToFTransform(MoveTemp(Pose), WorldToMetersScale); + Transform *= WorldToTrackingTransform; + } + else + { + Transform.SetLocation(WristLocation); + Transform.SetRotation(WristRotation); + } + + OutHandState.KeypointTransforms[JointIndex] = MoveTemp(Transform); + OutHandState.Radii[JointIndex] = 0.01f * WorldToMetersScale; + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGPalmJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + if (!ensureAlwaysMsgf(XRTrackingSystem, TEXT("Invalid XR tracking system!"))) + { + return false; + } + + static constexpr int32 MiddleFingerMiddle = 2; + static constexpr int32 MiddleProximalJointMiddle = 0; + const FVector& MiddleProximalLocation{JointLocations[MiddleFingerMiddle][MiddleProximalJointMiddle]}; + + static const FVector JointLocation{MiddleProximalLocation / 2.0f}; + static const FQuat JointRotation{FQuat::Identity}; + + if (!UpdateSGJointData(OutHandState, EHandKeypoint::Palm, + JointLocation, JointRotation, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGThumbJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + static constexpr int32 ThumbFingerIndex = 0; + static constexpr int32 ThumbMetacarpalJointIndex = 0; + static constexpr int32 ThumbProximalJointIndex = 1; + static constexpr int32 ThumbDistalJointIndex = 2; + static constexpr int32 ThumbTipJointIndex = 3; + + const FVector& ThumbMetacarpalLocation{JointLocations[ThumbFingerIndex][ThumbMetacarpalJointIndex]}; + const FVector& ThumbProximalLocation{JointLocations[ThumbFingerIndex][ThumbProximalJointIndex]}; + const FVector& ThumbDistalLocation{JointLocations[ThumbFingerIndex][ThumbDistalJointIndex]}; + const FVector& ThumbTipLocation{JointLocations[ThumbFingerIndex][ThumbTipJointIndex]}; + + const FQuat& ThumbMetacarpalRotation{JointRotations[ThumbFingerIndex][ThumbMetacarpalJointIndex]}; + const FQuat& ThumbProximalRotation{JointRotations[ThumbFingerIndex][ThumbProximalJointIndex]}; + const FQuat& ThumbDistalRotation{JointRotations[ThumbFingerIndex][ThumbDistalJointIndex]}; + const FQuat& ThumbTipRotation{JointRotations[ThumbFingerIndex][ThumbTipJointIndex]}; + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::ThumbMetacarpal, ThumbMetacarpalLocation, ThumbMetacarpalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::ThumbProximal, ThumbProximalLocation, ThumbProximalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::ThumbDistal, ThumbDistalLocation, ThumbDistalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::ThumbTip, ThumbTipLocation, ThumbTipRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGIndexJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + static constexpr int32 IndexFingerIndex = 1; + + static constexpr int32 IndexProximalJointIndex = 0; + static constexpr int32 IndexIntermediateJointIndex = 1; + static constexpr int32 IndexDistalJointIndex = 2; + static constexpr int32 IndexTipJointIndex = 3; + + const FVector& IndexProximalLocation{JointLocations[IndexFingerIndex][IndexProximalJointIndex]}; + const FVector IndexMetacarpalLocation{IndexProximalLocation.X / 3.75f, + IndexProximalLocation.Y, IndexProximalLocation.Z}; + const FVector& IndexIntermediateLocation{JointLocations[IndexFingerIndex][IndexIntermediateJointIndex]}; + const FVector& IndexDistalLocation{JointLocations[IndexFingerIndex][IndexDistalJointIndex]}; + const FVector& IndexTipLocation{JointLocations[IndexFingerIndex][IndexTipJointIndex]}; + + const FQuat IndexMetacarpalRotation{FQuat::Identity}; + const FQuat& IndexProximalRotation{JointRotations[IndexFingerIndex][IndexProximalJointIndex]}; + const FQuat& IndexIntermediateRotation{JointRotations[IndexFingerIndex][IndexIntermediateJointIndex]}; + const FQuat& IndexDistalRotation{JointRotations[IndexFingerIndex][IndexDistalJointIndex]}; + const FQuat& IndexTipRotation{JointRotations[IndexFingerIndex][IndexTipJointIndex]}; + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::IndexMetacarpal, IndexMetacarpalLocation, IndexMetacarpalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::IndexProximal, IndexProximalLocation, IndexProximalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::IndexIntermediate, IndexIntermediateLocation, IndexIntermediateRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::IndexDistal, IndexDistalLocation, IndexDistalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::IndexTip, IndexTipLocation, IndexTipRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGMiddleJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + static constexpr int32 MiddleFingerMiddle = 2; + + static constexpr int32 MiddleProximalJointMiddle = 0; + static constexpr int32 MiddleIntermediateJointMiddle = 1; + static constexpr int32 MiddleDistalJointMiddle = 2; + static constexpr int32 MiddleTipJointMiddle = 3; + + const FVector& MiddleProximalLocation{JointLocations[MiddleFingerMiddle][MiddleProximalJointMiddle]}; + const FVector MiddleMetacarpalLocation{MiddleProximalLocation.X / 3.75f, + MiddleProximalLocation.Y, MiddleProximalLocation.Z}; + const FVector& MiddleIntermediateLocation{JointLocations[MiddleFingerMiddle][MiddleIntermediateJointMiddle]}; + const FVector& MiddleDistalLocation{JointLocations[MiddleFingerMiddle][MiddleDistalJointMiddle]}; + const FVector& MiddleTipLocation{JointLocations[MiddleFingerMiddle][MiddleTipJointMiddle]}; + + const FQuat MiddleMetacarpalRotation{FQuat::Identity}; + const FQuat& MiddleProximalRotation{JointRotations[MiddleFingerMiddle][MiddleProximalJointMiddle]}; + const FQuat& MiddleIntermediateRotation{JointRotations[MiddleFingerMiddle][MiddleIntermediateJointMiddle]}; + const FQuat& MiddleDistalRotation{JointRotations[MiddleFingerMiddle][MiddleDistalJointMiddle]}; + const FQuat& MiddleTipRotation{JointRotations[MiddleFingerMiddle][MiddleTipJointMiddle]}; + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::MiddleMetacarpal, MiddleMetacarpalLocation, MiddleMetacarpalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::MiddleProximal, MiddleProximalLocation, MiddleProximalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::MiddleIntermediate, MiddleIntermediateLocation, MiddleIntermediateRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::MiddleDistal, MiddleDistalLocation, MiddleDistalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::MiddleTip, MiddleTipLocation, MiddleTipRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGRingJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + static constexpr int32 RingFingerRing = 3; + + static constexpr int32 RingProximalJointRing = 0; + static constexpr int32 RingIntermediateJointRing = 1; + static constexpr int32 RingDistalJointRing = 2; + static constexpr int32 RingTipJointRing = 3; + + const FVector& RingProximalLocation{JointLocations[RingFingerRing][RingProximalJointRing]}; + const FVector RingMetacarpalLocation{RingProximalLocation.X / 3.75f, + RingProximalLocation.Y, RingProximalLocation.Z}; + const FVector& RingIntermediateLocation{JointLocations[RingFingerRing][RingIntermediateJointRing]}; + const FVector& RingDistalLocation{JointLocations[RingFingerRing][RingDistalJointRing]}; + const FVector& RingTipLocation{JointLocations[RingFingerRing][RingTipJointRing]}; + + const FQuat RingMetacarpalRotation{FQuat::Identity}; + const FQuat& RingProximalRotation{JointRotations[RingFingerRing][RingProximalJointRing]}; + const FQuat& RingIntermediateRotation{JointRotations[RingFingerRing][RingIntermediateJointRing]}; + const FQuat& RingDistalRotation{JointRotations[RingFingerRing][RingDistalJointRing]}; + const FQuat& RingTipRotation{JointRotations[RingFingerRing][RingTipJointRing]}; + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::RingMetacarpal, RingMetacarpalLocation, RingMetacarpalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::RingProximal, RingProximalLocation, RingProximalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::RingIntermediate, RingIntermediateLocation, RingIntermediateRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::RingDistal, RingDistalLocation, RingDistalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::RingTip, RingTipLocation, RingTipRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGLittleJointData( + FSGXRHandState& OutHandState, + const TArray>& JointLocations, const TArray>& JointRotations, + const FVector& WristLocation, const FQuat& WristRotation, + const bool bMotionSourceHandTracking) const +{ + static constexpr int32 LittleFingerLittle = 4; + + static constexpr int32 LittleProximalJointLittle = 0; + static constexpr int32 LittleIntermediateJointLittle = 1; + static constexpr int32 LittleDistalJointLittle = 2; + static constexpr int32 LittleTipJointLittle = 3; + + const FVector& LittleProximalLocation{JointLocations[LittleFingerLittle][LittleProximalJointLittle]}; + const FVector LittleMetacarpalLocation{LittleProximalLocation.X / 3.75f, + LittleProximalLocation.Y, LittleProximalLocation.Z}; + const FVector& LittleIntermediateLocation{JointLocations[LittleFingerLittle][LittleIntermediateJointLittle]}; + const FVector& LittleDistalLocation{JointLocations[LittleFingerLittle][LittleDistalJointLittle]}; + const FVector& LittleTipLocation{JointLocations[LittleFingerLittle][LittleTipJointLittle]}; + + const FQuat LittleMetacarpalRotation{FQuat::Identity}; + const FQuat& LittleProximalRotation{JointRotations[LittleFingerLittle][LittleProximalJointLittle]}; + const FQuat& LittleIntermediateRotation{JointRotations[LittleFingerLittle][LittleIntermediateJointLittle]}; + const FQuat& LittleDistalRotation{JointRotations[LittleFingerLittle][LittleDistalJointLittle]}; + const FQuat& LittleTipRotation{JointRotations[LittleFingerLittle][LittleTipJointLittle]}; + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::LittleMetacarpal, LittleMetacarpalLocation, LittleMetacarpalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::LittleProximal, LittleProximalLocation, LittleProximalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::LittleIntermediate, LittleIntermediateLocation, LittleIntermediateRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::LittleDistal, LittleDistalLocation, LittleDistalRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGJointData( + OutHandState, + EHandKeypoint::LittleTip, LittleTipLocation, LittleTipRotation, + WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +bool FSGXRTracker::FImpl::UpdateSGHandState(const EControllerHand DeviceHand, FSGXRHandState& OutHandState) const +{ + USGHapticGlove* Glove{GetGlove(DeviceHand)}; + + if (!IsValid(Glove)) + { + return false; + } + + USGHandPose* HandPose = nullptr; + const bool bGotHandPose = Glove->GetHandPose(FImpl::GetWorld(), HandPose); + + if (!bGotHandPose || !IsValid(HandPose)) + { + return false; + } + + OutHandState.bReceivedJointPoses = true; + + const TArray> JointLocations{HandPose->GetJointPositions()}; + const TArray> JointRotations{HandPose->GetJointRotationsQ()}; + + FVector WristLocation; + FQuat WristRotation; + bool bMotionSourceHandTracking = false; + if (!GetWristLocationAndRotation(DeviceHand, WristLocation, WristRotation, bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGWristJointData(OutHandState, WristLocation, WristRotation, bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGPalmJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGThumbJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGIndexJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGMiddleJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGRingJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + if (!UpdateSGLittleJointData(OutHandState, JointLocations, JointRotations, WristLocation, WristRotation, + bMotionSourceHandTracking)) + { + return false; + } + + return true; +} + +void FSGXRTracker::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SenseGloveTracking.cpp b/Source/SenseGloveTracking/Private/SenseGloveTracking.cpp new file mode 100644 index 00000000..dad3ede2 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SenseGloveTracking.cpp @@ -0,0 +1,42 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SenseGloveTracking.h" + +#include "Modules/ModuleManager.h" + +#include "SenseGloveTrackingModule.h" + +IMPLEMENT_MODULE(FSenseGloveTrackingModule, SenseGloveTracking) \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SenseGloveTracking.h b/Source/SenseGloveTracking/Private/SenseGloveTracking.h new file mode 100644 index 00000000..5e097228 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SenseGloveTracking.h @@ -0,0 +1,38 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "CoreMinimal.h" \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.cpp b/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.cpp new file mode 100644 index 00000000..cfa96f03 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.cpp @@ -0,0 +1,181 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SenseGloveTrackingModule.h" + +#include "SGLog/SGLog.h" +#include "SGTracking/SGXRTracker.h" + +#define LOCTEXT_NAMESPACE "FSenseGloveTrackingModule" + +struct FSenseGloveTrackingModule::FImpl +{ + /************************ + * Member variables + ************************/ + + TSharedPtr InputDevice; + + /************************ + * Owner object + ************************/ + + FSenseGloveTrackingModule* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(FSenseGloveTrackingModule* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = default; + FImpl& operator=(const FImpl& Rhs) = default; +}; + + +FSenseGloveTrackingModule::FSenseGloveTrackingModule() + : IInputDeviceModule(), + Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) +{ +} + +FSenseGloveTrackingModule::FSenseGloveTrackingModule(const FSenseGloveTrackingModule& Rhs) + : IInputDeviceModule(Rhs), + Pimpl(TUniquePtr(new FImpl{*Rhs.Pimpl}, PimplDeleter)) +{ + Pimpl->Owner = this; +} + +FSenseGloveTrackingModule::FSenseGloveTrackingModule(FSenseGloveTrackingModule&& Rhs) SG_NOEXCEPT = default; + +FSenseGloveTrackingModule::~FSenseGloveTrackingModule() = default; + +FSenseGloveTrackingModule& FSenseGloveTrackingModule::operator=(const FSenseGloveTrackingModule& Rhs) +{ + IInputDeviceModule::operator=(Rhs); + *Pimpl = *Rhs.Pimpl; + Pimpl->Owner = this; + return *this; +} + +FSenseGloveTrackingModule& FSenseGloveTrackingModule::operator=(FSenseGloveTrackingModule&& Rhs) SG_NOEXCEPT = default; + +void FSenseGloveTrackingModule::StartupModule() +{ + SGLOG("Starting up SenseGloveTracking module..."); + + IInputDeviceModule::StartupModule(); + + /// HACK! + // Generic Application might not be instantiated at this point so we create the input device with a dummy message + // handler. When the Generic Application creates the input device it passes a valid message handler to it which is + // further on used for all the controller events. This hack fixes issues caused by using a custom input device + // before the Generic Application has instantiated it. Eg. within BeginPlay(). + // This also fixes the warnings that pop up on the custom input keys when the blueprint loads. Those warnings are + // caused because Unreal loads the Blueprints before the input device has been instantiated and has added its keys, + // thus leading Unreal to believe that those keys don't exist. This hack causes an earlier instantiation of the + // input device, and consequently, the custom keys. + TSharedPtr DummyMessageHandler(new FGenericApplicationMessageHandler()); + CreateInputDevice(DummyMessageHandler.ToSharedRef()); +} + +void FSenseGloveTrackingModule::PreUnloadCallback() +{ + SGLOG("Unloading SenseGloveTracking module..."); + + IInputDeviceModule::PreUnloadCallback(); +} + +void FSenseGloveTrackingModule::PostLoadCallback() +{ + SGLOG("Loading of SenseGloveTracking module has succeeded!"); + + IInputDeviceModule::PostLoadCallback(); +} + +void FSenseGloveTrackingModule::ShutdownModule() +{ + SGLOG("Shutting down SenseGloveTracking module..."); + + IInputDeviceModule::ShutdownModule(); +} + +TSharedPtr FSenseGloveTrackingModule::CreateInputDevice( + const TSharedRef& InMessageHandler) +{ + SGLOG("Creating the SenseGloveTracking input device..."); + + if (!Pimpl->InputDevice.IsValid()) + { + TSharedPtr HandTrackingInputDevice(new FSGXRTracker(InMessageHandler)); + Pimpl->InputDevice = HandTrackingInputDevice; + } + else + { + Pimpl->InputDevice.Get()->SetMessageHandler(InMessageHandler); + } + + return Pimpl->InputDevice; +} + +TSharedPtr FSenseGloveTrackingModule::GetInputDevice() +{ + if (!Pimpl->InputDevice.IsValid()) + { + CreateInputDevice(FSlateApplication::Get().GetPlatformApplication()->GetMessageHandler()); + } + + return Pimpl->InputDevice; +} + +FSenseGloveTrackingModule::FImpl::FImpl(FSenseGloveTrackingModule* InOwner) + : InputDevice(nullptr), + Owner(InOwner) +{ +} + +FSenseGloveTrackingModule::FImpl::~FImpl() = default; + +void FSenseGloveTrackingModule::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.h b/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.h new file mode 100644 index 00000000..04cd9e09 --- /dev/null +++ b/Source/SenseGloveTracking/Private/SenseGloveTrackingModule.h @@ -0,0 +1,116 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "IInputDeviceModule.h" +#include "Templates/UniquePtr.h" + +#include "SGBuildHacks/SGPlatform.h" + +#define LOCTEXT_NAMESPACE "SenseGloveTracking" + +class FSenseGloveTrackingModule : public IInputDeviceModule +{ +public: + static FORCEINLINE FSenseGloveTrackingModule& Get() + { + return FModuleManager::LoadModuleChecked("SenseGloveTracking"); + } + + static FORCEINLINE bool IsAvailable() + { + return FModuleManager::Get().IsModuleLoaded("SenseGloveTracking"); + } + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +public: + /** + * The default constructor. + */ + FSenseGloveTrackingModule(); + +public: + /** + * The copy constructor. + */ + FSenseGloveTrackingModule(const FSenseGloveTrackingModule& Rhs); + + /** + * The move constructor. + */ + FSenseGloveTrackingModule(FSenseGloveTrackingModule&& Rhs) SG_NOEXCEPT; + +public: + /** + * The destructor. + */ + virtual ~FSenseGloveTrackingModule() override; + +public: + /** + * The copy assignment operator. + */ + FSenseGloveTrackingModule& operator=(const FSenseGloveTrackingModule& Rhs); + + /** + * The move assignment operator. + */ + FSenseGloveTrackingModule& operator=(FSenseGloveTrackingModule&& Rhs) SG_NOEXCEPT; + +public: + virtual void StartupModule() override; + virtual void PreUnloadCallback() override; + virtual void PostLoadCallback() override; + virtual void ShutdownModule() override; + +public: + virtual TSharedPtr CreateInputDevice( + const TSharedRef& InMessageHandler) override; + + virtual TSharedPtr GetInputDevice(); +}; + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveTracking/Public/SGTracking/SGGloveTracker.h b/Source/SenseGloveTracking/Public/SGTracking/SGGloveTracker.h new file mode 100644 index 00000000..c51ba81d --- /dev/null +++ b/Source/SenseGloveTracking/Public/SGTracking/SGGloveTracker.h @@ -0,0 +1,117 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "Subsystems/WorldSubsystem.h" +#include "Templates/UniquePtr.h" +#include "UObject/Object.h" +#include "UObject/ObjectMacros.h" + +#include "SGCore/SGHapticGlove.h" + +#include "SGGloveTracker.generated.h" + +UCLASS() +class SENSEGLOVETRACKING_API USGGloveTracker : public UWorldSubsystem +{ + GENERATED_BODY() + +public: + static USGGloveTracker* GetInstance(); + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +private: + UPROPERTY(Transient) + USGHapticGlove* LeftGlove; + + UPROPERTY(Transient) + USGHapticGlove* RightGlove; + +private: + USGGloveTracker(); + +public: + FORCEINLINE USGHapticGlove* GetGlove(const bool bRight) const + { + return bRight ? RightGlove : LeftGlove; + } + + FORCEINLINE USGHapticGlove* GetLeftGlove() const + { + return LeftGlove; + } + + FORCEINLINE USGHapticGlove* GetRightGlove() const + { + return RightGlove; + } + + FORCEINLINE bool IsGloveConnected(const bool bRight) const + { + return IsValid(GetGlove(bRight)); + } + + FORCEINLINE bool IsLeftGloveConnected() const + { + return IsValid(GetLeftGlove()); + } + + FORCEINLINE bool IsRightGloveConnected() const + { + return IsValid(GetRightGlove()); + } + + FORCEINLINE bool IsAnyGloveConnected() const + { + return IsLeftGloveConnected() || IsRightGloveConnected(); + } + +public: + virtual bool ShouldCreateSubsystem(UObject* Outer) const override; + + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; +}; \ No newline at end of file diff --git a/Source/SenseGlove/Public/SenseGlove/SGHeadMountedDisplay.h b/Source/SenseGloveTracking/Public/SGTracking/SGHMDTracker.h similarity index 95% rename from Source/SenseGlove/Public/SenseGlove/SGHeadMountedDisplay.h rename to Source/SenseGloveTracking/Public/SGTracking/SGHMDTracker.h index 5cf71c9d..6a5a9ce0 100644 --- a/Source/SenseGlove/Public/SenseGlove/SGHeadMountedDisplay.h +++ b/Source/SenseGloveTracking/Public/SGTracking/SGHMDTracker.h @@ -39,15 +39,15 @@ #include "UObject/NameTypes.h" #include "UObject/ObjectMacros.h" -#include "SGTypes/SGTypes.h" +#include "SGTypes/SGTrackingTypes.h" -#include "SGHeadMountedDisplay.generated.h" +#include "SGHMDTracker.generated.h" /** * */ USTRUCT() -struct SENSEGLOVE_API FSGHeadMountedDisplay +struct SENSEGLOVETRACKING_API FSGHMDTracker { GENERATED_BODY() diff --git a/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h b/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h new file mode 100644 index 00000000..527baba8 --- /dev/null +++ b/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h @@ -0,0 +1,68 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "HeadMountedDisplayTypes.h" +#include "IOpenXRExtensionPlugin.h" +#include "Math/MathFwd.h" +#include "Templates/UnrealTemplate.h" + +struct SENSEGLOVETRACKING_API FSGXRHandState final : public FNoncopyable +{ +public: + XrHandTrackerEXT HandTracker; + + XrHandJointLocationEXT JointLocations[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointVelocityEXT JointVelocities[XR_HAND_JOINT_COUNT_EXT]; + + XrHandJointVelocitiesEXT Velocities; + XrHandJointLocationsEXT Locations; + + // Transforms are cached in Unreal Tracking Space + FTransform KeypointTransforms[EHandKeypointCount]; + + float Radii[EHandKeypointCount]; + + uint8_t bReceivedJointPoses : 1; + +public: + FSGXRHandState(); + virtual ~FSGXRHandState(); + +public: + bool GetTransform(EHandKeypoint Keypoint, FTransform& OutTransform) const; + const FTransform& GetTransform(EHandKeypoint Keypoint) const; +}; \ No newline at end of file diff --git a/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h b/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h new file mode 100644 index 00000000..f60cce4c --- /dev/null +++ b/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h @@ -0,0 +1,165 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "HeadMountedDisplayTypes.h" +#include "IHandTracker.h" +#include "IInputDevice.h" +#include "IOpenXRExtensionPlugin.h" +#include "Math/MathFwd.h" +#include "Runtime/Launch/Resources/Version.h" +#include "Templates/UniquePtr.h" +#include "UObject/NameTypes.h" +#include "XRMotionControllerBase.h" + +#include "SGBuildHacks/SGPlatform.h" + +struct SENSEGLOVETRACKING_API FSGXRHandState; + +class SENSEGLOVETRACKING_API FSGXRTracker final : public IOpenXRExtensionPlugin, + public IInputDevice, + public FXRMotionControllerBase, + public IHandTracker +{ + /** + * Parses the enum name removing the prefix. + */ + static FName ParseEOpenXRHandKeypointEnumName(const FName& EnumName); + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +private: + FSGXRTracker() = delete; + +public: + FSGXRTracker(const TSharedRef& InMessageHandler); + +public: + /** + * The copy constructor. + */ + FSGXRTracker(const FSGXRTracker& Rhs) = delete; + + /** + * The move constructor. + */ + FSGXRTracker(FSGXRTracker&& Rhs) SG_NOEXCEPT; + +public: + /** + * The destructor. + */ + virtual ~FSGXRTracker() override; + +public: + /** + * The copy assignment operator. + */ + FSGXRTracker& operator=(const FSGXRTracker& Rhs) = delete; + + /** + * The move assignment operator. + */ + FSGXRTracker& operator=(FSGXRTracker&& Rhs) SG_NOEXCEPT; + +public: + bool IsHandTrackingSupportedByDevice() const; + + const FSGXRHandState& GetLeftHandState() const; + const FSGXRHandState& GetRightHandState() const; + +public: + // The IOpenXRExtensionPlugin interface. + + virtual FString GetDisplayName() override; + virtual bool GetRequiredExtensions(TArray& OutExtensions) override; + virtual const void* OnGetSystem(XrInstance InInstance, const void* InNext) override; + virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override; + virtual const void* OnBeginSession(XrSession InSession, const void* InNext) override; + virtual void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override; + +public: + // The IMotionController interface. + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 + 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; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + 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 /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ + virtual FName GetMotionControllerDeviceTypeName() const override; + virtual void EnumerateSources(TArray& SourcesOut) const override; + +public: + // The IInputDevice interface. + + virtual void Tick(float DeltaTime) override; + virtual void SendControllerEvents() override; + virtual void SetMessageHandler(const TSharedRef& 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; + virtual bool IsGamepadAttached() const override; + +public: + // The IHandTracker interface. + + virtual FName GetHandTrackerDeviceTypeName() const override; + virtual bool IsHandTrackingStateValid() const override; + virtual bool GetKeypointState( + EControllerHand Hand, + EHandKeypoint Keypoint, FTransform& OutTransform, float& OutRadius) const override; + virtual bool GetAllKeypointStates( + EControllerHand Hand, + TArray& OutPositions, TArray& OutRotations, TArray& OutRadii) const override; +}; \ No newline at end of file diff --git a/Source/SenseGloveTracking/SenseGloveTracking.Build.cs b/Source/SenseGloveTracking/SenseGloveTracking.Build.cs new file mode 100644 index 00000000..ad53d33d --- /dev/null +++ b/Source/SenseGloveTracking/SenseGloveTracking.Build.cs @@ -0,0 +1,93 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +using System; +using UnrealBuildTool; + +public class SenseGloveTracking : ModuleRules +{ + public SenseGloveTracking(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "ApplicationCore", + "Core", + "CoreUObject", + "Engine", + "HeadMountedDisplay", + "InputCore", + "InputDevice", + "OpenXRHMD", + "OpenXRInput", + // NOTE + // Only UE 5.3+ provides the XRBase plugin. +#if UE_5_3_OR_LATER + "XRBase", +#else + "OpenXR", +#endif + "Slate", + "SlateCore", + "SenseGloveBackend", + "SenseGloveCore", + "SenseGloveLog", + "SenseGloveSettings", + "SenseGloveTypes", + } + ); + + if (Target.bBuildEditor) + { + PrivateDependencyModuleNames.AddRange( + new string[] + { + "UnrealEd", + } + ); + } + + PrivateIncludePathModuleNames.AddRange( + new string[] + { + "SenseGloveBuildHacks", + } + ); + + AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR"); + } +} \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGGloveTrackerKismetLibrary.cpp b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGGloveTrackerKismetLibrary.cpp new file mode 100644 index 00000000..304b3e8d --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGGloveTrackerKismetLibrary.cpp @@ -0,0 +1,79 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGTrackingKismet/SGGloveTrackerKismetLibrary.h" + +#include "SGCore/SGHapticGlove.h" +#include "SGTracking/SGGloveTracker.h" + +USGGloveTracker* USGGloveTrackerKismetLibrary::GetInstance() +{ + return USGGloveTracker::GetInstance(); +} + +USGHapticGlove* USGGloveTrackerKismetLibrary::GetGlove(const USGGloveTracker* GloveTracker, const bool bRight) +{ + return GloveTracker->GetGlove(bRight); +} + +USGHapticGlove* USGGloveTrackerKismetLibrary::GetLeftGlove(const USGGloveTracker* GloveTracker) +{ + return GloveTracker->GetLeftGlove(); +} + +USGHapticGlove* USGGloveTrackerKismetLibrary::GetRightGlove(const USGGloveTracker* GloveTracker) +{ + return GloveTracker->GetRightGlove(); +} + +bool USGGloveTrackerKismetLibrary::IsGloveConnected(const USGGloveTracker* GloveTracker, const bool bRight) +{ + return GloveTracker->IsGloveConnected(bRight); +} + +bool USGGloveTrackerKismetLibrary::IsLeftGloveConnected(const USGGloveTracker* GloveTracker) +{ + return GloveTracker->IsLeftGloveConnected(); +} + +bool USGGloveTrackerKismetLibrary::IsRightGloveConnected(const USGGloveTracker* GloveTracker) +{ + return GloveTracker->IsRightGloveConnected(); +} + +bool USGGloveTrackerKismetLibrary::IsAnyGloveConnected(const USGGloveTracker* GloveTracker) +{ + return GloveTracker->IsAnyGloveConnected(); +} \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGHMDTrackerKismetLibrary.cpp b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGHMDTrackerKismetLibrary.cpp new file mode 100644 index 00000000..95c435c6 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGHMDTrackerKismetLibrary.cpp @@ -0,0 +1,78 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SGTrackingKismet/SGHMDTrackerKismetLibrary.h" + +#include "SGTracking/SGHMDTracker.h" + +FName USGHMDTrackerKismetLibrary::GetDeviceName() +{ + return FSGHMDTracker::GetDeviceName(); +} + +bool USGHMDTrackerKismetLibrary::IsMetaQuest2() +{ + return FSGHMDTracker::IsMetaQuest2(); +} + +bool USGHMDTrackerKismetLibrary::IsMetaQuestPro() +{ + return FSGHMDTracker::IsMetaQuestPro(); +} + +bool USGHMDTrackerKismetLibrary::IsMetaQuest3() +{ + return FSGHMDTracker::IsMetaQuest3(); +} + +bool USGHMDTrackerKismetLibrary::IsHtcVivePro() +{ + return FSGHMDTracker::IsHtcVivePro(); +} + +bool USGHMDTrackerKismetLibrary::IsHtcViveFocus3() +{ + return FSGHMDTracker::IsHtcViveFocus3(); +} + +bool USGHMDTrackerKismetLibrary::IsHtcViveXRElite() +{ + return FSGHMDTracker::IsHtcViveXRElite(); +} + +ESGHeadMountedDisplayDevice USGHMDTrackerKismetLibrary::GetDeviceType() +{ + return FSGHMDTracker::GetDeviceType(); +} \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.cpp b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.cpp new file mode 100644 index 00000000..23bfd236 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.cpp @@ -0,0 +1,40 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SenseGloveTrackingKismet.h" +#include "Modules/ModuleManager.h" +#include "SenseGloveTrackingKismetModule.h" + +IMPLEMENT_MODULE(FSenseGloveTrackingKismetModule, SenseGloveTrackingKismet) diff --git a/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.h b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.h new file mode 100644 index 00000000..5e097228 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismet.h @@ -0,0 +1,38 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "CoreMinimal.h" \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.cpp b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.cpp new file mode 100644 index 00000000..e9721a3e --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.cpp @@ -0,0 +1,62 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#include "SenseGloveTrackingKismetModule.h" + +#include "SGLog/SGLog.h" + +#define LOCTEXT_NAMESPACE "FSenseGloveTrackingKismetModule" + +void FSenseGloveTrackingKismetModule::StartupModule() +{ + SGLOG("Starting up the SenseGloveTrackingKismet module..."); +} + +void FSenseGloveTrackingKismetModule::PreUnloadCallback() +{ + SGLOG("Unloading the SenseGloveTrackingKismet module..."); +} + +void FSenseGloveTrackingKismetModule::PostLoadCallback() +{ + SGLOG("Loading of SenseGloveTrackingKismet module has succeeded!"); +} + +void FSenseGloveTrackingKismetModule::ShutdownModule() +{ + SGLOG("Shutting down the SenseGloveTrackingKismet module..."); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.h b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.h new file mode 100644 index 00000000..9d56a910 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Private/SenseGloveTrackingKismetModule.h @@ -0,0 +1,51 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "Modules/ModuleInterface.h" + +#define LOCTEXT_NAMESPACE "SenseGloveTrackingKismet" + +class FSenseGloveTrackingKismetModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void PreUnloadCallback() override; + virtual void PostLoadCallback() override; + virtual void ShutdownModule() override; +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGGloveTrackerKismetLibrary.h b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGGloveTrackerKismetLibrary.h new file mode 100644 index 00000000..a15dbd96 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGGloveTrackerKismetLibrary.h @@ -0,0 +1,75 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "SGKismet/SGBlueprintFunctionLibrary.h" + +#include "SGGloveTrackerKismetLibrary.generated.h" + +class USGGloveTracker; +class USGHapticGlove; + +UCLASS(meta=(ScriptName = "SesneGloveTrackerKismetLibrary")) +class SENSEGLOVETRACKINGKISMET_API USGGloveTrackerKismetLibrary final : public USGBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | Glove Tracker", + meta=(WorldContext="WorldContextObject")) + static USGGloveTracker* GetInstance(); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static USGHapticGlove* GetGlove(const USGGloveTracker* GloveTracker, const bool bRight); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static USGHapticGlove* GetLeftGlove(const USGGloveTracker* GloveTracker); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static USGHapticGlove* GetRightGlove(const USGGloveTracker* GloveTracker); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static bool IsGloveConnected(const USGGloveTracker* GloveTracker, const bool bRight); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static bool IsLeftGloveConnected(const USGGloveTracker* GloveTracker); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static bool IsRightGloveConnected(const USGGloveTracker* GloveTracker); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Tracking | Glove Tracker") + static bool IsAnyGloveConnected(const USGGloveTracker* GloveTracker); +}; \ No newline at end of file diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGHeadMountedDisplayKismetLibrary.h b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGHMDTrackerKismetLibrary.h similarity index 74% rename from Source/SenseGloveKismet/Public/SGKismet/SGHeadMountedDisplayKismetLibrary.h rename to Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGHMDTrackerKismetLibrary.h index 37cc6374..9b2953b1 100644 --- a/Source/SenseGloveKismet/Public/SGKismet/SGHeadMountedDisplayKismetLibrary.h +++ b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGHMDTrackerKismetLibrary.h @@ -1,4 +1,4 @@ -/** +/** * @file * * @author Mamadou Babaei @@ -38,41 +38,41 @@ #include "UObject/NameTypes.h" #include "SGKismet/SGBlueprintFunctionLibrary.h" -#include "SGTypes/SGTypes.h" +#include "SGTypes/SGTrackingTypes.h" -#include "SGHeadMountedDisplayKismetLibrary.generated.h" +#include "SGHMDTrackerKismetLibrary.generated.h" -UCLASS(meta=(ScriptName = "SesneGloveHeadMountedDisplayKismetLibrary")) -class SENSEGLOVEKISMET_API UHeadMountedDisplayKismetLibrary final : public USGBlueprintFunctionLibrary +UCLASS(meta=(ScriptName = "SesneGloveHeadMountDisplayKismetLibrary")) +class SENSEGLOVETRACKINGKISMET_API USGHMDTrackerKismetLibrary final : public USGBlueprintFunctionLibrary { GENERATED_BODY() public: - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static FName GetDeviceName(); /** * Checks whether the HMD device is Meta Quest 2, or not. */ - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsMetaQuest2(); /** * Checks whether the HMD device is Meta Quest Pro, or not. */ - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsMetaQuestPro(); /** * Checks whether the HMD device is Meta Quest 3, or not. */ - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsMetaQuest3(); /** * Checks whether the HMD device is HTC VIVE Pro, or not. */ - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsHtcVivePro(); /** @@ -81,7 +81,7 @@ public: * @remark At the moment, VIVE Focus 3 cannot be distinguished from VIVE XR Elite, so when IsHtcViveFocus3 returns * true, IsHtcViveXRElite will return true as well; thus we cannot know for sure which headset we are running on. */ - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsHtcViveFocus3(); /** @@ -90,9 +90,9 @@ public: * @remark At the moment, VIVE Focus 3 cannot be distinguished from VIVE XR Elite, so when IsHtcViveXRElite returns * true, IsHtcViveFocus3 will return true as well; thus we cannot know for sure which headset we are running on. */ - UFUNCTION(BlueprintCallable, Category = "SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static bool IsHtcViveXRElite(); - UFUNCTION(BlueprintCallable, Category="SenseGlove | Head-mounted Display") + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | HMD Tracker") static ESGHeadMountedDisplayDevice GetDeviceType(); }; diff --git a/Source/SenseGloveTrackingKismet/SenseGloveTrackingKismet.Build.cs b/Source/SenseGloveTrackingKismet/SenseGloveTrackingKismet.Build.cs new file mode 100644 index 00000000..c8b1e8f1 --- /dev/null +++ b/Source/SenseGloveTrackingKismet/SenseGloveTrackingKismet.Build.cs @@ -0,0 +1,59 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2024 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +using System; +using UnrealBuildTool; + +public class SenseGloveTrackingKismet : ModuleRules +{ + public SenseGloveTrackingKismet(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "SenseGloveCore", + "SenseGloveLog", + "SenseGloveKismet", + "SenseGloveTracking", + "SenseGloveTypes", + } + ); + } +} \ No newline at end of file diff --git a/Source/SenseGloveTypes/Private/SGTypes/SGTypes.cpp b/Source/SenseGloveTypes/Private/SGTypes/SGTrackingTypes.cpp similarity index 97% rename from Source/SenseGloveTypes/Private/SGTypes/SGTypes.cpp rename to Source/SenseGloveTypes/Private/SGTypes/SGTrackingTypes.cpp index b3249ab1..028b8146 100644 --- a/Source/SenseGloveTypes/Private/SGTypes/SGTypes.cpp +++ b/Source/SenseGloveTypes/Private/SGTypes/SGTrackingTypes.cpp @@ -33,4 +33,4 @@ */ -#include "SGTypes/SGTypes.h" \ No newline at end of file +#include "SGTypes/SGTrackingTypes.h" \ No newline at end of file diff --git a/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h b/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h similarity index 83% rename from Source/SenseGloveTypes/Public/SGTypes/SGTypes.h rename to Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h index 6663ae5f..3b6f889f 100644 --- a/Source/SenseGloveTypes/Public/SGTypes/SGTypes.h +++ b/Source/SenseGloveTypes/Public/SGTypes/SGTrackingTypes.h @@ -38,7 +38,9 @@ #include "CoreTypes.h" #include "UObject/ObjectMacros.h" -/* The Head-mounted Display device enum to determine the HMD device type. */ +/** + * The Head-mounted Display device enum to determine the HMD device type. + */ UENUM(BlueprintType) enum class ESGHeadMountedDisplayDevice : uint8 { @@ -62,4 +64,17 @@ enum class ESGHeadMountedDisplayDevice : uint8 /* The HTC VIVE XR Elite HMD. */ HtcViveXRElite UMETA(DisplayName = "HTC VIVE XR Elite"), -}; \ No newline at end of file +}; + +/* + * The + */ +UENUM(BlueprintType) +enum class ESGViveHMDDetectionPriority : uint8 +{ + /* First try to detect HTC VIVE Focus 3. */ + HtcViveFocus3First UMETA(DisplayName = "HTC VIVE Focus 3 First"), + + /* First try to detect HTC VIVE XR Elite. */ + HtcViveXREliteFirst UMETA(DisplayName = "HTC VIVE XR Elite First"), +};