From 016e6d3cd061b6f8298f06c1edd03e54e78179e4 Mon Sep 17 00:00:00 2001 From: Mamadou Babaei Date: Mon, 9 Feb 2026 17:12:23 +0100 Subject: [PATCH] implement a USGHapticsComponent to allow sending a variety of haptic feedbacks to the gloves without touching senseglove's low-level api --- Handbook/src/appendix/changelog.md | 1 + .../Components/SGHapticsComponent.cpp | 252 ++++++++++++++++++ .../Components/SGHapticsComponent.h | 192 +++++++++++++ .../SGHapticsComponentKismetLibrary.cpp | 135 ++++++++++ .../SGHapticsComponentKismetLibrary.h | 170 ++++++++++++ 5 files changed, 750 insertions(+) create mode 100644 Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp create mode 100644 Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h create mode 100644 Source/SenseGloveKismet/Private/SGKismet/SGHapticsComponentKismetLibrary.cpp create mode 100644 Source/SenseGloveKismet/Public/SGKismet/SGHapticsComponentKismetLibrary.h diff --git a/Handbook/src/appendix/changelog.md b/Handbook/src/appendix/changelog.md index 6ab563ff..ef54b8aa 100644 --- a/Handbook/src/appendix/changelog.md +++ b/Handbook/src/appendix/changelog.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a `USGHapticsComponent` to allow sending a variety of haptic feedbacks to the gloves without touching SenseGlove's low-level API. - Added a `USGHandTrackerComponent` to allow retrieval or visualization of `FXRHandTrackingState` without relying on low-level SenseGlove API or UE's generic `GetHandTrackingState()` functionality. This is useful when developing a custom hand-interaction system using SenseGlove/UE OpenXR API or interfacing with third-party OpenXR-compatible plugins such as [VR Expansion Plugin (VRE)](https://vreue4.com/). Using this component removes the need to calculate the wrist offsets manually, or an extra call to `USGHapticGlove::GetWristLocation()`, in comparison to when the `FXRHandTrackingState` is retrieved via UE's `GetHandTrackingState()`. - A new `FSGDebugVirtualHand::Draw()` overload has been added to allow visualizing `FSGDebugGizmoSettings` directly. This is used internally by the new `USGHandTrackerComponent` to visualize its `FXRHandTrackingState` if `bVisualize` is enabled. diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp new file mode 100644 index 00000000..5f25a68d --- /dev/null +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp @@ -0,0 +1,252 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2026 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/SGHapticsComponent.h" + +#include "SGCore/SGCustomWaveform.h" +#include "SGCore/SGHapticGlove.h" +#include "SGTracking/SGGloveTracker.h" + +struct USGHapticsComponent::FImpl +{ + /************************ + * Static methods + ************************/ + + /************************ + * Owner object + ************************/ + + USGHapticsComponent* Owner; + + /************************ + * Constructor / Destructor + ************************/ + + explicit FImpl(USGHapticsComponent* InOwner); + ~FImpl(); + + /************************ + * Default copy constructor & copy assignment operator + ************************/ + + FImpl(const FImpl& Rhs) = default; + FImpl& operator=(const FImpl& Rhs) = default; + + /************************ + * Methods + ************************/ + + FORCEINLINE USGHapticGlove* GetGlove() const + { + const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(Owner->GetOuter())}; + USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(Owner->IsRight()) : nullptr}; + return Glove; + } + + void AutoStopAllHaptics() const; +}; + +USGHapticsComponent::USGHapticsComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer), + Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) +{ + bRight = true; +} + +void USGHapticsComponent::SetRight(const bool bInRight) +{ + bRight = bInRight; + + if (bAutoStopAllHaptics) + { + Pimpl->AutoStopAllHaptics(); + } +} + +void USGHapticsComponent::UninitializeComponent() +{ + Super::UninitializeComponent(); + + if (bAutoStopAllHaptics) + { + Pimpl->AutoStopAllHaptics(); + } +} + +void USGHapticsComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + Super::EndPlay(EndPlayReason); + + if (bAutoStopAllHaptics) + { + Pimpl->AutoStopAllHaptics(); + } +} + +void USGHapticsComponent::StopHaptics() +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + Glove->StopHaptics(); + } +} + +void USGHapticsComponent::StopVibrations() +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + Glove->StopVibrations(); + } +} + +bool USGHapticsComponent::SendHaptics() +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->SendHaptics(); + } + + return false; +} + +bool USGHapticsComponent::SupportsCustomWaveform(const ESGHapticLocation AtLocation) const +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->SupportsCustomWaveform(AtLocation); + } + + return false; +} + +bool USGHapticsComponent::SendCustomWaveform( + const float Amplitude, const float Duration, const ESGHapticLocation Location) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + USGCustomWaveform* Waveform{ + USGCustomWaveform::NewCustomWaveform(GetOuter(), Amplitude, Duration) + }; + return Glove->SendCustomWaveform(MoveTemp(Waveform), Location); + } + + return false; +} + +bool USGHapticsComponent::SendCustomWaveform( + const float Amplitude, const float Duration, const float Frequency, const ESGHapticLocation Location) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + USGCustomWaveform* Waveform{ + USGCustomWaveform::NewCustomWaveform(GetOuter(), Amplitude, Duration, Frequency) + }; + return Glove->SendCustomWaveform(MoveTemp(Waveform), Location); + } + + return false; +} + +bool USGHapticsComponent::QueueForceFeedbackLevels(const TArray& Levels01) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->QueueForceFeedbackLevels(Levels01); + } + + return false; +} + +bool USGHapticsComponent::QueueForceFeedbackLevel(const int32 Finger, const float Level01) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->QueueForceFeedbackLevel(Finger, Level01); + } + + return false; +} + +bool USGHapticsComponent::QueueVibroLevels(const TArray& Levels01) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->QueueVibroLevels(Levels01); + } + + return false; +} + +bool USGHapticsComponent::QueueVibroLevel(const ESGHapticLocation Location, const float Level01) +{ + USGHapticGlove* Glove{Pimpl->GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected()) + { + return Glove->QueueVibroLevel(Location, Level01); + } + + return false; +} + +USGHapticsComponent::FImpl::FImpl(USGHapticsComponent* InOwner) + : Owner(InOwner) +{ +} + +USGHapticsComponent::FImpl::~FImpl() = default; + +void USGHapticsComponent::FImpl::AutoStopAllHaptics() const +{ + USGHapticGlove* Glove{GetGlove()}; + if (IsValid(Glove) && Glove->IsConnected() && Owner->bAutoStopAllHaptics) + { + Glove->StopHaptics(); + } +} + +void USGHapticsComponent::FImplDeleter::operator()(const FImpl* P) const +{ + delete P; +} \ No newline at end of file diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h new file mode 100644 index 00000000..06ad350b --- /dev/null +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h @@ -0,0 +1,192 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2026 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 "Components/SceneComponent.h" +#include "Templates/UniquePtr.h" + +#include "SGTypes/SGCoreTypes.h" + +#include "SGHapticsComponent.generated.h" + +class AActor; + +class USGCustomWaveform; + +UCLASS(Blueprintable, BlueprintType, ClassGroup=(SenseGlove), meta=(BlueprintSpawnableComponent)) +class SENSEGLOVE_API USGHapticsComponent : public USceneComponent +{ + GENERATED_UCLASS_BODY() + +private: + /** + * Determines whether this component operates on the right or the left hand. + */ + UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false")) + bool bRight; + + /** + * If enabled, forces all haptics to stop automatically on the UninitializeComponent or EndPlay events, or when the + * handedness changes. This is useful for situations where the simulation has ended, but ongoing haptic feedback + * might remain active on the glove indefinitely. By default, this setting is enabled. + */ + UPROPERTY(Config, EditDefaultsOnly, Category="Haptics") + bool bAutoStopAllHaptics; + +private: + struct FImpl; + + struct FImplDeleter + { + void operator()(const FImpl* P) const; + }; + + TUniquePtr Pimpl; + FImplDeleter PimplDeleter; + +public: + FORCEINLINE bool IsLeft() const + { + return !IsRight(); + } + + FORCEINLINE bool IsRight() const + { + return bRight; + } + + void SetRight(const bool bInRight); + + FORCEINLINE bool AutoStopsAllHaptics() const + { + return bAutoStopAllHaptics; + } + + void SetAutoStopAllHaptics(const bool bInAutoStopAllHaptics) + { + bAutoStopAllHaptics = bInAutoStopAllHaptics; + } + +public: + virtual void UninitializeComponent() override; + + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + +public: + /** + * Stops all Haptic effects if any are currently playing. Useful at the end of simulations or when restarting the + * level. + */ + void StopHaptics(); + + /** + * Stops only vibrations. + */ + void StopVibrations(); + + /** + * Take all active commands in the device queue, compile them into one and send them to the device. + * + * @return Returns true if the message was successfully sent to SenseCom. + */ + bool SendHaptics(); + + /** + * Returns true if the haptic glove supports vibration feedback at the specified location. + * + * @param AtLocation + */ + bool SupportsCustomWaveform(ESGHapticLocation AtLocation) const; + + /** + * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support + * custom waveforms. + * + * @param Amplitude + * @param Duration + * @param Location + */ + bool SendCustomWaveform(float Amplitude, float Duration, ESGHapticLocation Location); + + /** + * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support + * custom waveforms. + * + * @param Amplitude + * @param Duration + * @param Frequency + * @param Location + */ + bool SendCustomWaveform(float Amplitude, float Duration, float Frequency, ESGHapticLocation Location); + + /** + * Queue a list of force-feedback levels, between 0.0f and 1.0f. Your list should be sorted from thumb to pinky. + * + * @param Levels01 Array containing the Force-Feedback levels, from 0.0f (no FFB) to 1.0f. A value < 0.0f will be + * ignored. + * + * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0. + */ + bool QueueForceFeedbackLevels(const TArray& Levels01); + + /** + * Set the Force-Feedback value of a particular finger to a specific level + * + * @param Level01 Value will be clamped between [0...1], where 0.0f means no Force-Feedback, and 1.0 means full + * force-feedback. + * @param Finger The finger to which to send the command. + */ + bool QueueForceFeedbackLevel(int32 Finger, float Level01); + + /** + * Queue a list of vibration levels, between 0.0 and 1.0. Your list should be sorted from thumb to pinky. + * + * @param Levels01 Array containing the vibration levels, from 0.0 (no vibration) to 1.0. A value < 0.0f will be + * ignored. + * + * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0. + */ + bool QueueVibroLevels(const TArray& Levels01); + + /** + * Queue a command to set the (continuous) vibration level at a specific location to a set amplitude. + * + * @param Location + * @param Level01 Value will be clamped between [0...1], where 0.0f means no vibration, and 1.0 means full + * vibration. + */ + bool QueueVibroLevel(ESGHapticLocation Location, float Level01); +}; \ No newline at end of file diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGHapticsComponentKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGHapticsComponentKismetLibrary.cpp new file mode 100644 index 00000000..dd0501c0 --- /dev/null +++ b/Source/SenseGloveKismet/Private/SGKismet/SGHapticsComponentKismetLibrary.cpp @@ -0,0 +1,135 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2026 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 "SGKismet/SGHapticsComponentKismetLibrary.h" + +#include "SenseGlove/Components/SGHapticsComponent.h" + +bool UHapticsComponentKismetLibrary::IsLeft( + const USGHapticsComponent* HapticsComponent) +{ + return HapticsComponent->IsLeft(); +} + +bool UHapticsComponentKismetLibrary::IsRight( + const USGHapticsComponent* HapticsComponent) +{ + return HapticsComponent->IsRight(); +} + +void UHapticsComponentKismetLibrary::SetRight( + USGHapticsComponent* HapticsComponent, const bool bInRight) +{ + HapticsComponent->SetRight(bInRight); +} + +bool UHapticsComponentKismetLibrary::AutoStopsAllHaptics( + const USGHapticsComponent* HapticsComponent) +{ + return HapticsComponent->AutoStopsAllHaptics(); +} + +void UHapticsComponentKismetLibrary::SetAutoStopAllHaptics( + USGHapticsComponent* HapticsComponent, const bool bInAutoStopAllHaptics) +{ + HapticsComponent->SetAutoStopAllHaptics(bInAutoStopAllHaptics); +} + +void UHapticsComponentKismetLibrary::StopHaptics( + USGHapticsComponent* HapticsComponent) +{ + HapticsComponent->StopHaptics(); +} + +void UHapticsComponentKismetLibrary::StopVibrations( + USGHapticsComponent* HapticsComponent) +{ + HapticsComponent->StopVibrations(); +} + +bool UHapticsComponentKismetLibrary::SendHaptics( + USGHapticsComponent* HapticsComponent) +{ + return HapticsComponent->SendHaptics(); +} + +bool UHapticsComponentKismetLibrary::SupportsCustomWaveform( + const USGHapticsComponent* HapticsComponent, + const ESGHapticLocation AtLocation) +{ + return HapticsComponent->SupportsCustomWaveform(AtLocation); +} + +bool UHapticsComponentKismetLibrary::SendCustomWaveform_Amplitude_Duration_Location( + USGHapticsComponent* HapticsComponent, + const float Amplitude, const float Duration, const ESGHapticLocation Location) +{ + return HapticsComponent->SendCustomWaveform(Amplitude, Duration, Location); +} + +bool UHapticsComponentKismetLibrary::SendCustomWaveform_Amplitude_Duration_Frequency_Location( + USGHapticsComponent* HapticsComponent, + const float Amplitude, const float Duration, const float Frequency, const ESGHapticLocation Location) +{ + return HapticsComponent->SendCustomWaveform(Amplitude, Duration, Frequency, Location); +} + +bool UHapticsComponentKismetLibrary::QueueForceFeedbackLevels( + USGHapticsComponent* HapticsComponent, + const TArray& Levels01) +{ + return HapticsComponent->QueueForceFeedbackLevels(Levels01); +} + +bool UHapticsComponentKismetLibrary::QueueForceFeedbackLevel( + USGHapticsComponent* HapticsComponent, + const int32 Finger, const float Level01) +{ + return HapticsComponent->QueueForceFeedbackLevel(Finger, Level01); +} + +bool UHapticsComponentKismetLibrary::QueueVibroLevels( + USGHapticsComponent* HapticsComponent, + const TArray& Levels01) +{ + return HapticsComponent->QueueVibroLevels(Levels01); +} + +bool UHapticsComponentKismetLibrary::QueueVibroLevel( + USGHapticsComponent* HapticsComponent, + const ESGHapticLocation Location, const float Level01) +{ + return HapticsComponent->QueueVibroLevel(Location, Level01); +} \ No newline at end of file diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGHapticsComponentKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGHapticsComponentKismetLibrary.h new file mode 100644 index 00000000..9f03deeb --- /dev/null +++ b/Source/SenseGloveKismet/Public/SGKismet/SGHapticsComponentKismetLibrary.h @@ -0,0 +1,170 @@ +/** + * @file + * + * @author Mamadou Babaei + * + * @section LICENSE + * + * (The MIT License) + * + * Copyright (c) 2020 - 2026 SenseGlove + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @section DESCRIPTION + * + * + */ + + +#pragma once + +#include "SGKismet/SGBlueprintFunctionLibrary.h" + +#include "SGHapticsComponentKismetLibrary.generated.h" + +class AActor; +class USGHapticsComponent; + +UCLASS(meta=(ScriptName = "SenseGloveHapticsComponentKismetLibrary")) +class SENSEGLOVEKISMET_API UHapticsComponentKismetLibrary final : public USGBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Haptics Component") + static bool IsLeft(const USGHapticsComponent* HapticsComponent); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Haptics Component") + static bool IsRight(const USGHapticsComponent* HapticsComponent); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static void SetRight(UPARAM(ref) USGHapticsComponent* HapticsComponent, bool bInRight); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Haptics Component") + static bool AutoStopsAllHaptics(const USGHapticsComponent* HapticsComponent); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static void SetAutoStopAllHaptics(UPARAM(ref) USGHapticsComponent* HapticsComponent, bool bInAutoStopAllHaptics); + + /** + * Stops all Haptic effects if any are currently playing. Useful at the end of simulations or when restarting the + * level. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static void StopHaptics(UPARAM(ref) USGHapticsComponent* HapticsComponent); + + /** + * Stops only vibrations. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static void StopVibrations(UPARAM(ref) USGHapticsComponent* HapticsComponent); + + /** + * Take all active commands in the device queue, compile them into one and send them to the device. + * + * @return Returns true if the message was successfully sent to SenseCom. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static bool SendHaptics(UPARAM(ref) USGHapticsComponent* HapticsComponent); + + /** + * Returns true if the haptic glove supports vibration feedback at the specified location. + * + * @param AtLocation + */ + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Haptics Component") + static bool SupportsCustomWaveform(const USGHapticsComponent* HapticsComponent, + ESGHapticLocation AtLocation); + + /** + * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support + * custom waveforms. + * + * @param Amplitude + * @param Duration + * @param Location + */ + UFUNCTION(BlueprintCallable, DisplayName="Send Custom Waveform", + Category="SenseGlove | Components | Haptics Component") + static bool SendCustomWaveform_Amplitude_Duration_Location( + UPARAM(ref) USGHapticsComponent* HapticsComponent, + float Amplitude, float Duration, ESGHapticLocation Location); + + /** + * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support + * custom waveforms. + * + * @param Amplitude + * @param Duration + * @param Frequency + * @param Location + */ + UFUNCTION(BlueprintCallable, DisplayName="Send Custom Waveform", + Category="SenseGlove | Components | Haptics Component") + static bool SendCustomWaveform_Amplitude_Duration_Frequency_Location( + UPARAM(ref) USGHapticsComponent* HapticsComponent, + float Amplitude, float Duration, float Frequency, ESGHapticLocation Location); + + /** + * Queue a list of force-feedback levels, between 0.0f and 1.0f. Your list should be sorted from thumb to pinky. + * + * @param Levels01 Array containing the Force-Feedback levels, from 0.0f (no FFB) to 1.0f. A value < 0.0f will be + * ignored. + * + * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static bool QueueForceFeedbackLevels(UPARAM(ref) USGHapticsComponent* HapticsComponent, + const TArray& Levels01); + + /** + * Set the Force-Feedback value of a particular finger to a specific level + * + * @param Level01 Value will be clamped between [0...1], where 0.0f means no Force-Feedback, and 1.0 means full + * force-feedback. + * @param Finger The finger to which to send the command. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static bool QueueForceFeedbackLevel(UPARAM(ref) USGHapticsComponent* HapticsComponent, + int32 Finger, float Level01); + + /** + * Queue a list of vibration levels, between 0.0 and 1.0. Your list should be sorted from thumb to pinky. + * + * @param Levels01 Array containing the vibration levels, from 0.0 (no vibration) to 1.0. A value < 0.0f will be + * ignored. + * + * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static bool QueueVibroLevels(UPARAM(ref) USGHapticsComponent* HapticsComponent, + const TArray& Levels01); + + /** + * Queue a command to set the (continuous) vibration level at a specific location to a set amplitude. + * + * @param Location + * @param Level01 Value will be clamped between [0...1], where 0.0f means no vibration, and 1.0 means full + * vibration. + */ + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Haptics Component") + static bool QueueVibroLevel(UPARAM(ref) USGHapticsComponent* HapticsComponent, + ESGHapticLocation Location, float Level01); +}; \ No newline at end of file