diff --git a/Handbook/src/advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/README.md b/Handbook/src/advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/README.md index 978328ca..b2821e04 100644 --- a/Handbook/src/advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/README.md +++ b/Handbook/src/advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/README.md @@ -1,9 +1,187 @@ # SGHapticsComponent +`SGHapticsComponent`, introduced in the SenseGlove Unreal Engine Plugin `v2.8.0`, provides a highly convenient, high-level interface for sending various types of haptic feedback to a SenseGlove device directly from Unreal Engine. + +Prior to this release, integrating haptic feedback into a custom hand interaction system was possible in several ways: + +- SenseGlove low-level C++ API: + - Via the [SGHandLayer API](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGloveCore/Public/SGCore/SGHandLayer.h). + - Via the [SGHpaticGlove API](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGloveCore/Public/SGCore/SGHapticGlove.h). +- SenseGlove Blueprint API: + - Via the [SGHandLayer API](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGHandLayerKismetLibrary.h) which provides a higher-level abstraction compared to the `SGHapticGlove` API. + - Via the [SGHapticGlove API](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGHapticGloveKismetLibrary.h), + which offers a lower-level interface than the `SGHandLayer` API and requires some boilerplate code to safely obtain an instance of the desired glove (see [Safe and Reliable Glove Access in Blueprint](../../safe-glove-access-blueprint/)). +- Additionally, there is the [`SGTouchComponent`](../../../getting-started/setup-touch-system/), which provides simplified and limited functionality. On its own, it cannot trigger haptics. It is designed to work in conjunction with the stock `SGPlayerController` shipped with the SenseGlove Unreal Engine plugin. + +While all of the above approaches remain fully supported, whether in C++ or Blueprint, `SGHapticsComponent` eliminates some of the caveats associated with them, while still giving you full control in a significantly more convenient and streamlined manner. + +> [!IMPORTANT] +> For more detailed information on +> [Nova 2 Glove Vibration Tips & Tricks](https://senseglove.gitlab.io/SenseGloveDocs/nova2-vibration.html), +> please visit the in-depth guide available on +> [SenseGlove Docs](https://senseglove.gitlab.io/SenseGloveDocs/). +> +> We strongly recommend reviewing that comprehensive upstream haptics documentation, +> as this guide focuses solely on applying haptic feedback from Unreal Engine. +> +> A solid understanding of the SenseGlove haptics API and its hardware capabilities +> will help you follow and apply this guide effectively, while also enabling you +> to troubleshoot haptics-based Unreal Engine projects with confidence. + +## Adding the Component to Your Actors + +Adding `SGHapticsComponent` is straightforward. In the `Components` panel, click the `Add` button and locate it under the `SenseGlove` section: + ![SGHapticsComponent - Adding component](sghapticscomponent-add-component.png "SGHapticsComponent - Adding Component") +## Blueprint Properties + +`SGHapticsComponent` exposes the following properties through the `Details` panel in Unreal’s Blueprint Editor: + ![SGHapticsComponent - Exposed Blueprint properties accessible via the Details panel](sghapticscomponent-details-panel-uproperties.png "SGHapticsComponent - Exposed Blueprint properties accessible via the Details panel") +- `Right`: If enabled, the component controls haptics feedback for the **right** hand. If disabled, it controls haptics for the **left** hand instead. +- `AutoStopAllHaptics`: If enabled, automatically calls the `StopHaptics()` function when: 1) The component is uninitialized 2) the `EndPlay` event occurs 3) or, the **handedness** changes. This ensures that vibrations won't continue after the simulation ends, or when the active glove it controls, is switched mid-simulation. + +## C++ and Blueprint Functions + +`SGHapticsComponent` provdies the following C++ methods: + +```cpp +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: + /** + * 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 OutWaveform + * @param Location + */ + bool SendCustomWaveform(USGCustomWaveform* OutWaveform, 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 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); + + /** + * Returns true if the chosen glove supports active contact feedback on the Wrist. + */ + bool SupportsWristSqueeze() const; + + /** + * Queue a command to set the amount of squeeze level (a.k.a. squeeze-feedback) to the desired level + * (0 = no squeeze, 1 = full squeeze) on the wrist, and optionally send it right away. + * + * @param SqueezeLevel01 + * @param bSendImmediate + */ + bool QueueWristSqueeze(float SqueezeLevel01, bool bSendImmediate); +``` + +The same set of functions are also exposed to Blueprint: + ![SGHapticsComponent - Blueprint functions](sghapticscomponent-functions1.png "SGHapticsComponent - Blueprint functions") ![SGHapticsComponent - Blueprint functions](sghapticscomponent-functions2.png "SGHapticsComponent - Blueprint functions")