diff --git a/CHANGELOG.md b/CHANGELOG.md index c7b76de4..b20159fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This is a minor release focusing mainly on bringing OpenXR-compatible hand track - Added USGVirtualHandComponent::OnHandVisibilityChanged() event in order to notify other components/actors whenever the virtual hand mesh appears or disappears (for example, this could happen when a glove is connected/disconnected). - Added USGWristTrackerComponent::GetMotionControllerData(). - Added USGWristTrackerComponent::OnWristDeviceVisualTypeChanged event in order to be able to notify other components/actors whenever the wrist-tracking device switches between hand tracking or motion controllers. +- Added SGWristTrackerVisualizationComponent which is replacing UXRDeviceVisualizationComponent usage inside SGPawn. - Added the SenseGloveDebugKismet module in order to allow drawing of debugging gizmos and virtual hands from Blueprint. - Added a new static Draw() method overload to DebugGizmo which allows passing an FQuat instead of a FRotator. - Added SGEngineUtils. diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp index 037a0d11..3b207283 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp @@ -50,7 +50,6 @@ #include "SenseGlove/Components/SGWristTrackerComponent.h" #include "SGCore/SGHapticGlove.h" #include "SGDebug/SGDebugVirtualHand.h" -#include "SGLog/SGLog.h" #include "SGSettings/SGSettings.h" #include "SGTracking/SGGloveTracker.h" diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp index 32b3d777..86a863a8 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp @@ -35,14 +35,9 @@ #include "SenseGlove/Components/SGWristTrackerComponent.h" -#include "Animation/Skeleton.h" -#include "Components/SkeletalMeshComponent.h" -#include "Engine/SkeletalMesh.h" #include "IXRTrackingSystem.h" -#include "UObject/ConstructorHelpers.h" #include "SGDebug/SGDebugGizmo.h" -#include "SGLog/SGLog.h" #include "SGSettings/SGSettings.h" struct USGWristTrackerComponent::FImpl @@ -300,11 +295,9 @@ void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const 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().DebugGizmoSettings}; - FSGDebugGizmo::Draw(World, Location, Rotation, DebugGizmoSettings); + FSGDebugGizmo::Draw(World, Owner->WristLocation, Owner->WristRotation, DebugGizmoSettings); } void USGWristTrackerComponent::FImplDeleter::operator()(const FImpl* P) const diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerVisualizationComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerVisualizationComponent.cpp new file mode 100644 index 00000000..b84a0b6a --- /dev/null +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerVisualizationComponent.cpp @@ -0,0 +1,169 @@ +/** + * @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 "SenseGlove/Components/SGWristTrackerVisualizationComponent.h" + +#include "SenseGlove/Components/SGWristTrackerComponent.h" +#include "SGLog/SGLog.h" + +struct USGWristTrackerVisualizationComponent::FImpl +{ + /************************ + * Static methods + ************************/ + + /************************ + * Owner object + ************************/ + + USGWristTrackerVisualizationComponent* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(USGWristTrackerVisualizationComponent* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = default; + FImpl& operator=(const FImpl& Rhs) = default; + + /************************ + * Methods + ************************/ + + bool IsEditor() const; + + void UpdateLocationAndRotation() const; +}; + +USGWristTrackerVisualizationComponent::USGWristTrackerVisualizationComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer), + Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) +{ + PrimaryComponentTick.bTickEvenWhenPaused = false; + PrimaryComponentTick.bCanEverTick = true; + PrimaryComponentTick.bStartWithTickEnabled = true; + bTickInEditor = true; + bNeverNeedsRenderUpdate = true; + bAllowConcurrentTick = true; + + SetCastShadow(false); + SetCastHiddenShadow(false); + bCastDynamicShadow = false; + SetOnlyOwnerSee(false); + SetReceivesDecals(false); + SetCanEverAffectNavigation(false); +} + +void USGWristTrackerVisualizationComponent::InitializeComponent() +{ + Super::InitializeComponent(); + + Pimpl->UpdateLocationAndRotation(); +} + +void USGWristTrackerVisualizationComponent::BeginPlay() +{ + Super::BeginPlay(); + + Pimpl->UpdateLocationAndRotation(); +} + +void USGWristTrackerVisualizationComponent::TickComponent( + const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ +#if WITH_EDITOR + if (Pimpl->IsEditor()) + { + EditorTick(DeltaTime, TickType, ThisTickFunction); + return; + } +#endif /* WITH_EDITOR */ + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + Pimpl->UpdateLocationAndRotation(); +} + + +void USGWristTrackerVisualizationComponent::EditorTick( + const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + Pimpl->UpdateLocationAndRotation(); +} + +USGWristTrackerVisualizationComponent::FImpl::FImpl(USGWristTrackerVisualizationComponent* InOwner) + : Owner(InOwner) +{ +} + +USGWristTrackerVisualizationComponent::FImpl::~FImpl() = default; + +bool USGWristTrackerVisualizationComponent::FImpl::IsEditor() const +{ + const UWorld* World{Owner->GetWorld()}; + if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview)) + { + return true; + } + + return false; +} + +void USGWristTrackerVisualizationComponent::FImpl::UpdateLocationAndRotation() const +{ + USGWristTrackerComponent* ParentComponent{Cast(Owner->GetAttachParent())}; + if (!ensureAlwaysMsgf(IsValid(ParentComponent), + TEXT("This component should be attached to a SGWristTackerComponent!"))) + { + return; + } + + SGLOG(ParentComponent->GetWristRotation(), ParentComponent->GetWristLocation()) + + //Owner->MotionSource = FName("Left"); + // Owner->SetWorldLocation(ParentComponent->GetWristLocation()); + // Owner->SetWorldRotation(ParentComponent->GetWristRotation()); +} + +void USGWristTrackerVisualizationComponent::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} \ No newline at end of file diff --git a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp index 39ac3fbc..9f0b0e22 100644 --- a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp +++ b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp @@ -39,13 +39,13 @@ #include "Components/PrimitiveComponent.h" #include "Components/SphereComponent.h" #include "Kismet/KismetMathLibrary.h" -#include "XRDeviceVisualizationComponent.h" #include "UObject/UObjectGlobals.h" #include "SenseGlove/Components/SGGrabComponent.h" #include "SenseGlove/Components/SGTouchComponent.h" #include "SenseGlove/Components/SGVirtualHandComponent.h" #include "SenseGlove/Components/SGWristTrackerComponent.h" +#include "SenseGlove/Components/SGWristTrackerVisualizationComponent.h" #include "SGLog/SGLog.h" static constexpr float SG_HAND_MESH_YAW_CORRECTION_OFFSET = -90.0f; @@ -190,7 +190,7 @@ ASGPawn::ASGPawn(const FObjectInitializer& ObjectInitializer) WristTrackerLeft->SetupAttachment(GetRootComponent()); WristTrackerLeft->SetRight(false); - ControllerVisualizerLeft = ObjectInitializer.CreateDefaultSubobject( + ControllerVisualizerLeft = ObjectInitializer.CreateDefaultSubobject( this, TEXT("ControllerVisualizerLeft")); ControllerVisualizerLeft->SetupAttachment(WristTrackerLeft); ControllerVisualizerLeft->SetDisplayModelSource(FName("OpenXR")); @@ -349,7 +349,7 @@ ASGPawn::ASGPawn(const FObjectInitializer& ObjectInitializer) WristTrackerRight->SetupAttachment(GetRootComponent()); WristTrackerRight->SetRight(true); - ControllerVisualizerRight = ObjectInitializer.CreateDefaultSubobject( + ControllerVisualizerRight = ObjectInitializer.CreateDefaultSubobject( this, TEXT("ControllerVisualizerRight")); ControllerVisualizerRight->SetupAttachment(WristTrackerRight); ControllerVisualizerRight->SetDisplayModelSource(FName("OpenXR")); @@ -1687,7 +1687,7 @@ void ASGPawn::FImpl::UpdateWristTrackerVisualization(const bool bRight, const EX const bool bIsVisualizationActive = VisualType == EXRVisualType::Controller || !!Settings.bHandTrackingVisualizationAllowed; - UXRDeviceVisualizationComponent* ControllerVisualizer{ + USGWristTrackerVisualizationComponent* ControllerVisualizer{ bRight ? Owner->ControllerVisualizerLeft : Owner->ControllerVisualizerRight }; diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerVisualizationComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerVisualizationComponent.h new file mode 100644 index 00000000..6d2b4b6f --- /dev/null +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerVisualizationComponent.h @@ -0,0 +1,76 @@ +/** + * @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 "GameFramework/Actor.h" +#include "HeadMountedDisplayTypes.h" +#include "Math/Rotator.h" +#include "Math/Vector.h" +#include "XRDeviceVisualizationComponent.h" +#include "Templates/UniquePtr.h" + +#include "SGWristTrackerVisualizationComponent.generated.h" + +UCLASS(Blueprintable, ClassGroup = MotionController, meta = (BlueprintSpawnableComponent)) +class SENSEGLOVE_API USGWristTrackerVisualizationComponent : public UXRDeviceVisualizationComponent +{ + GENERATED_UCLASS_BODY() + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +public: + virtual void InitializeComponent() override; + + virtual void BeginPlay() override; + + virtual void TickComponent(float DeltaTime, + enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) override; + +protected: + virtual void EditorTick(float DeltaTime, + enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction); +}; \ No newline at end of file diff --git a/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h b/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h index e812d410..c8af2178 100644 --- a/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h +++ b/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h @@ -48,10 +48,10 @@ class UCameraComponent; class UPrimitiveComponent; class USphereComponent; -class UXRDeviceVisualizationComponent; class USGVirtualHandComponent; class USGWristTrackerComponent; +class USGWristTrackerVisualizationComponent; UCLASS(Config=Game, BlueprintType, Blueprintable) class SENSEGLOVE_API ASGPawn : public APawn @@ -131,7 +131,7 @@ private: USGWristTrackerComponent* WristTrackerLeft; UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) - UXRDeviceVisualizationComponent* ControllerVisualizerLeft; + USGWristTrackerVisualizationComponent* ControllerVisualizerLeft; UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) USGVirtualHandComponent* HandLeft; @@ -167,7 +167,7 @@ private: USGWristTrackerComponent* WristTrackerRight; UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) - UXRDeviceVisualizationComponent* ControllerVisualizerRight; + USGWristTrackerVisualizationComponent* ControllerVisualizerRight; UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) USGVirtualHandComponent* HandRight; @@ -251,7 +251,7 @@ public: return WristTrackerLeft; } - FORCEINLINE UXRDeviceVisualizationComponent* GetControllerVisualizerLeft() const + FORCEINLINE USGWristTrackerVisualizationComponent* GetControllerVisualizerLeft() const { return ControllerVisualizerLeft; } @@ -311,7 +311,7 @@ public: return WristTrackerRight; } - FORCEINLINE UXRDeviceVisualizationComponent* GetControllerVisualizerRight() const + FORCEINLINE USGWristTrackerVisualizationComponent* GetControllerVisualizerRight() const { return ControllerVisualizerLeft; } diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGPawnKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGPawnKismetLibrary.cpp index 96011ce6..e3be1f63 100644 --- a/Source/SenseGloveKismet/Private/SGKismet/SGPawnKismetLibrary.cpp +++ b/Source/SenseGloveKismet/Private/SGKismet/SGPawnKismetLibrary.cpp @@ -38,10 +38,10 @@ #include "Runtime/Launch/Resources/Version.h" #include "Camera/CameraComponent.h" #include "Components/SphereComponent.h" -#include "XRDeviceVisualizationComponent.h" #include "SenseGlove/Components/SGVirtualHandComponent.h" #include "SenseGlove/Components/SGWristTrackerComponent.h" +#include "SenseGlove/Components/SGWristTrackerVisualizationComponent.h" #include "SenseGlove/GameFramework/SGPawn.h" UCameraComponent* UPawnKismetLibrary::GetCamera(const ASGPawn* Pawn) @@ -54,7 +54,7 @@ USGWristTrackerComponent* UPawnKismetLibrary::GetWristTrackerLeft(const ASGPawn* return Pawn->GetWristTrackerLeft(); } -UXRDeviceVisualizationComponent* UPawnKismetLibrary::GetControllerVisualizerLeft(const ASGPawn* Pawn) +USGWristTrackerVisualizationComponent* UPawnKismetLibrary::GetControllerVisualizerLeft(const ASGPawn* Pawn) { return Pawn->GetControllerVisualizerLeft(); } @@ -114,7 +114,7 @@ USGWristTrackerComponent* UPawnKismetLibrary::GetWristTrackerRight(const ASGPawn return Pawn->GetWristTrackerRight(); } -UXRDeviceVisualizationComponent* UPawnKismetLibrary::GetControllerVisualizerRight(const ASGPawn* Pawn) +USGWristTrackerVisualizationComponent* UPawnKismetLibrary::GetControllerVisualizerRight(const ASGPawn* Pawn) { return Pawn->GetControllerVisualizerRight(); } diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGPawnKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGPawnKismetLibrary.h index 3ad85e9a..0df38e1f 100644 --- a/Source/SenseGloveKismet/Public/SGKismet/SGPawnKismetLibrary.h +++ b/Source/SenseGloveKismet/Public/SGKismet/SGPawnKismetLibrary.h @@ -49,11 +49,11 @@ class UCameraComponent; class USphereComponent; -class UXRDeviceVisualizationComponent; class ASGPawn; class USGVirtualHandComponent; class USGWristTrackerComponent; +class USGWristTrackerVisualizationComponent; UCLASS(meta=(ScriptName = "SesneGlovePawnKismetLibrary")) class SENSEGLOVEKISMET_API UPawnKismetLibrary final : public USGBlueprintFunctionLibrary @@ -68,7 +68,7 @@ public: static USGWristTrackerComponent* GetWristTrackerLeft(const ASGPawn* Pawn); UFUNCTION(BlueprintPure, Category="SenseGlove | Game Framework | Pawn") - static UXRDeviceVisualizationComponent* GetControllerVisualizerLeft(const ASGPawn* Pawn); + static USGWristTrackerVisualizationComponent* GetControllerVisualizerLeft(const ASGPawn* Pawn); UFUNCTION(BlueprintPure, Category="SenseGlove | Game Framework | Pawn") static USGVirtualHandComponent* GetHandLeft(const ASGPawn* Pawn); @@ -104,7 +104,7 @@ public: static USGWristTrackerComponent* GetWristTrackerRight(const ASGPawn* Pawn); UFUNCTION(BlueprintPure, Category="SenseGlove | Game Framework | Pawn") - static UXRDeviceVisualizationComponent* GetControllerVisualizerRight(const ASGPawn* Pawn); + static USGWristTrackerVisualizationComponent* GetControllerVisualizerRight(const ASGPawn* Pawn); UFUNCTION(BlueprintPure, Category="SenseGlove | Game Framework | Pawn") static USGVirtualHandComponent* GetHandRight(const ASGPawn* Pawn); diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp index d5bc1bde..dee55121 100644 --- a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp @@ -139,6 +139,136 @@ struct FSGXRTracker::FImpl return Settings->GetTrackingSettings().WristTrackingSettings; } + FORCEINLINE static const FName& GetDefaultLeftMotionSourceName() + { + static const FName Name(TEXT("Left")); + return Name; + } + + FORCEINLINE static const FName& GetDefaultRightMotionSourceName() + { + static const FName Name(TEXT("Right")); + return Name; + } + + FORCEINLINE static const FName& GetDefaultMotionSourceName(const EControllerHand Hand) + { + const FName& Name{ + Hand == EControllerHand::Right + ? GetDefaultRightMotionSourceName() + : GetDefaultLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetDefaultMotionSourceName(const bool bRight) + { + const FName& Name{ + bRight + ? GetDefaultRightMotionSourceName() + : GetDefaultLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetLegacyControllerLeftMotionSourceName() + { + static const FName Name(TEXT("Left")); + return Name; + } + + FORCEINLINE static const FName& GetLegacyControllerRightMotionSourceName() + { + static const FName Name(TEXT("Right")); + return Name; + } + + FORCEINLINE static const FName& GetLegacyControllerMotionSourceName(const EControllerHand Hand) + { + const FName& Name{ + Hand == EControllerHand::Right + ? GetLegacyControllerRightMotionSourceName() + : GetLegacyControllerLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetLegacyControllerMotionSourceName(const bool bRight) + { + const FName& Name{ + bRight + ? GetLegacyControllerRightMotionSourceName() + : GetLegacyControllerLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetMoreSpecificLeftMotionSourceName() + { + static const FName Name(TEXT("HandTrackingLeft")); + return Name; + } + + FORCEINLINE static const FName& GetMoreSpecificRightMotionSourceName() + { + static const FName Name(TEXT("HandTrackingRight")); + return Name; + } + + FORCEINLINE static const FName& GetMoreSpecificMotionSourceName(const EControllerHand Hand) + { + const FName& Name{ + Hand == EControllerHand::Right + ? GetMoreSpecificRightMotionSourceName() + : GetMoreSpecificLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetMoreSpecificMotionSourceName(const bool bRight) + { + const FName& Name{ + bRight + ? GetMoreSpecificRightMotionSourceName() + : GetMoreSpecificLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetLeftMotionSourceName() + { + const bool bUseMoreSpecificMotionSourceNames = ShouldUseMoreSpecificMotionSourceNames(); + const FName& Name{ + bUseMoreSpecificMotionSourceNames + ? GetMoreSpecificLeftMotionSourceName() + : GetDefaultLeftMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetRightMotionSourceName() + { + const bool bUseMoreSpecificMotionSourceNames = ShouldUseMoreSpecificMotionSourceNames(); + const FName& Name{ + bUseMoreSpecificMotionSourceNames + ? GetMoreSpecificRightMotionSourceName() + : GetDefaultRightMotionSourceName() + }; + return Name; + } + + FORCEINLINE static const FName& GetMotionSourceName(const EControllerHand Hand) + { + const FName& Name{Hand == EControllerHand::Right ? GetRightMotionSourceName() : GetLeftMotionSourceName()}; + return Name; + } + + FORCEINLINE static const FName& GetMotionSourceName(const bool bRight) + { + const FName& Name{bRight ? GetRightMotionSourceName() : GetLeftMotionSourceName()}; + return Name; + } + static ESGPositionalTrackingHardware GetPositionalTrackingHardware(); FORCEINLINE static USGHapticGlove* GetGlove(const EControllerHand Hand) @@ -926,19 +1056,24 @@ void FSGXRTracker::UpdateDeviceLocations( LocateInfo.baseSpace = TrackingSpace; LocateInfo.time = DisplayTime; + const bool bFallbackToHandTrackingIfNoGloveDetected = FImpl::ShouldFallbackToHandTrackingIfNoGloveDetected(); + 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. + /// See the NOTE inside the FSGXRTracker::FImpl::GetWristTransform method. Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[0], LocateInfo); Pimpl->UpdateSGHandState(EControllerHand::Left, Pimpl->HandStates[0]); } else { - Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[0], LocateInfo); + if (bFallbackToHandTrackingIfNoGloveDetected) + { + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[0], LocateInfo); + } } const bool bRightGloveConnected = FImpl::IsRightGloveConnected(); @@ -953,7 +1088,10 @@ void FSGXRTracker::UpdateDeviceLocations( } else { - Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[1], LocateInfo); + if (bFallbackToHandTrackingIfNoGloveDetected) + { + Pimpl->UpdateXRHandTrackingData(Pimpl->HandStates[1], LocateInfo); + } } } @@ -984,36 +1122,40 @@ bool FSGXRTracker::GetControllerOrientationAndPosition(const int32 ControllerInd { FTransform ControllerTransform{FTransform::Identity}; + const bool bFallbackToHandTrackingIfNoGloveDetected = FImpl::ShouldFallbackToHandTrackingIfNoGloveDetected(); + if (bFallbackToHandTrackingIfNoGloveDetected) + { #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 - const FImpl::FSGMotionSourceInfo* KeyPointInfoPtr{Pimpl->MotionSourceToKeypointMap.Find(MotionSource)}; - if (KeyPointInfoPtr) + 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; + 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 (bIsLeft) + { #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 - ControllerTransform = GetLeftHandState().GetTransform(KeyPoint); + ControllerTransform = GetLeftHandState().GetTransform(KeyPoint); #else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ ControllerTransform = GetLeftHandState().GetTransform(EHandKeypoint::Wrist); #endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ - bTracked = !!GetLeftHandState().bReceivedJointPoses; - } - else - { + bTracked = !!GetLeftHandState().bReceivedJointPoses; + } + else + { #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 - ControllerTransform = GetRightHandState().GetTransform(KeyPoint); + ControllerTransform = GetRightHandState().GetTransform(KeyPoint); #else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ ControllerTransform = GetRightHandState().GetTransform(EHandKeypoint::Wrist); #endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 3 */ - bTracked = !!GetRightHandState().bReceivedJointPoses; + bTracked = !!GetRightHandState().bReceivedJointPoses; + } } } @@ -1026,24 +1168,10 @@ bool FSGXRTracker::GetControllerOrientationAndPosition(const int32 ControllerInd { static const FName OpenXRName{TEXT("OpenXR")}; + static constexpr int32 CurrentPlayerControllerIndex = 0; + #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; - } + const FName& MotionSource{FImpl::GetDefaultMotionSourceName(DeviceHand)}; #endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION <= 2 */ const TArray MotionControllers{ @@ -1064,8 +1192,9 @@ bool FSGXRTracker::GetControllerOrientationAndPosition(const int32 ControllerInd continue; } - const ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus(0, MotionSource); - if (TrackingStatus != ETrackingStatus::Tracked) + const ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus( + CurrentPlayerControllerIndex, MotionSource); + if (TrackingStatus == ETrackingStatus::NotTracked) { continue; } @@ -1110,8 +1239,12 @@ ETrackingStatus FSGXRTracker::GetControllerTrackingStatus(const int32 Controller return ETrackingStatus::Tracked; } - const FSGXRHandState& HandState{bIsLeft ? GetLeftHandState() : GetRightHandState()}; - return !!HandState.bReceivedJointPoses ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked; + const bool bFallbackToHandTrackingIfNoGloveDetected = FImpl::ShouldFallbackToHandTrackingIfNoGloveDetected(); + if (bFallbackToHandTrackingIfNoGloveDetected) + { + 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 */ @@ -1129,8 +1262,6 @@ void FSGXRTracker::EnumerateSources(TArray& SourcesOut) { ensureAlwaysMsgf(IsInGameThread(), TEXT("Is not in the game thread!")); - const bool bUseMoreSpecificMotionSourceNames = FImpl::ShouldUseMoreSpecificMotionSourceNames(); - SourcesOut.Reserve(SourcesOut.Num() + (EHandKeypointCount * 2)); const UEnum* EnumPtr{ @@ -1138,14 +1269,14 @@ void FSGXRTracker::EnumerateSources(TArray& SourcesOut) }; 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")); + const FName& Left(FImpl::GetLeftMotionSourceName()); + const FName& Right(FImpl::GetRightMotionSourceName()); 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)}; + const FString StringLeft{FString::Printf(TEXT("%s%s"), *Left.ToString(), *EnumString)}; + const FString StringRight{FString::Printf(TEXT("%s%s"), *Right.ToString(), *EnumString)}; FName SourceL(*(StringLeft)); FName SourceR(*(StringRight)); SourcesOut.Add(MoveTemp(SourceL)); @@ -1345,7 +1476,6 @@ void FSGXRTracker::FImpl::BuildMotionSourceToKeypointMap() 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]. @@ -1360,24 +1490,28 @@ void FSGXRTracker::FImpl::BuildMotionSourceToKeypointMap() 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")); + const FName& Left(GetLeftMotionSourceName()); + const FName& Right(GetRightMotionSourceName()); 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); + FName LeftName(Left.ToString() + EnumName); + FName RightName(Right.ToString() + EnumName); MotionSourceToKeypointMap.Add(MoveTemp(LeftName), FSGMotionSourceInfo(EnumValue, true)); MotionSourceToKeypointMap.Add(MoveTemp(RightName), FSGMotionSourceInfo(EnumValue, false)); } if (bSupportLegacyControllerMotionSources) { - MotionSourceToKeypointMap.Add(FName("Left"), FSGMotionSourceInfo(EHandKeypoint::Wrist, true)); - MotionSourceToKeypointMap.Add(FName("Right"), FSGMotionSourceInfo(EHandKeypoint::Wrist, false)); + const FName& LeftLegacyControllerMotionSourceName{GetLegacyControllerLeftMotionSourceName()}; + const FName& RightLegacyControllerMotionSourceName{GetLegacyControllerRightMotionSourceName()}; + MotionSourceToKeypointMap.Add( + LeftLegacyControllerMotionSourceName, FSGMotionSourceInfo(EHandKeypoint::Wrist, true)); + MotionSourceToKeypointMap.Add( + RightLegacyControllerMotionSourceName, FSGMotionSourceInfo(EHandKeypoint::Wrist, false)); } } @@ -1526,23 +1660,11 @@ bool FSGXRTracker::FImpl::GetWristTransform( 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; - } - + const FName& MotionSource{GetMotionSourceName(DeviceHand)}; static constexpr int32 CurrentPlayerControllerIndex = 0; + const bool bFallbackToHandTrackingIfNoGloveDetected = ShouldFallbackToHandTrackingIfNoGloveDetected(); + const TArray MotionControllers{IModularFeatures::Get().GetModularFeatureImplementations< IMotionController>(IMotionController::GetModularFeatureName())}; @@ -1553,25 +1675,28 @@ bool FSGXRTracker::FImpl::GetWristTransform( continue; } - ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus( + const ETrackingStatus TrackingStatus = MotionController->GetControllerTrackingStatus( CurrentPlayerControllerIndex, MotionSource); - if (TrackingStatus != ETrackingStatus::Tracked) + if (TrackingStatus == ETrackingStatus::NotTracked) { continue; } - FName DeviceTypeName{MotionController->GetMotionControllerDeviceTypeName()}; + const 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. + // 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) + if (bFallbackToHandTrackingIfNoGloveDetected) { - bOutMotionSourceHandTracking = true; - const bool bResult = GetOpenXRHandTrackingWristTransform(DeviceHand, OutTransform); - return bResult; + if (DeviceTypeName == OpenXRHandTrackingName) + { + bOutMotionSourceHandTracking = true; + const bool bResult = GetOpenXRHandTrackingWristTransform(DeviceHand, OutTransform); + return bResult; + } } if (DeviceTypeName == OpenXRName) @@ -1582,6 +1707,20 @@ bool FSGXRTracker::FImpl::GetWristTransform( } } + /// WORKAROUND + // Sometimes the motion controller array does not contain a OpenXRHandTracking though it's being tracked! So, as a + // last resort we always attempt at retrieving the OpenXRHandTracking wrist transform in case all other measures we + // have taken has already failed! + if (bFallbackToHandTrackingIfNoGloveDetected) + { + const bool bResult = GetOpenXRHandTrackingWristTransform(DeviceHand, OutTransform); + if (bResult) + { + bOutMotionSourceHandTracking = true; + } + return bResult; + } + return false; } @@ -2039,10 +2178,13 @@ bool FSGXRTracker::FImpl::UpdateSGHandState(const EControllerHand DeviceHand, FS FTransform WristTransform; bool bMotionSourceHandTracking = false; - if (!GetWristTransform(DeviceHand, WristTransform, bMotionSourceHandTracking)) - { - return false; - } + const bool bGotWristTransform = GetWristTransform(DeviceHand, WristTransform, bMotionSourceHandTracking); + + /// NOTE + // Even if we fail to get the wrist transform, the SenseGlove data needs to make it to the FXRMotionControllerData! + // So, we just ignore the bGotWristTransform results! Otherwise, even if bFallbackToHandTrackingIfNoGloveDetected + // is disabled it will output the hand-tracking data. + (void) bGotWristTransform; if (!UpdateSGWristJointData(OutHandState, WristTransform, bMotionSourceHandTracking)) {