diff --git a/Handbook/src/appendix/changelog.md b/Handbook/src/appendix/changelog.md index bc1df1fb..e794ef46 100644 --- a/Handbook/src/appendix/changelog.md +++ b/Handbook/src/appendix/changelog.md @@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +This is a minor release with some breaking API and ABI changes, focusing mainly on migrating away from the deprecated `FXRMotionControllerData` in favor of `FXRMotionControllerState` and `FXRHandTrackingState` on Unreal Engine `5.5+`. + +### Added + +- Completed support for the upcoming Unreal Engine `5.5` release. +- Added `USGVirtualHandComponent::GetMotionControllerState()` and the equivalent Blueprint function`UVirtualHandComponentKismetLibrary::GetMotionControllerState` on UE `5.5+`. +- Added `USGVirtualHandComponent::GetHandTrackingState()` and the equivalent Blueprint function `UVirtualHandComponentKismetLibrary::GetHandTrackingState` on UE `5.5+`. +- Added `USGWristTrackerComponent::GetMotionControllerState()` and the equivalent Blueprint function `UWristTrackerComponentKismetLibrary::GetMotionControllerState` on UE `5.5+`. +- Added `USGWristTrackerComponent::GetHandTrackingState()` and the equivalent Blueprint function `UWristTrackerComponentKismetLibrary::GetHandTrackingState` on UE `5.5+`. +- Added a variant of `FSGDebugVirtualHand::Draw()` and the equivalent Blueprint function `USGDebugVirtualHandKismetLibrary::Draw_FXRHandTrackingState()` which accept `FXRHandTrackingState` on UE `5.5+`. +- Added the new member `bTracked` to the `FSGXRHandState` struct. +- Added `FSGXRTracker::GetMotionControllerState()` and the equivalent Blueprint function `USGXRTrackerKismetLibrary::GetMotionControllerState()`. +- Added `FSGXRTracker::GetHandTrackingState()` and the equivalent Blueprint function `USGXRTrackerKismetLibrary::GetHandTrackingState()`. + +### Fixed + +- Additional minor fixes and improvements that may not be listed here. + +### Changed + +- Replaced all internal usages of the `FXRMotionControllerData` struct with either `FXRMotionControllerState` or `FXRHandTrackingState` on UE `5.5+`. +- Deprecated `USGVirtualHandComponent::GetMotionControllerData()` on UE `5.5+`. +- Deprecated `USGWristTrackerComponent::GetMotionControllerData()` on UE `5.5+`. +- Deprecated the variant of `FSGDebugVirtualHand::Draw()` which accepts `FXRMotionControllerData` as a parameter on UE `5.5+`. +- Renamed `USGDebugVirtualHandKismetLibrary::Draw` to `USGDebugVirtualHandKismetLibrary::Draw_FXRMotionControllerData` for more clarification. +- Renamed an `FSGXRHandState` member from `bReceivedJointPoses` to `bHasReceivedJointPoses`. +- Changed the `FSGXRTracker::GetAllKeypointStates()` signature on UE `5.5+` to match the `IHandTracker` interface API changes. +- The animation system on UE `5.5+` has been revamped to utilize `FXRHandTrackingState` instead of `FXRMotionControllerData`. +- The wrist tracking system on UE `5.5+` has been revamped to utilize `FXRHandTrackingState` instead of `FXRMotionControllerData`. +- The hand interaction manipulation on UE `5.5+` has been revamped to utilize `FXRHandTrackingState`. +- The virtual hand debugging system on UE `5.5+` has been revamped to utilize `FXRHandTrackingState`. + ## [2.1.3] - 2024-10-11 ### Added diff --git a/Handbook/src/appendix/planned-features-completion-status.md b/Handbook/src/appendix/planned-features-completion-status.md index 6b68deaf..661cacc6 100644 --- a/Handbook/src/appendix/planned-features-completion-status.md +++ b/Handbook/src/appendix/planned-features-completion-status.md @@ -1,6 +1,6 @@ ## Planned Features Completion Status -### Implemented as of v2.1.x +### Implemented as of v2.2.x - [X] Full SenseGlove low-level core API access through Unreal C++. - [X] Full SenseGlove low-level core API access through Blueprint. @@ -28,17 +28,18 @@ - [X] C++/Blueprint interaction events such as OnGrabStateUpdated, OnTouchStateUpdated, OnActorGrabbed, OnActorReleased, OnActorBeginTouch, and OnActorEndTouch. - [X] A fall back to HMD and wrist tracker hardware auto-detection mechanism when automatic detection of the wrist tracker hardware is desired. - [X] OpenXR-compatible hand tracking (XR_EXT_hand_tracking) support. -- [X] FXRMotionControllerData compatible hand animation system. -- [X] FXRMotionControllerData compatible wrist tracking system. -- [X] FXRMotionControllerData compatible hand interaction manipulation system. +- [X] `FXRMotionControllerData` compatible hand animation system on UE versions `5.2`, `5.3`, and `5.4`. +- [X] `FXRHandTrackingState` compatible hand animation system on UE versions `5.5+`. +- [X] `FXRMotionControllerData` compatible wrist tracking system on UE versions `5.2`, `5.3`, and `5.4`. +- [X] `FXRHandTrackingState` compatible wrist tracking system on UE versions `5.5+`. +- [X] `FXRMotionControllerData` compatible hand interaction manipulation system on UE versions `5.2`, `5.3`, and `5.4`. +- [X] `FXRHandTrackingState` compatible hand interaction manipulation system on UE versions `5.5+`. - [X] Ability to fallback to hand tracking when a glove is not present and use the bare hands for interactions, or a combination of glove and hand tracking if no motion controller input is detected. - [X] The SenseGlove grab/touch sockets one-click-setup ability on any Epic-compliant virtual hand mesh from within the Unreal Editor's Content Browser, Skeleton Editor, or Skeletal Mesh Editor. - [X] A flexible virtual hand animation system that can take the mesh bone's transforms into account for a more reliable hand animation. - [X] Ability to manage the Engine Scalability Settings through the SenseGlove plugin in order to change the graphics settings on the fly. -### Upcoming features planned for the v2.2.x release - -- [ ] Migrating away from the deprecated `FXRMotionControllerData` in favor of `FXRMotionControllerState` and `FXRHandTrackingState`. +### Upcoming features planned for the v2.3.x release ### Planned features long-term diff --git a/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp b/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp index 9c146a12..825b15e4 100644 --- a/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp @@ -47,7 +47,6 @@ #include "Runtime/Launch/Resources/Version.h" #include "SenseGlove/Components/SGVirtualHandComponent.h" -#include "SGLog/SGLog.h" FSGVirtualHandAnimInstanceProxy::FSGVirtualHandAnimInstanceProxy() : FAnimInstanceProxy{} @@ -108,6 +107,16 @@ void FSGVirtualHandAnimInstanceProxy::PreEvaluateAnimation(UAnimInstance* InAnim return; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + const bool bGotHandTrackingState = VirtualHand->GetHandTrackingState( + EXRSpaceType::UnrealWorldSpace, HandTrackingState); + if (!bGotHandTrackingState || !HandTrackingState.bValid) + { + return; + } + ensureAlwaysMsgf(HandTrackingState.HandKeyLocations.Num() == HandTrackingState.HandKeyRotations.Num(), + TEXT("Mismatched hand tracking state locations and rotations!")); +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ const bool bGotMotionControllerData = VirtualHand->GetMotionControllerData(MotionControllerData); if (!bGotMotionControllerData || !MotionControllerData.bValid) { @@ -115,10 +124,18 @@ void FSGVirtualHandAnimInstanceProxy::PreEvaluateAnimation(UAnimInstance* InAnim } ensureAlwaysMsgf(MotionControllerData.HandKeyPositions.Num() == MotionControllerData.HandKeyRotations.Num(), TEXT("Mismatched motion controller data positions and rotations!")); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + BoneNamesMap.Empty(); for (TArray::SizeType HandKeyIndex = 0; - HandKeyIndex < MotionControllerData.HandKeyRotations.Num(); ++HandKeyIndex) + HandKeyIndex < +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + HandTrackingState.HandKeyRotations.Num() +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + MotionControllerData.HandKeyRotations.Num() +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + ; ++HandKeyIndex) { const FName& BoneName{VirtualHand->GetHandBoneName(HandKeyIndex)}; BoneNamesMap.Add(HandKeyIndex, BoneName); @@ -136,10 +153,17 @@ bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output) return FAnimInstanceProxy::Evaluate(Output); } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + if (!HandTrackingState.bValid) + { + return FAnimInstanceProxy::Evaluate(Output); + } +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ if (!MotionControllerData.bValid) { return FAnimInstanceProxy::Evaluate(Output); } +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FComponentSpacePoseContext PoseContext{Output.AnimInstanceProxy}; PoseContext.Pose.InitPose(Output.Pose); @@ -164,9 +188,16 @@ bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output) BoneReference.BoneIndex = BoneIndex; BoneReference.bUseSkeletonIndex = true; +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + const FVector& HandKeyLocation{HandTrackingState.HandKeyLocations[HandKeyIndex]}; + const FQuat& HandKeyRotation{HandTrackingState.HandKeyRotations[HandKeyIndex]}; + const FTransform HandKeyTransform{HandKeyRotation, HandKeyLocation}; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ const FVector& HandKeyPosition{MotionControllerData.HandKeyPositions[HandKeyIndex]}; const FQuat& HandKeyRotation{MotionControllerData.HandKeyRotations[HandKeyIndex]}; const FTransform HandKeyTransform{HandKeyRotation, HandKeyPosition}; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + const FTransform BoneTransform{AnimationBoneCorrectionOffsetTransform * HandKeyTransform}; FRotator BoneRotation{BoneTransform.GetRotation().Rotator()}; diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp index 33893693..10ba4f17 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp @@ -452,8 +452,10 @@ bool USGVirtualHandComponent::GetMotionControllerData(FXRMotionControllerData& O OutMotionControllerData.bValid = false; const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + PRAGMA_DISABLE_DEPRECATION_WARNINGS const bool bGotMotionControllerData = FSGXRTracker::GetMotionControllerData(this, Hand, OutMotionControllerData); + PRAGMA_ENABLE_DEPRECATION_WARNINGS if (!bGotMotionControllerData || !OutMotionControllerData.bValid) { return false; @@ -462,6 +464,46 @@ bool USGVirtualHandComponent::GetMotionControllerData(FXRMotionControllerData& O return OutMotionControllerData.bValid; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + +bool USGVirtualHandComponent::GetMotionControllerState( + const EXRSpaceType XRSpaceType, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + OutMotionControllerState.bValid = false; + + const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + const bool bGotMotionControllerState = + FSGXRTracker::GetMotionControllerState( + this, XRSpaceType, Hand, XRControllerPoseType, OutMotionControllerState); + if (!bGotMotionControllerState || !OutMotionControllerState.bValid) + { + return false; + } + + return OutMotionControllerState.bValid; +} + +bool USGVirtualHandComponent::GetHandTrackingState( + const EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState) +{ + OutHandTrackingState.bValid = false; + + const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + const bool bGotHandTrackingState = + FSGXRTracker::GetHandTrackingState( + this, XRSpaceType, Hand, OutHandTrackingState); + if (!bGotHandTrackingState || !OutHandTrackingState.bValid) + { + return false; + } + + return OutHandTrackingState.bValid; +} + +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + const TArray& USGVirtualHandComponent::GetHandBoneNames() const { const TArray& Names{ @@ -747,12 +789,25 @@ void USGVirtualHandComponent::FImpl::SetVisibility(const bool bInVisible) void USGVirtualHandComponent::FImpl::UpdateVisibility() { +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + FXRHandTrackingState HandTrackingState; + const bool bGotHandTrackingState = + Owner->GetHandTrackingState(EXRSpaceType::UnrealWorldSpace, HandTrackingState); +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FXRMotionControllerData MotionControllerData; const bool bGotMotionControllerData = Owner->GetMotionControllerData(MotionControllerData); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + const bool bHandDataAvailable = +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + bGotHandTrackingState + && HandTrackingState.bValid +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ bGotMotionControllerData && MotionControllerData.bValid - && MotionControllerData.DeviceVisualType == EXRVisualType::Hand; + && MotionControllerData.DeviceVisualType == EXRVisualType::Hand +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + ; const bool bHandVisible = Owner->IsVisible(); const bool bShouldBeVisibleWhenHandDataUnavailable = Owner->GetVirtualHandSettings().bVisibleWhenHandDataUnavailable; @@ -811,6 +866,16 @@ void USGVirtualHandComponent::FImpl::DrawDebugVirtualHand() const return; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + FXRHandTrackingState HandTrackingState; + const bool bGotHandTrackingState = Owner->GetHandTrackingState(EXRSpaceType::UnrealWorldSpace, HandTrackingState); + if (!bGotHandTrackingState || !HandTrackingState.bValid) + { + return; + } + + FSGDebugVirtualHand::Draw(World, HandTrackingState, Owner->GetVirtualHandSettings().DebuggingSettings); +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FXRMotionControllerData MotionControllerData; const bool bGotMotionControllerData = Owner->GetMotionControllerData(MotionControllerData); if (!bGotMotionControllerData || !MotionControllerData.bValid) @@ -819,6 +884,7 @@ void USGVirtualHandComponent::FImpl::DrawDebugVirtualHand() const } FSGDebugVirtualHand::Draw(World, MotionControllerData, Owner->GetVirtualHandSettings().DebuggingSettings); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ } void USGVirtualHandComponent::FImpl::TriggerHandVisibilityChangedEvent(const bool bNewVisibility) const diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp index ee09713b..863fcb2f 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp @@ -246,8 +246,11 @@ bool USGWristTrackerComponent::GetMotionControllerData(FXRMotionControllerData& OutMotionControllerData.bValid = false; const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + PRAGMA_DISABLE_DEPRECATION_WARNINGS const bool bGotMotionControllerData = FSGXRTracker::GetMotionControllerData(this, Hand, OutMotionControllerData); + PRAGMA_ENABLE_DEPRECATION_WARNINGS + if (!bGotMotionControllerData || !OutMotionControllerData.bValid) { return false; @@ -256,6 +259,46 @@ bool USGWristTrackerComponent::GetMotionControllerData(FXRMotionControllerData& return OutMotionControllerData.bValid; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + +bool USGWristTrackerComponent::GetMotionControllerState( + const EXRSpaceType XRSpaceType, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + OutMotionControllerState.bValid = false; + + const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + const bool bGotMotionControllerState = + FSGXRTracker::GetMotionControllerState( + this, XRSpaceType, Hand, XRControllerPoseType, OutMotionControllerState); + if (!bGotMotionControllerState || !OutMotionControllerState.bValid) + { + return false; + } + + return OutMotionControllerState.bValid; +} + +bool USGWristTrackerComponent::GetHandTrackingState( + const EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState) +{ + OutHandTrackingState.bValid = false; + + const EControllerHand Hand = IsRight() ? EControllerHand::Right : EControllerHand::Left; + const bool bGotHandTrackingState = + FSGXRTracker::GetHandTrackingState( + this, XRSpaceType, Hand, OutHandTrackingState); + if (!bGotHandTrackingState || !OutHandTrackingState.bValid) + { + return false; + } + + return OutHandTrackingState.bValid; +} + +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + USGWristTrackerComponent::FImpl::FImpl(USGWristTrackerComponent* InOwner) : bOverridePluginSettingsPropertyAlreadyModified(false), Owner(InOwner) diff --git a/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h b/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h index 2dd97607..83675e73 100644 --- a/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h +++ b/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h @@ -67,7 +67,12 @@ private: bool bVirtualHandVisible; bool bMarkRenderStateDirty; +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + FXRHandTrackingState HandTrackingState; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FXRMotionControllerData MotionControllerData; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + TMap::SizeType, FName> BoneNamesMap; FTransform AnimationBoneCorrectionOffsetTransform; @@ -103,10 +108,17 @@ protected: return bMarkRenderStateDirty; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + FORCEINLINE const FXRHandTrackingState& GetHandTrackingState() const + { + return HandTrackingState; + } +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FORCEINLINE const FXRMotionControllerData& GetMotionControllerData() const { return MotionControllerData; } +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ FORCEINLINE const TMap::SizeType, FName>& GetBoneNamesMap() const { diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h index c0819dfb..224112d5 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h @@ -175,8 +175,19 @@ protected: public: bool IsGloveConnected() const; USGHapticGlove* GetConnectedGlove() const; + + UE_DEPRECATED(5.5, "Deprecated by UE 5.5 in favor of GetMotionControllerState and GetHandTrackingState") bool GetMotionControllerData(FXRMotionControllerData& OutMotionControllerData); +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + bool GetMotionControllerState( + EXRSpaceType XRSpaceType, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + bool GetHandTrackingState( + EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + public: const TArray& GetHandBoneNames() const; const FName& GetHandBoneName(int32 Joint) const; diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h index 3ad5ee5c..36beb88c 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h @@ -164,5 +164,15 @@ protected: FActorComponentTickFunction* ThisTickFunction); public: + UE_DEPRECATED(5.5, "Deprecated by UE 5.5 in favor of GetMotionControllerState and GetHandTrackingState") bool GetMotionControllerData(FXRMotionControllerData& OutMotionControllerData); + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + bool GetMotionControllerState( + EXRSpaceType XRSpaceType, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + bool GetHandTrackingState( + EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ }; \ No newline at end of file diff --git a/Source/SenseGloveDebug/Private/SGDebug/SGDebugVirtualHand.cpp b/Source/SenseGloveDebug/Private/SGDebug/SGDebugVirtualHand.cpp index 7205d962..86349552 100644 --- a/Source/SenseGloveDebug/Private/SGDebug/SGDebugVirtualHand.cpp +++ b/Source/SenseGloveDebug/Private/SGDebug/SGDebugVirtualHand.cpp @@ -72,4 +72,36 @@ void FSGDebugVirtualHand::Draw(const UWorld* World, FSGDebugGizmo::Draw(World, HandKeyPosition, HandKeyRotation, Settings.DebugGizmoHandSettings); } } -} \ No newline at end of file +} + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 +void FSGDebugVirtualHand::Draw(const UWorld* World, + const FXRHandTrackingState& HandTrackingState, + const FSGVirtualHandDebuggingSettings& Settings) +{ + ensureAlwaysMsgf(Settings.DrawingMode != ESGDebugVirtualHandDrawingMode::None, + TEXT("Invalid virtual hand drawing mode!")); + + ensureAlwaysMsgf(HandTrackingState.bValid, + TEXT("Invalid hand tracking state!")); + + ensureAlwaysMsgf(HandTrackingState.HandKeyLocations.Num() == HandTrackingState.HandKeyRotations.Num(), + TEXT("Mismatched hand tracking state locations and rotations!")); + + for (TArray::SizeType HandKeyIndex = 0; + HandKeyIndex < HandTrackingState.HandKeyLocations.Num(); ++HandKeyIndex) + { + const FVector& HandKeyLocation{HandTrackingState.HandKeyLocations[HandKeyIndex]}; + const FQuat& HandKeyRotation{HandTrackingState.HandKeyRotations[HandKeyIndex]}; + + if (Settings.DrawingMode == ESGDebugVirtualHandDrawingMode::CubicJoints) + { + FSGDebugCube::Draw(World, HandKeyLocation, HandKeyRotation, Settings.DebugCubicHandSettings); + } + else if (Settings.DrawingMode == ESGDebugVirtualHandDrawingMode::GizmoJoints) + { + FSGDebugGizmo::Draw(World, HandKeyLocation, HandKeyRotation, Settings.DebugGizmoHandSettings); + } + } +} +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ \ No newline at end of file diff --git a/Source/SenseGloveDebug/Public/SGDebug/SGDebugVirtualHand.h b/Source/SenseGloveDebug/Public/SGDebug/SGDebugVirtualHand.h index 56abdfad..cb8c8509 100644 --- a/Source/SenseGloveDebug/Public/SGDebug/SGDebugVirtualHand.h +++ b/Source/SenseGloveDebug/Public/SGDebug/SGDebugVirtualHand.h @@ -47,11 +47,19 @@ class UWorld; class SENSEGLOVEDEBUG_API FSGDebugVirtualHand final { public: + UE_DEPRECATED(5.5, "Deprecated in favor of the variant that accepts FXRHandTrackingState") static void Draw( const UWorld* World, const FXRMotionControllerData& MotionControllerData, const FSGVirtualHandDebuggingSettings& Settings); +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + static void Draw( + const UWorld* World, + const FXRHandTrackingState& HandTrackingState, + const FSGVirtualHandDebuggingSettings& Settings); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + public: FSGDebugVirtualHand() = delete; virtual ~FSGDebugVirtualHand() = delete; diff --git a/Source/SenseGloveDebugKismet/Private/SGDebugKismet/SGDebugVirtualHandKismetLibrary.cpp b/Source/SenseGloveDebugKismet/Private/SGDebugKismet/SGDebugVirtualHandKismetLibrary.cpp index 2f2cb344..8545d148 100644 --- a/Source/SenseGloveDebugKismet/Private/SGDebugKismet/SGDebugVirtualHandKismetLibrary.cpp +++ b/Source/SenseGloveDebugKismet/Private/SGDebugKismet/SGDebugVirtualHandKismetLibrary.cpp @@ -37,11 +37,24 @@ #include "SGDebug/SGDebugVirtualHand.h" -void USGDebugVirtualHandKismetLibrary::Draw( +void USGDebugVirtualHandKismetLibrary::Draw_FXRMotionControllerData( UObject* WorldContextObject, - const FXRMotionControllerData& MotionControllerData, const FSGVirtualHandDebuggingSettings& Settings) + const FXRMotionControllerData& MotionControllerData, + const FSGVirtualHandDebuggingSettings& Settings) { ensureAlwaysMsgf(WorldContextObject, TEXT("Invalid world context object!")); + PRAGMA_DISABLE_DEPRECATION_WARNINGS FSGDebugVirtualHand::Draw(WorldContextObject->GetWorld(), MotionControllerData, Settings); + PRAGMA_ENABLE_DEPRECATION_WARNINGS +} + +void USGDebugVirtualHandKismetLibrary::Draw_FXRHandTrackingState( + UObject* WorldContextObject, + const FXRHandTrackingState& HandTrackingState, + const FSGVirtualHandDebuggingSettings& Settings) +{ + ensureAlwaysMsgf(WorldContextObject, TEXT("Invalid world context object!")); + + FSGDebugVirtualHand::Draw(WorldContextObject->GetWorld(), HandTrackingState, Settings); } \ No newline at end of file diff --git a/Source/SenseGloveDebugKismet/Public/SGDebugKismet/SGDebugVirtualHandKismetLibrary.h b/Source/SenseGloveDebugKismet/Public/SGDebugKismet/SGDebugVirtualHandKismetLibrary.h index 03da3f45..ede3c809 100644 --- a/Source/SenseGloveDebugKismet/Public/SGDebugKismet/SGDebugVirtualHandKismetLibrary.h +++ b/Source/SenseGloveDebugKismet/Public/SGDebugKismet/SGDebugVirtualHandKismetLibrary.h @@ -35,8 +35,10 @@ #pragma once +#include "UObject/ObjectMacros.h" +#include "HeadMountedDisplayTypes.h" + #include "SGKismet/SGBlueprintFunctionLibrary.h" -#include "SGDebug/SGDebugVirtualHand.h" #include "SGDebugVirtualHandKismetLibrary.generated.h" @@ -48,10 +50,17 @@ class SENSEGLOVEDEBUGKISMET_API USGDebugVirtualHandKismetLibrary final : public GENERATED_BODY() public: - UFUNCTION(BlueprintCallable, Category="SenseGlove | Debug | Virtual Hand", + UFUNCTION(BlueprintCallable, DisplayName="Draw", Category="SenseGlove | Debug | Virtual Hand", meta=(WorldContext="WorldContextObject")) - static void Draw( + static void Draw_FXRMotionControllerData( UObject* WorldContextObject, const FXRMotionControllerData& MotionControllerData, const FSGVirtualHandDebuggingSettings& Settings); + + UFUNCTION(BlueprintCallable, DisplayName="Draw", Category="SenseGlove | Debug | Virtual Hand", + meta=(WorldContext="WorldContextObject")) + static void Draw_FXRHandTrackingState( + UObject* WorldContextObject, + const FXRHandTrackingState& HandTrackingState, + const FSGVirtualHandDebuggingSettings& Settings); }; \ No newline at end of file diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandComponentKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandComponentKismetLibrary.cpp index 2ffceb9b..49c99877 100644 --- a/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandComponentKismetLibrary.cpp +++ b/Source/SenseGloveKismet/Private/SGKismet/SGVirtualHandComponentKismetLibrary.cpp @@ -113,7 +113,23 @@ USGHapticGlove* UVirtualHandComponentKismetLibrary::GetConnectedGlove( bool UVirtualHandComponentKismetLibrary::GetMotionControllerData( USGVirtualHandComponent* VirtualHandComponent, FXRMotionControllerData& OutMotionControllerData) { + PRAGMA_DISABLE_DEPRECATION_WARNINGS return VirtualHandComponent->GetMotionControllerData(OutMotionControllerData); + PRAGMA_ENABLE_DEPRECATION_WARNINGS +} + +bool UVirtualHandComponentKismetLibrary::GetMotionControllerState(USGVirtualHandComponent* VirtualHandComponent, + const EXRSpaceType XRSpaceType, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + return VirtualHandComponent->GetMotionControllerState( + XRSpaceType, XRControllerPoseType, OutMotionControllerState); +} + +bool UVirtualHandComponentKismetLibrary::GetHandTrackingState(USGVirtualHandComponent* VirtualHandComponent, + const EXRSpaceType XRSpaceType, FXRHandTrackingState& OutHandTrackingState) +{ + return VirtualHandComponent->GetHandTrackingState(XRSpaceType, OutHandTrackingState); } const TArray& UVirtualHandComponentKismetLibrary::GetHandBoneNames( diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGWristTrackerComponentKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGWristTrackerComponentKismetLibrary.cpp index 0e83ee7e..33339adc 100644 --- a/Source/SenseGloveKismet/Private/SGKismet/SGWristTrackerComponentKismetLibrary.cpp +++ b/Source/SenseGloveKismet/Private/SGKismet/SGWristTrackerComponentKismetLibrary.cpp @@ -93,5 +93,24 @@ const FRotator& UWristTrackerComponentKismetLibrary::GetWristRotation( bool UWristTrackerComponentKismetLibrary::GetMotionControllerData( USGWristTrackerComponent* WristTrackerComponent, FXRMotionControllerData& OutMotionControllerData) { + PRAGMA_DISABLE_DEPRECATION_WARNINGS return WristTrackerComponent->GetMotionControllerData(OutMotionControllerData); + PRAGMA_ENABLE_DEPRECATION_WARNINGS +} + +bool UWristTrackerComponentKismetLibrary::GetMotionControllerState( + USGWristTrackerComponent* WristTrackerComponent, + const EXRSpaceType XRSpaceType, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + return WristTrackerComponent->GetMotionControllerState( + XRSpaceType, XRControllerPoseType, OutMotionControllerState); +} + +bool UWristTrackerComponentKismetLibrary::GetHandTrackingState( + USGWristTrackerComponent* WristTrackerComponent, + const EXRSpaceType XRSpaceType, FXRHandTrackingState& OutHandTrackingState) +{ + return WristTrackerComponent->GetHandTrackingState( + XRSpaceType, OutHandTrackingState); } \ No newline at end of file diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandComponentKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandComponentKismetLibrary.h index 6906c194..f83d7068 100644 --- a/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandComponentKismetLibrary.h +++ b/Source/SenseGloveKismet/Public/SGKismet/SGVirtualHandComponentKismetLibrary.h @@ -106,6 +106,16 @@ public: static bool GetMotionControllerData(UPARAM(ref) USGVirtualHandComponent* VirtualHandComponent, FXRMotionControllerData& OutMotionControllerData); + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Component") + static bool GetMotionControllerState(UPARAM(ref) USGVirtualHandComponent* VirtualHandComponent, + EXRSpaceType XRSpaceType, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Virtual Hand Component") + static bool GetHandTrackingState(UPARAM(ref) USGVirtualHandComponent* VirtualHandComponent, + EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState); + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Virtual Hand Component") static const TArray& GetHandBoneNames(const USGVirtualHandComponent* VirtualHandComponent); diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGWristTrackerComponentKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGWristTrackerComponentKismetLibrary.h index 91b3e827..e2540b7c 100644 --- a/Source/SenseGloveKismet/Public/SGKismet/SGWristTrackerComponentKismetLibrary.h +++ b/Source/SenseGloveKismet/Public/SGKismet/SGWristTrackerComponentKismetLibrary.h @@ -86,4 +86,14 @@ public: UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Wrist Tracker Component") static bool GetMotionControllerData(UPARAM(ref) USGWristTrackerComponent* WristTrackerComponent, FXRMotionControllerData& OutMotionControllerData); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Wrist Tracker Component") + static bool GetMotionControllerState(UPARAM(ref) USGWristTrackerComponent* WristTrackerComponent, + EXRSpaceType XRSpaceType, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Wrist Tracker Component") + static bool GetHandTrackingState(UPARAM(ref) USGWristTrackerComponent* WristTrackerComponent, + EXRSpaceType XRSpaceType, + FXRHandTrackingState& OutHandTrackingState); }; \ No newline at end of file diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp index 037f57b1..a9ad0aaf 100644 --- a/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRHandState.cpp @@ -43,7 +43,8 @@ FSGXRHandState::FSGXRHandState() Locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}, KeypointTransforms{}, Radii{}, - bReceivedJointPoses(false) + bHasReceivedJointPoses(false), + bTracked(false) { Velocities.jointCount = XR_HAND_JOINT_COUNT_EXT; Velocities.jointVelocities = JointVelocities; @@ -60,7 +61,7 @@ bool FSGXRHandState::GetTransform(const EHandKeypoint Keypoint, FTransform& OutT check(static_cast(Keypoint) < EHandKeypointCount); OutTransform = KeypointTransforms[static_cast(Keypoint)]; - return bReceivedJointPoses; + return bHasReceivedJointPoses; } const FTransform& FSGXRHandState::GetTransform(const EHandKeypoint Keypoint) const diff --git a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp index 1c73246e..7dec8e72 100644 --- a/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp +++ b/Source/SenseGloveTracking/Private/SGTracking/SGXRTracker.cpp @@ -998,7 +998,9 @@ FTransform FSGXRTracker::FImpl::GetMeshFingerStartBoneRefTransform( } bool FSGXRTracker::GetMotionControllerData( - UObject* WorldContextObject, const EControllerHand Hand, FXRMotionControllerData& OutMotionControllerData) + UObject* WorldContextObject, + const EControllerHand Hand, + FXRMotionControllerData& OutMotionControllerData) { OutMotionControllerData.DeviceName = NAME_None; OutMotionControllerData.ApplicationInstanceID = FApp::GetInstanceId(); @@ -1163,16 +1165,14 @@ bool FSGXRTracker::GetMotionControllerData( { OutMotionControllerData.DeviceVisualType = EXRVisualType::Hand; - /// TODO - // This is a just a temporary workaround to silence a UE 5.5 deprecation warning. - // Should be refactored with a proper implementation when migrating away from the deprecated - // `FXRMotionControllerData` in favor of newly introduced - // `FXRMotionControllerState` and `FXRHandTrackingState` structs. #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 bool bTracked = false; #endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + OutMotionControllerData.bValid = HandTracker->GetAllKeypointStates( - Hand, OutMotionControllerData.HandKeyPositions, OutMotionControllerData.HandKeyRotations, + Hand, + OutMotionControllerData.HandKeyPositions, + OutMotionControllerData.HandKeyRotations, OutMotionControllerData.HandKeyRadii #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 , bTracked @@ -1180,10 +1180,25 @@ bool FSGXRTracker::GetMotionControllerData( ); #if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 - if (!bTracked) + // Unused! + (void) bTracked; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + // TODO + // Begin backward compatibility with the deprecated function, remove this block along with the deprecated + // GetAllKeypointStates (though it should be time to remove this entire function as well)! + if (!OutMotionControllerData.bValid) { - return false; + PRAGMA_DISABLE_DEPRECATION_WARNINGS + OutMotionControllerData.bValid = HandTracker->GetAllKeypointStates( + Hand, + OutMotionControllerData.HandKeyPositions, + OutMotionControllerData.HandKeyRotations, + OutMotionControllerData.HandKeyRadii); + PRAGMA_ENABLE_DEPRECATION_WARNINGS } + // End backward compability. #endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ if (!ensureAlwaysMsgf( @@ -1204,13 +1219,293 @@ bool FSGXRTracker::GetMotionControllerData( OutMotionControllerData.TrackingStatus = ETrackingStatus::Tracked; } - /// TODO - // This is reportedly a WMR-specific convenience function for rapid prototyping. Not sure if it's useful for OpenXR. - OutMotionControllerData.bIsGrasped = false; - return OutMotionControllerData.bValid; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + +bool FSGXRTracker::GetMotionControllerState( + UObject* WorldContextObject, + const EXRSpaceType XRSpaceType, const EControllerHand Hand, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + auto ToMotionSourceName = []( + const EControllerHand Hand, const EXRControllerPoseType XRControllerPoseType) -> FLazyName + { + static FLazyName LeftAim = "LeftAim"; + static FLazyName LeftGrip = "LeftGrip"; + static FLazyName LeftPalm = "LeftPalm"; + static FLazyName RightAim = "RightAim"; + static FLazyName RightGrip = "RightGrip"; + static FLazyName RightPalm = "RightPalm"; + if (Hand == EControllerHand::Left) + { + switch (XRControllerPoseType) + { + case EXRControllerPoseType::Aim: + return LeftAim; + case EXRControllerPoseType::Grip: + return LeftGrip; + case EXRControllerPoseType::Palm: + return LeftPalm; + default: + check(false); + return LeftGrip; + } + } + else + { + switch (XRControllerPoseType) + { + case EXRControllerPoseType::Aim: + return RightAim; + case EXRControllerPoseType::Grip: + return RightGrip; + case EXRControllerPoseType::Palm: + return RightPalm; + default: + check(false); + return RightGrip; + } + } + }; + + OutMotionControllerState.DeviceName = NAME_None; + OutMotionControllerState.ApplicationInstanceID = FApp::GetInstanceId(); + OutMotionControllerState.TrackingStatus = ETrackingStatus::NotTracked; + OutMotionControllerState.Hand = Hand; + OutMotionControllerState.XRSpaceType = XRSpaceType; + OutMotionControllerState.bValid = false; + + if (!IsValid(WorldContextObject)) + { + return false; + } + + if (!IsValid(WorldContextObject)) + { + return false; + } + + IXRTrackingSystem* XRTrackingSystem{GEngine ? GEngine->XRSystem.Get() : nullptr}; + if (!XRTrackingSystem) + { + return false; + } + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 + IOpenXRHMD* OpenXRHMD{XRTrackingSystem->GetIOpenXRHMD()}; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 */ + FOpenXRHMD* OpenXRHMD{static_cast(GEngine->XRSystem.Get())}; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 */ + if (!OpenXRHMD) + { + return false; + } + + TArray Devices; + const int32 HandIndex = static_cast(Hand); + const bool bGotDevices = + XRTrackingSystem->EnumerateTrackedDevices(Devices, EXRTrackedDeviceType::Controller); + if (bGotDevices && Devices.IsValidIndex(HandIndex)) + { + const XrSession Session{OpenXRHMD->GetSession()}; + if (Session) + { + XrInteractionProfileState Profile = {XR_TYPE_INTERACTION_PROFILE_STATE}; + const XrPath TopLevelUserPath{OpenXRHMD->GetTrackedDevicePath(Devices[HandIndex])}; + const XrResult CurrentInteractionProfile{ + xrGetCurrentInteractionProfile(Session, TopLevelUserPath, &Profile) + }; + if (XR_SUCCEEDED(CurrentInteractionProfile) && Profile.interactionProfile != XR_NULL_PATH) + { + OutMotionControllerState.DeviceName = FOpenXRPath(Profile.interactionProfile); + } + } + } + + if ((Hand == EControllerHand::Left) || (Hand == EControllerHand::Right)) + { + static const FName MotionControllerName("OpenXR"); + TArray MotionControllers{ + IModularFeatures::Get().GetModularFeatureImplementations( + IMotionController::GetModularFeatureName()) + }; + IMotionController* MotionController{nullptr}; + for (IMotionController* MotionControllerIterator: MotionControllers) + { + if (!MotionControllerIterator) + { + continue; + } + + const FName DeviceTypeName{MotionControllerIterator->GetMotionControllerDeviceTypeName()}; + if (DeviceTypeName != MotionControllerName) + { + continue; + } + + MotionController = MotionControllerIterator; + break; + } + + if (MotionController) + { + { + // Handle the pose that is actually being requested. + + const FName MotionSource{ToMotionSourceName(Hand, XRControllerPoseType)}; + const FTransform TrackingToWorld{ + XRSpaceType == EXRSpaceType::UnrealWorldSpace + ? XRTrackingSystem->GetTrackingToWorldTransform() + : FTransform::Identity + }; + const float WorldToMeters = XRSpaceType == EXRSpaceType::UnrealWorldSpace + ? XRTrackingSystem->GetWorldToMetersScale() + : 100.0f; + + FVector Position{FVector::ZeroVector}; + FRotator Rotation{FRotator::ZeroRotator}; + + bool bGotMotionControllerOrientationAndPosition = MotionController->GetControllerOrientationAndPosition( + 0, MotionSource, Rotation, Position, WorldToMeters); + + if (bGotMotionControllerOrientationAndPosition) + { + OutMotionControllerState.ControllerLocation = TrackingToWorld.TransformPosition(Position); + OutMotionControllerState.ControllerRotation = TrackingToWorld.TransformRotation(FQuat{Rotation}); + } + + OutMotionControllerState.bValid |= bGotMotionControllerOrientationAndPosition; + OutMotionControllerState.TrackingStatus = + MotionController->GetControllerTrackingStatus(0, MotionSource); + } + + { + // We always provide the grip transform in Unreal space for XRVisualizationFunctionLibrary; + // The bValid and TrackingStatus above are also valid for this pose. + + const FName MotionSource = ToMotionSourceName(Hand, EXRControllerPoseType::Grip); + const FTransform TrackingToWorld{XRTrackingSystem->GetTrackingToWorldTransform()}; + const float WorldToMeters = XRTrackingSystem->GetWorldToMetersScale(); + + FVector Position = FVector::ZeroVector; + FRotator Rotation = FRotator::ZeroRotator; + + bool bGotMotionControllerOrientationAndPosition = MotionController->GetControllerOrientationAndPosition( + 0, MotionSource, Rotation, Position, WorldToMeters); + + if (bGotMotionControllerOrientationAndPosition) + { + OutMotionControllerState.GripUnrealSpaceLocation = TrackingToWorld.TransformPosition(Position); + OutMotionControllerState.GripUnrealSpaceRotation = + TrackingToWorld.TransformRotation(FQuat(Rotation)); + } + } + } + } + + return OutMotionControllerState.bValid; +} + +bool FSGXRTracker::GetHandTrackingState( + UObject* WorldContextObject, + const EXRSpaceType XRSpaceType, const EControllerHand Hand, + FXRHandTrackingState& OutHandTrackingState) +{ + OutHandTrackingState.ApplicationInstanceID = FApp::GetInstanceId(); + OutHandTrackingState.TrackingStatus = ETrackingStatus::NotTracked; + OutHandTrackingState.Hand = Hand; + OutHandTrackingState.XRSpaceType = XRSpaceType; + OutHandTrackingState.bValid = false; + + if (!IsValid(WorldContextObject)) + { + return false; + } + + IXRTrackingSystem* XRTrackingSystem{GEngine ? GEngine->XRSystem.Get() : nullptr}; + if (!XRTrackingSystem) + { + return false; + } + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 + IOpenXRHMD* OpenXRHMD{XRTrackingSystem->GetIOpenXRHMD()}; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 */ + FOpenXRHMD* OpenXRHMD{static_cast(GEngine->XRSystem.Get())}; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4 */ + if (!OpenXRHMD) + { + return false; + } + + FName HandTrackerName("OpenXRHandTracking"); + TArray HandTrackers = IModularFeatures::Get().GetModularFeatureImplementations( + IHandTracker::GetModularFeatureName()); + IHandTracker* HandTracker = nullptr; + for (auto Itr: HandTrackers) + { + if (Itr->GetHandTrackerDeviceTypeName() == HandTrackerName) + { + HandTracker = Itr; + break; + } + } + + if ((Hand == EControllerHand::Left) || (Hand == EControllerHand::Right)) + { + const bool bHandTrackingStateValid = HandTracker && HandTracker->IsHandTrackingStateValid(); + if (bHandTrackingStateValid) + { + bool bTracked = false; + OutHandTrackingState.bValid = HandTracker->GetAllKeypointStates( + Hand, + OutHandTrackingState.HandKeyLocations, + OutHandTrackingState.HandKeyRotations, + OutHandTrackingState.HandKeyRadii, + bTracked); + + /// TODO + // Begin backward compatibility with the deprecated function, remove this block along with the deprecated + // GetAllKeypointStates. + if (!OutHandTrackingState.bValid) + { + PRAGMA_DISABLE_DEPRECATION_WARNINGS + OutHandTrackingState.bValid = HandTracker->GetAllKeypointStates( + Hand, + OutHandTrackingState.HandKeyLocations, + OutHandTrackingState.HandKeyRotations, + OutHandTrackingState.HandKeyRadii); + PRAGMA_ENABLE_DEPRECATION_WARNINGS + bTracked = OutHandTrackingState.bValid; + // The inability to distinguish this is the reason for the deprecation. + } + // End backward compability. + + if (!ensureAlwaysMsgf( + !OutHandTrackingState.bValid + || (OutHandTrackingState.HandKeyLocations.Num() == EHandKeypointCount + && OutHandTrackingState.HandKeyRotations.Num() == EHandKeypointCount + && OutHandTrackingState.HandKeyRadii.Num() == EHandKeypointCount), + TEXT("Corrupted OutHandTrackingState!"))) + { + OutHandTrackingState.bValid = false; + return false; + } + + if (OutHandTrackingState.bValid) + { + OutHandTrackingState.TrackingStatus = bTracked ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked; + } + } + } + + return OutHandTrackingState.bValid; +} + +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ + FSGXRTracker::FSGXRTracker(const TSharedRef& InMessageHandler) : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) { @@ -1480,7 +1775,7 @@ ETrackingStatus FSGXRTracker::GetControllerTrackingStatus(const int32 Controller if (bFallbackToHandTrackingIfNoGloveDetected) { const FSGXRHandState& HandState{bRight ? GetRightHandState() : GetLeftHandState()}; - return HandState.bReceivedJointPoses ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked; + return HandState.bTracked ? ETrackingStatus::Tracked : ETrackingStatus::NotTracked; } return ETrackingStatus::NotTracked; @@ -1606,9 +1901,17 @@ bool FSGXRTracker::GetKeypointState( return bGotTransform; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 +bool FSGXRTracker::GetAllKeypointStates( + const EControllerHand Hand, + TArray& OutPositions, TArray& OutRotations, TArray& OutRadii, + bool& OutIsTracked) const +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ bool FSGXRTracker::GetAllKeypointStates( const EControllerHand Hand, TArray& OutPositions, TArray& OutRotations, TArray& OutRadii) const +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5*/ + { if (!IsHandTrackingStateValid()) { @@ -1629,11 +1932,17 @@ bool FSGXRTracker::GetAllKeypointStates( Hand == EControllerHand::Left ? GetLeftHandState() : GetRightHandState() }; - if (!HandState.bReceivedJointPoses) + if (!HandState.bHasReceivedJointPoses) { return false; } +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + OutIsTracked = HandState.bTracked; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5*/ + + // If we ever received poses we return the last data we received, even if not currently tracking to avoid poping + // back to (0.0f, 0.0f, 0.0f) and the like. OutPositions.Empty(EHandKeypointCount); OutRotations.Empty(EHandKeypointCount); OutRadii.Empty(EHandKeypointCount); @@ -1765,8 +2074,9 @@ bool FSGXRTracker::FImpl::UpdateXRHandTrackingData( XR_ENSURE(XRLocateHandJointsEXT(OutHandState.HandTracker, &LocateInfo, &OutHandState.Locations)); - OutHandState.bReceivedJointPoses = OutHandState.Locations.isActive == XR_TRUE; - if (!OutHandState.bReceivedJointPoses) + OutHandState.bTracked = OutHandState.Locations.isActive == XR_TRUE; + OutHandState.bHasReceivedJointPoses |= HandStates->bTracked; + if (!OutHandState.bTracked) { return false; } @@ -1893,21 +2203,16 @@ bool FSGXRTracker::FImpl::GetControllerTransform( if (!bTracked && bFallbackToHandTrackingIfNoGloveDetected) { const FSGXRHandState& HandState{bRight ? Owner->GetRightHandState() : Owner->GetLeftHandState()}; - if (!HandState.bReceivedJointPoses) + if (!HandState.bHasReceivedJointPoses) { return false; } const EHandKeypoint KeyPoint = KeyPointInfoPtr->Key; - const FTransform& ControllerTransform{ - bRight - ? GetRightHandState().GetTransform(KeyPoint) - : GetLeftHandState().GetTransform(KeyPoint) - }; - + const FTransform& ControllerTransform{HandStates->GetTransform(KeyPoint)}; OutTransform = ControllerTransform; bOutMotionSourceHandTracking = true; - bTracked = true; + bTracked = HandState.bTracked; } return bTracked; @@ -2404,7 +2709,8 @@ bool FSGXRTracker::FImpl::UpdateSGHandState(const bool bRight, FSGXRHandState& O return false; } - OutHandState.bReceivedJointPoses = true; + OutHandState.bHasReceivedJointPoses = true; + OutHandState.bTracked = true; const TArray> JointLocations{HandPose->GetJointPositions()}; const TArray> JointRotations{HandPose->GetJointRotationsQ()}; diff --git a/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h b/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h index 66378d7a..3326d368 100644 --- a/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h +++ b/Source/SenseGloveTracking/Public/SGTracking/SGXRHandState.h @@ -51,12 +51,16 @@ public: XrHandJointVelocitiesEXT Velocities; XrHandJointLocationsEXT Locations; - // Transforms are cached in Unreal Tracking Space + // Transforms are cached in Unreal Tracking Space. FTransform KeypointTransforms[EHandKeypointCount]; float Radii[EHandKeypointCount]; - bool bReceivedJointPoses; + // true if this hand has ever been tracked, and therefore its poses are valid. + bool bHasReceivedJointPoses; + + // true if tracked right now. + bool bTracked; public: FSGXRHandState(); diff --git a/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h b/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h index 2cf9319c..c35a5f06 100644 --- a/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h +++ b/Source/SenseGloveTracking/Public/SGTracking/SGXRTracker.h @@ -55,8 +55,22 @@ class SENSEGLOVETRACKING_API FSGXRTracker final : public IOpenXRExtensionPlugin, public IHandTracker { public: - static bool GetMotionControllerData(UObject* WorldContextObject, EControllerHand Hand, - FXRMotionControllerData& OutMotionControllerData); + UE_DEPRECATED(5.5, "Deprecated by UE 5.5 in favor of GetMotionControllerState and GetHandTrackingState") + static bool GetMotionControllerData( + UObject* WorldContextObject, + EControllerHand Hand, + FXRMotionControllerData& OutMotionControllerData); + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + static bool GetMotionControllerState( + UObject* WorldContextObject, + EXRSpaceType XRSpaceType, EControllerHand Hand, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + static bool GetHandTrackingState( + UObject* WorldContextObject, + EXRSpaceType XRSpaceType, EControllerHand Hand, + FXRHandTrackingState& OutHandTrackingState); +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ private: struct FImpl; @@ -158,7 +172,15 @@ public: virtual bool GetKeypointState( EControllerHand Hand, EHandKeypoint Keypoint, FTransform& OutTransform, float& OutRadius) const override; + +#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 + virtual bool GetAllKeypointStates( + EControllerHand Hand, + TArray& OutPositions, TArray& OutRotations, TArray& OutRadii, + bool& OutIsTracked) const override; +#else /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ virtual bool GetAllKeypointStates( EControllerHand Hand, TArray& OutPositions, TArray& OutRotations, TArray& OutRadii) const override; +#endif /* ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 5 */ }; \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGXRTrackerKismetLibrary.cpp b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGXRTrackerKismetLibrary.cpp index 138db9fe..d98a71c2 100644 --- a/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGXRTrackerKismetLibrary.cpp +++ b/Source/SenseGloveTrackingKismet/Private/SGTrackingKismet/SGXRTrackerKismetLibrary.cpp @@ -42,5 +42,24 @@ bool USGXRTrackerKismetLibrary::GetMotionControllerData( UObject* WorldContextObject, const EControllerHand Hand, FXRMotionControllerData& OutMotionControllerData) { + PRAGMA_DISABLE_DEPRECATION_WARNINGS return FSGXRTracker::GetMotionControllerData(WorldContextObject, Hand, OutMotionControllerData); + PRAGMA_ENABLE_DEPRECATION_WARNINGS +} + +bool USGXRTrackerKismetLibrary::GetMotionControllerState( + UObject* WorldContextObject, + const EXRSpaceType XRSpaceType, const EControllerHand Hand, const EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState) +{ + return FSGXRTracker::GetMotionControllerState( + WorldContextObject, XRSpaceType, Hand, XRControllerPoseType, OutMotionControllerState); +} + +bool USGXRTrackerKismetLibrary::GetHandTrackingState( + UObject* WorldContextObject, + const EXRSpaceType XRSpaceType, const EControllerHand Hand, + FXRHandTrackingState& OutHandTrackingState) +{ + return FSGXRTracker::GetHandTrackingState(WorldContextObject, XRSpaceType, Hand, OutHandTrackingState); } \ No newline at end of file diff --git a/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGXRTrackerKismetLibrary.h b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGXRTrackerKismetLibrary.h index 32a0b0e8..b6d5b065 100644 --- a/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGXRTrackerKismetLibrary.h +++ b/Source/SenseGloveTrackingKismet/Public/SGTrackingKismet/SGXRTrackerKismetLibrary.h @@ -53,6 +53,22 @@ class SENSEGLOVETRACKINGKISMET_API USGXRTrackerKismetLibrary final : public USGB public: UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | XR Tracker", meta=(WorldContext="WorldContextObject")) - static bool GetMotionControllerData(UObject* WorldContextObject, EControllerHand Hand, - FXRMotionControllerData& OutMotionControllerData); -}; + static bool GetMotionControllerData( + UObject* WorldContextObject, + EControllerHand Hand, + FXRMotionControllerData& OutMotionControllerData); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | XR Tracker", + meta=(WorldContext="WorldContextObject")) + static bool GetMotionControllerState( + UObject* WorldContextObject, + EXRSpaceType XRSpaceType, EControllerHand Hand, EXRControllerPoseType XRControllerPoseType, + FXRMotionControllerState& OutMotionControllerState); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Tracking | XR Tracker", + meta=(WorldContext="WorldContextObject")) + static bool GetHandTrackingState( + UObject* WorldContextObject, + EXRSpaceType XRSpaceType, EControllerHand Hand, + FXRHandTrackingState& OutHandTrackingState); +}; \ No newline at end of file