diff --git a/Handbook/src/appendix/changelog.md b/Handbook/src/appendix/changelog.md index b7d3b899..d9909d3a 100644 --- a/Handbook/src/appendix/changelog.md +++ b/Handbook/src/appendix/changelog.md @@ -22,6 +22,16 @@ This minor release focuses on delivering performance improvements, made possible - Added third-party module `SGLoguruThirdPartyLibs`. - Added third-party module `SGWjwwoodSerialThirdPartyLibs` to replace `SGSerialThirdPartyLibs` while retaining `SGSerialThirdPartyLibs` for a different purpose. See the relevant comment in the Changed section below. - Added UPROPERTY `SGPawn::HandVelocitySamplerFramerate` to control the hand velocity sampler's timer interval. This allows getting rid of `SGPawn::Tick()` for improved performance and reliability, which also improves chopiness on low frame-rates. +- Added UPROPERTY `USGTouchComponent::VibrotactileAmplitude`. +- Added UPROPERTY `USGTouchComponent::VibrotactileFrequency`. +- Added method `USGTouchComponent::GetVibrotactileAmplitude()`. +- Added method `USGTouchComponent::SetVibrotactileAmplitude()`. +- Added method `USGTouchComponent::GetVibrotactileFrequency()`. +- Added method `USGTouchComponent::SetVibrotactileFrequency()`. +- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::GetVibrotactileAmplitude()`. +- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::SetVibrotactileAmplitude()`. +- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::GetVibrotactileFrequency()`. +- Added Blueprint-accessible method `USGTouchComponentKismetLibrary::SetVibrotactileFrequency()`. ### Fixed @@ -42,6 +52,8 @@ This minor release focuses on delivering performance improvements, made possible - `USGWristTrackerComponent::GetHandTrackingState()` signature has changed. - `USGVirtualHandComponentKismetLibrary::GetMotionControllerData()` signature has changed. - `USGVirtualHandComponentKismetLibrary::GetHandTrackingState()` signature has changed. +- `SGPawn` and `SGTouchComponent` no longer use the legacy `QueueVibroLevels` method for applying vibrotactile feedback. Instead it's been replaced with `SendCustomWaveform`. +- Changed `USGTouchComponent::VibrotactileDuration` UPROPERTY's maximum value to `1.0f`. Previously it was uncapped, and now any value beyond `1.0f` seconds is clamped. ### Removed @@ -55,11 +67,17 @@ This minor release focuses on delivering performance improvements, made possible - `USGWristTrackerComponent::GetMotionControllerState()` has been removed. - `USGVirtualHandComponentKismetLibrary::GetMotionControllerState()` has been removed. - `USGWristTrackerComponentKismetLibrary::GetMotionControllerState()` has been removed. +- `USGTouchComponent::VibrotactileLevel` UPROPERTY has been removed. +- `USGTouchComponent::GetVibrotactileLevel()` method has been removed. +- `USGTouchComponent::SetVibrotactileLevel()` method has been removed. +- `USGTouchComponentKismetLibrary::GetVibrotactileLevel()` method has been removed and is no longer available to Blueprint. +- `USGTouchComponentKismetLibrary::SetVibrotactileLevel()` method has been removed and is no longer available to Blueprint. ### Documentation - Added [{fmt} Formatting Library License](../license/fmt-formatting-library.html) section. - Added [Loguru Logging Library License](../license/loguru-loggin-library.html) section. +- Updated [Setting up the Touch System](../getting-started/setup-touch-system/index.html) section to reflect the recent touch system changes. ## [2.6.3] - 2025-06-27 diff --git a/Handbook/src/getting-started/setup-touch-system/sgtouchcomponent-settings.png b/Handbook/src/getting-started/setup-touch-system/sgtouchcomponent-settings.png index dc318168..876cda89 100755 --- a/Handbook/src/getting-started/setup-touch-system/sgtouchcomponent-settings.png +++ b/Handbook/src/getting-started/setup-touch-system/sgtouchcomponent-settings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:205e843cc7e39a61c146fbd230de2ec9237f4e662ce8e7e58deddc79ab87bdfc -size 296193 +oid sha256:680e9a66551122b0b1eeea674eb340553e278fb880ed36c61b555eae027b4d1d +size 310506 diff --git a/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp b/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp index 51372e14..4617f2bd 100644 --- a/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp +++ b/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp @@ -103,8 +103,9 @@ USGTouchComponent::USGTouchComponent(const FObjectInitializer& ObjectInitializer { ForceFeedbackLevel = 1.0f; - VibrotactileDuration = 0.25f; - VibrotactileLevel = 0.0f; + VibrotactileAmplitude = 1.0f; + VibrotactileDuration = 1.0f; + VibrotactileFrequency= 500.0f; } USGTouchComponent::FImpl::FImpl(USGTouchComponent* InOwner) diff --git a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp index 9f638aa9..62152f48 100644 --- a/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp +++ b/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp @@ -36,8 +36,6 @@ #include "SenseGlove/GameFramework/SGPlayerController.h" #include "Runtime/Launch/Resources/Version.h" -#include "Engine/TimerHandle.h" -#include "TimerManager.h" #include "SenseGlove/Components/SGTouchComponent.h" #include "SenseGlove/Components/SGVirtualHandComponent.h" @@ -45,6 +43,7 @@ #include "SenseGlove/Haptics/SGGrabState.h" #include "SenseGlove/Haptics/SGTouchState.h" #include "SGBuildHacks/SGPlatform.h" +#include "SGCore/SGCustomWaveform.h" #include "SGCore/SGHapticGlove.h" struct ASGPlayerController::FImpl @@ -55,11 +54,6 @@ struct ASGPlayerController::FImpl static float GetForceFeedbackLevel(const AActor* Actor); - static float GetVibrotactileDuration(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching, - const AActor* ActorMiddleTouching, const AActor* ActorRingTouching, - const AActor* ActorPinkyTouching); - static float GetVibrotactileLevel(const AActor* Actor); - static TArray GetForceFeedbackLevels( const AActor* ActorThumbTouching, const AActor* ActorIndexTouching, @@ -67,19 +61,10 @@ struct ASGPlayerController::FImpl const AActor* ActorRingTouching, const AActor* ActorPinkyTouching); - static TArray GetVibrotactileLevels( - const AActor* ActorThumbTouching, - const AActor* ActorIndexTouching, - const AActor* ActorMiddleTouching, - const AActor* ActorRingTouching, - const AActor* ActorPinkyTouching); - /************************ * Member variables ************************/ - FTimerHandle TouchVibrotactileStopTimer; - /************************ * Owner object ************************/ @@ -104,6 +89,8 @@ struct ASGPlayerController::FImpl * Methods ************************/ + USGCustomWaveform* GetCustomWaveform(const AActor* Actor); + void UpdateHapticsFeedback(const FSGTouchState& TouchState); }; @@ -121,74 +108,6 @@ float ASGPlayerController::FImpl::GetForceFeedbackLevel(const AActor* Actor) return 0.0f; } -float ASGPlayerController::FImpl::GetVibrotactileDuration(const AActor* ActorThumbTouching, - const AActor* ActorIndexTouching, - const AActor* ActorMiddleTouching, - const AActor* ActorRingTouching, - const AActor* ActorPinkyTouching) -{ - if (IsValid(ActorThumbTouching)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorThumbTouching); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileDuration(); - } - } - - if (IsValid(ActorIndexTouching)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorIndexTouching); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileDuration(); - } - } - - if (IsValid(ActorMiddleTouching)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorMiddleTouching); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileDuration(); - } - } - - if (IsValid(ActorRingTouching)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorRingTouching); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileDuration(); - } - } - - if (IsValid(ActorPinkyTouching)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorPinkyTouching); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileDuration(); - } - } - - return 0.0f; -} - -float ASGPlayerController::FImpl::GetVibrotactileLevel(const AActor* Actor) -{ - if (IsValid(Actor)) - { - const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor); - if (IsValid(TouchComponent)) - { - return TouchComponent->GetVibrotactileLevel(); - } - } - - return 0.0f; -} - TArray ASGPlayerController::FImpl::GetForceFeedbackLevels( const AActor* ActorThumbTouching, const AActor* ActorIndexTouching, @@ -213,30 +132,6 @@ TArray ASGPlayerController::FImpl::GetForceFeedbackLevels( return ForceFeedbackLevels; } -TArray ASGPlayerController::FImpl::GetVibrotactileLevels( - const AActor* ActorThumbTouching, - const AActor* ActorIndexTouching, - const AActor* ActorMiddleTouching, - const AActor* ActorRingTouching, - const AActor* ActorPinkyTouching) -{ - const float ThumbVibrotactileLevel = GetVibrotactileLevel(ActorThumbTouching); - const float IndexVibrotactileLevel = GetVibrotactileLevel(ActorIndexTouching); - const float MiddleVibrotactileLevel = GetVibrotactileLevel(ActorMiddleTouching); - const float RingVibrotactileLevel = GetVibrotactileLevel(ActorRingTouching); - const float PinkyVibrotactileLevel = GetVibrotactileLevel(ActorPinkyTouching); - - const TArray ForceFeedbackLevels{ - ThumbVibrotactileLevel, - IndexVibrotactileLevel, - MiddleVibrotactileLevel, - RingVibrotactileLevel, - PinkyVibrotactileLevel, - }; - - return ForceFeedbackLevels; -} - ASGPlayerController::ASGPlayerController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer), Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter)) @@ -330,6 +225,29 @@ ASGPlayerController::FImpl::FImpl(ASGPlayerController* InOwner) ASGPlayerController::FImpl::~FImpl() = default; +USGCustomWaveform* ASGPlayerController::FImpl::GetCustomWaveform(const AActor* Actor) +{ + float Amplitude = 0.0f; + float Duration = 0.0f; + float Frequency = 0.0f; + + if (IsValid(Actor)) + { + const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor); + if (IsValid(TouchComponent)) + { + Amplitude = TouchComponent->GetVibrotactileAmplitude(); + Duration = TouchComponent->GetVibrotactileDuration(); + Frequency = TouchComponent->GetVibrotactileFrequency(); + } + } + + USGCustomWaveform* CustomWaveform{ + USGCustomWaveform::NewCustomWaveform(Owner, Amplitude, Duration, Frequency) + }; + return CustomWaveform; +} + void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& TouchState) { static const TArray VibrotactileOffCommand{0.0f, 0.0f, 0.0, 0.0, 0.0f}; @@ -359,44 +277,43 @@ void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& Touc }; Glove->QueueForceFeedbackLevels(MoveTemp(ForceFeedbackLevels)); - // Queue the Vibrotactile command... - TArray VibrotactileLevels{ - GetVibrotactileLevels( - TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching, - TouchState.ActorRingTouching, TouchState.ActorPinkyTouching) - }; - Glove->QueueVibroLevels(MoveTemp(VibrotactileLevels)); - // Send the haptics commands! Glove->SendHaptics(); - // Stop the Vibrotactile command after the timeout if any haptic feedback is ongoing. - const float VibrotactileDuration = FImpl::GetVibrotactileDuration( - TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching, - TouchState.ActorRingTouching, TouchState.ActorPinkyTouching); - - Owner->GetWorldTimerManager().ClearTimer(TouchVibrotactileStopTimer); - - FTimerDelegate TouchVibrotactileStopHandler; - TouchVibrotactileStopHandler.BindLambda([= SG_CAPTURE_THIS]()-> void + // Send Vibrotactile to the thumb finger if it's touching an actor... + if (IsValid(TouchState.ActorThumbTouching)) { - if (!IsValid(Glove)) - { - return; - } + USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorThumbTouching)); + Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::ThumbTip); + } - const bool bGloveConnected = Glove->IsConnected(); - if (!bGloveConnected) - { - return; - } + // Send Vibrotactile to the index finger if it's touching an actor... + if (IsValid(TouchState.ActorIndexTouching)) + { + USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorIndexTouching)); + Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::IndexTip); + } - Glove->QueueVibroLevels(VibrotactileOffCommand); - Glove->SendHaptics(); - }); + // Send Vibrotactile to the middle finger if it's touching an actor... + if (IsValid(TouchState.ActorMiddleTouching)) + { + USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorMiddleTouching)); + Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::MiddleTip); + } - Owner->GetWorldTimerManager().SetTimer( - TouchVibrotactileStopTimer, TouchVibrotactileStopHandler, VibrotactileDuration, false, -1.0f); + // Send Vibrotactile to the ring finger if it's touching an actor... + if (IsValid(TouchState.ActorRingTouching)) + { + USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorRingTouching)); + Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::RingTip); + } + + // Send Vibrotactile to the pinky finger if it's touching an actor... + if (IsValid(TouchState.ActorPinkyTouching)) + { + USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorPinkyTouching)); + Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::PinkyTip); + } } void ASGPlayerController::FImplDeleter::operator()(const FImpl* P) const diff --git a/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h b/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h index 814da107..3f665600 100644 --- a/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h +++ b/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h @@ -57,12 +57,17 @@ private: meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0")) float ForceFeedbackLevel; - UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false", ClampMin = "0.0", UIMin = "0.0")) + UPROPERTY(EditAnywhere, Category="SenseGlove", + meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0")) + float VibrotactileAmplitude; + + UPROPERTY(EditAnywhere, Category="SenseGlove", + meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0")) float VibrotactileDuration; UPROPERTY(EditAnywhere, Category="SenseGlove", - meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0")) - float VibrotactileLevel; + meta=(AllowPrivateAccess="false", ClampMin = "10.0", ClampMax = "500.0", UIMin = "10.0", UIMax = "500.0")) + float VibrotactileFrequency; private: struct FImpl; @@ -86,6 +91,16 @@ public: ForceFeedbackLevel = Level; } + FORCEINLINE float GetVibrotactileAmplitude() const + { + return VibrotactileAmplitude; + } + + FORCEINLINE void SetVibrotactileAmplitude(const float Amplitude) + { + VibrotactileAmplitude = Amplitude; + } + FORCEINLINE float GetVibrotactileDuration() const { return VibrotactileDuration; @@ -95,13 +110,14 @@ public: { VibrotactileDuration = Duration; } - FORCEINLINE float GetVibrotactileLevel() const + + FORCEINLINE float GetVibrotactileFrequency() const { - return VibrotactileLevel; + return VibrotactileFrequency; } - FORCEINLINE void SetVibrotactileLevel(const float Level) + FORCEINLINE void SetVibrotactileFrequency(const float Frequency) { - VibrotactileLevel = Level; + VibrotactileFrequency = Frequency; } }; \ No newline at end of file diff --git a/Source/SenseGloveKismet/Private/SGKismet/SGTouchComponentKismetLibrary.cpp b/Source/SenseGloveKismet/Private/SGKismet/SGTouchComponentKismetLibrary.cpp index 9661a1fe..3d1dab98 100644 --- a/Source/SenseGloveKismet/Private/SGKismet/SGTouchComponentKismetLibrary.cpp +++ b/Source/SenseGloveKismet/Private/SGKismet/SGTouchComponentKismetLibrary.cpp @@ -60,14 +60,14 @@ void UTouchComponentKismetLibrary::SetForceFeedbackLevel(USGTouchComponent* Touc TouchComponent->SetForceFeedbackLevel(Level); } -float UTouchComponentKismetLibrary::GetVibrotactileLevel(const USGTouchComponent* TouchComponent) +float UTouchComponentKismetLibrary::GetVibrotactileAmplitude(const USGTouchComponent* TouchComponent) { - return TouchComponent->GetVibrotactileLevel(); + return TouchComponent->GetVibrotactileAmplitude(); } -void UTouchComponentKismetLibrary::SetVibrotactileLevel(USGTouchComponent* TouchComponent, const float Level) +void UTouchComponentKismetLibrary::SetVibrotactileAmplitude(USGTouchComponent* TouchComponent, const float Amplitude) { - TouchComponent->SetVibrotactileLevel(Level); + TouchComponent->SetVibrotactileAmplitude(Amplitude); } float UTouchComponentKismetLibrary::GetVibrotactileDuration(const USGTouchComponent* TouchComponent) @@ -78,4 +78,15 @@ float UTouchComponentKismetLibrary::GetVibrotactileDuration(const USGTouchCompon void UTouchComponentKismetLibrary::SetVibrotactileDuration(USGTouchComponent* TouchComponent, const float Duration) { TouchComponent->SetVibrotactileDuration(Duration); -} \ No newline at end of file +} + +float UTouchComponentKismetLibrary::GetVibrotactileFrequency(const USGTouchComponent* TouchComponent) +{ + return TouchComponent->GetVibrotactileFrequency(); +} + +void UTouchComponentKismetLibrary::SetVibrotactileFrequency(USGTouchComponent* TouchComponent, const float Frequency) +{ + TouchComponent->SetVibrotactileFrequency(Frequency); +} + diff --git a/Source/SenseGloveKismet/Public/SGKismet/SGTouchComponentKismetLibrary.h b/Source/SenseGloveKismet/Public/SGKismet/SGTouchComponentKismetLibrary.h index 15b0e1aa..6bb8bfb0 100644 --- a/Source/SenseGloveKismet/Public/SGKismet/SGTouchComponentKismetLibrary.h +++ b/Source/SenseGloveKismet/Public/SGKismet/SGTouchComponentKismetLibrary.h @@ -61,14 +61,20 @@ public: static void SetForceFeedbackLevel(UPARAM(ref) USGTouchComponent* TouchComponent, float Level); UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component") - static float GetVibrotactileLevel(const USGTouchComponent* TouchComponent); + static float GetVibrotactileAmplitude(const USGTouchComponent* TouchComponent); UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component") - static void SetVibrotactileLevel(UPARAM(ref) USGTouchComponent* TouchComponent, float Level); + static void SetVibrotactileAmplitude(UPARAM(ref) USGTouchComponent* TouchComponent, float Amplitude); UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component") static float GetVibrotactileDuration(const USGTouchComponent* TouchComponent); UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component") static void SetVibrotactileDuration(UPARAM(ref) USGTouchComponent* TouchComponent, float Duration); + + UFUNCTION(BlueprintPure, Category="SenseGlove | Components | Touch Component") + static float GetVibrotactileFrequency(const USGTouchComponent* TouchComponent); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Components | Touch Component") + static void SetVibrotactileFrequency(UPARAM(ref) USGTouchComponent* TouchComponent, float Frequency); }; \ No newline at end of file