fix a few wrist tracker issues and introduce SGWristTrackerVisualizationComponent

This commit is contained in:
Mamadou Babaei
2024-08-16 16:37:24 +02:00
parent e14440a911
commit c9aa422dd1
10 changed files with 484 additions and 104 deletions
+1
View File
@@ -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.
@@ -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"
@@ -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
@@ -0,0 +1,169 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FImpl, FImplDeleter>(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<USGWristTrackerComponent>(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;
}
@@ -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<UXRDeviceVisualizationComponent>(
ControllerVisualizerLeft = ObjectInitializer.CreateDefaultSubobject<USGWristTrackerVisualizationComponent>(
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<UXRDeviceVisualizationComponent>(
ControllerVisualizerRight = ObjectInitializer.CreateDefaultSubobject<USGWristTrackerVisualizationComponent>(
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
};
@@ -0,0 +1,76 @@
/**
* @file
*
* @author Mamadou Babaei <mamadou@senseglove.com>
*
* @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<FImpl, FImplDeleter> 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);
};
@@ -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;
}
@@ -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();
}
@@ -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);
@@ -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<IMotionController*> 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<FMotionControllerSource>& 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<FMotionControllerSource>& 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<EHandKeypoint>(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<IMotionController*> 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))
{