Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5f7bb47e2
|
||
|
|
1f4c0abe8e
|
||
|
|
e009d88b65
|
||
|
|
043ba3d071
|
||
|
|
bf90a9d9fb
|
||
|
|
9160b22e31
|
||
|
|
c78dd0fd86
|
||
|
|
4e362468cc
|
||
|
|
7352089aab
|
||
|
|
9427506851
|
||
|
|
f493e5b0fe
|
||
|
|
5ffbaa9a66
|
||
|
|
0f04fea5c2
|
||
|
|
350d6bac44
|
||
|
|
7c3c0bd6a9
|
||
|
|
dbd0dd23d2
|
||
|
|
848dc5545a
|
||
|
|
aeccb102eb
|
||
|
|
57718c9594
|
||
|
|
bbf98fc1ac
|
||
|
|
462db47578
|
||
|
|
a1e23f430a
|
||
|
|
77b89fc0a3
|
||
|
|
c98f16a28a
|
||
|
|
2724a8dc7f
|
||
|
|
d380fea8da
|
||
|
|
9208adf7c7
|
||
|
|
585f035dfb
|
||
|
|
6fbe4df85c
|
||
|
|
f2b0aa5b63
|
||
|
|
4c6861b850
|
||
|
|
c6dbe1df13
|
||
|
|
abc3a145b5
|
||
|
|
9c4cc7ab13
|
||
|
|
f0beaf1f96
|
||
|
|
18f1d22321
|
||
|
|
9f04f41644
|
||
|
|
b40babc9ee
|
||
|
|
88d3dfa444
|
||
|
|
f74b31a65a
|
||
|
|
f4bfba18f7
|
||
|
|
e6156463a3
|
||
|
|
12ea962e96
|
||
|
|
a353f30530
|
||
|
|
e7a07863ee
|
||
|
|
8a9df3b312
|
||
|
|
1fb420ebb9
|
||
|
|
bd8872a35c
|
||
|
|
814146ae59
|
||
|
|
293e8777b0
|
||
|
|
eda920b079
|
||
|
|
6c67cbe342
|
||
|
|
5f3db734c3
|
||
|
|
5d5aad6e68
|
||
|
|
e86addd974
|
||
|
|
6ce080b536
|
||
|
|
e956b323bb
|
||
|
|
7aad244d65
|
||
|
|
d6a4cea0d1
|
||
|
|
71199a3d52
|
||
|
|
7263429815
|
||
|
|
19482948a8
|
||
|
|
b9bdbc39b5
|
||
|
|
3f0a5de6d3
|
||
|
|
e2d128468e
|
||
|
|
526eeee4b0
|
||
|
|
1efaa0681d
|
||
|
|
b6f0c0602c
|
||
|
|
1b4e9a16db
|
||
|
|
864509d0a5
|
||
|
|
e0fc5d17e9
|
||
|
|
d2e0645e0b
|
||
|
|
16f2ef6f4f
|
||
|
|
326b8c1ecc
|
||
|
|
169147ecd1
|
||
|
|
19613a21ec
|
||
|
|
f9578bcdfe
|
||
|
|
962e038754
|
||
|
|
0239b0e441
|
||
|
|
061686987a
|
||
|
|
40b84fc3af
|
||
|
|
31dd1d89b1
|
||
|
|
eb996f782f
|
||
|
|
f8a6500ca7
|
||
|
|
65198dbf19
|
||
|
|
633be51e62
|
||
|
|
cae50c0f6e
|
||
|
|
c4952f900d
|
||
|
|
cbd7baa7bb
|
||
|
|
c75685875b
|
||
|
|
f316d93690
|
||
|
|
53ac3cb081
|
||
|
|
5a04f3c070
|
||
|
|
ea62a54dd9
|
||
|
|
28a02334bf
|
||
|
|
3462d5b4da
|
||
|
|
43bb540de9
|
||
|
|
bb7e777ae6
|
||
|
|
b4e526996c
|
||
|
|
114e7dfcf9
|
||
|
|
944a7da804
|
||
|
|
a2cee0762d
|
||
|
|
0d7c37a5fb
|
||
|
|
fd495d00e8
|
@@ -50,3 +50,9 @@ Handbook/dist/**/* filter=lfs diff=lfs merge=lfs -text
|
||||
# LFS: sgum-packager Linux and Windows binaries
|
||||
Packager/sgum-packager filter=lfs diff=lfs merge=lfs -text
|
||||
Packager/sgum-packager.exe filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# LFS: sgfakeshm-connect-client abd sgfakeshm-core-client Linux and Windows binaries
|
||||
Tuning/sgfakeshm-connect-client filter=lfs diff=lfs merge=lfs -text
|
||||
Tuning/sgfakeshm-connect-client.exe filter=lfs diff=lfs merge=lfs -text
|
||||
Tuning/sgfakeshm-core-client filter=lfs diff=lfs merge=lfs -text
|
||||
Tuning/sgfakeshm-core-client.exe filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# (The MIT License)
|
||||
#
|
||||
# Copyright (c) 2020 - 2025 SenseGlove
|
||||
# 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
|
||||
|
||||
+13
-6
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# (The MIT License)
|
||||
#
|
||||
# Copyright (c) 2020 - 2025 SenseGlove
|
||||
# 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
|
||||
@@ -119,6 +119,12 @@ SG_PLUGIN_LICENSE_THIRD_PARTY_FILE := $(SG_PLUGIN_DIR)/LICENSE_THIRD_PARTY.m
|
||||
SG_PLUGIN_PLANNED_FEATURES_COMPLETION_STATUS_FILE := $(SG_PLUGIN_DIR)/PLANNED_FEATURES_COMPLETION_STATUS.md
|
||||
SG_PLUGIN_PLATFORM_SUPPORT_MATRIX_FILE := $(SG_PLUGIN_DIR)/PLATFORM_SUPPORT_MATRIX.md
|
||||
|
||||
################################################################################
|
||||
# Rust/Cargo Settings
|
||||
################################################################################
|
||||
|
||||
RUST_TOOLCHAIN_VERSION := 1.96.1
|
||||
|
||||
################################################################################
|
||||
# Version and Build Information
|
||||
################################################################################
|
||||
@@ -365,17 +371,18 @@ clean:
|
||||
.PHONY: install-build-deps
|
||||
install-build-deps:
|
||||
@echo "Installing build dependencies if they have not already been installed..."
|
||||
@cargo install mdbook --version "0.4.49" --locked
|
||||
@cargo install xq --vers "^0.4" --locked
|
||||
@cargo install mdbook-alerts --vers "^0.7" --locked
|
||||
@cargo install mdbook-pdf --vers "^0.1" --locked
|
||||
@rustup install $(RUST_TOOLCHAIN_VERSION)
|
||||
@rustup override set $(RUST_TOOLCHAIN_VERSION)
|
||||
@cargo install mdbook --version "0.5.4" --locked
|
||||
@cargo install xq --vers "^0.5" --locked
|
||||
@cargo install mdbook-pdf --vers "0.1.13" --locked
|
||||
ifeq ($(SG_BUILD_PLATFORM),$(SG_BUILD_PLATFORM_LINUX))
|
||||
@echo "Installing mdbook-pdf-outline..."
|
||||
@python -m venv $(VIRTUAL_ENV) \
|
||||
&& . $(SG_LINUX_PYTHON_VENV_BIN_DIR)/activate \
|
||||
&& pip install mdbook-pdf-outline 2>/dev/null
|
||||
endif # ($(SG_BUILD_PLATFORM),$(SG_BUILD_PLATFORM_LINUX))
|
||||
@cargo install --git https://github.com/Michael-F-Bryan/mdbook-epub --rev cac03b7f4b151f106f7f05b13da4c33fc098dd2c --locked
|
||||
@cargo install mdbook-epub --vers "0.5.4" --locked
|
||||
|
||||
################################################################################
|
||||
# Make Targets - mdBook
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# (The MIT License)
|
||||
#
|
||||
# Copyright (c) 2020 - 2025 SenseGlove
|
||||
# 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
|
||||
@@ -33,8 +33,8 @@ fi
|
||||
|
||||
readonly JSON_INPUT=$(cat)
|
||||
|
||||
readonly CONTEXT=$(echo "${JSON_INPUT}" | xq '.[0]')
|
||||
readonly BOOK=$(echo "${JSON_INPUT}" | xq '.[1]')
|
||||
readonly CONTEXT=$(printf '%s' "${JSON_INPUT}" | xq '.[0]')
|
||||
readonly BOOK=$(printf '%s' "${JSON_INPUT}" | xq '.[1]')
|
||||
|
||||
escape_string() {
|
||||
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
|
||||
@@ -63,8 +63,8 @@ SG_PLUGIN_VERSION_PATCH_ESCAPED=$(escape_string "${SG_PLUGIN_VERSION_PATCH}")
|
||||
SG_BUILD_HOST_ESCAPED=$(escape_string "${SG_BUILD_HOST}")
|
||||
SG_BUILD_TIME_ESCAPED=$(escape_string "${SG_BUILD_TIME}")
|
||||
|
||||
PARSED_BOOK=$(echo "${BOOK}" | xq "
|
||||
.sections[] |= (
|
||||
PARSED_BOOK=$(printf '%s' "${BOOK}" | xq "
|
||||
.items[] |= (
|
||||
if has(\"Chapter\") then
|
||||
.Chapter.content |=
|
||||
walk(if type == \"string\" then
|
||||
@@ -103,4 +103,4 @@ PARSED_BOOK=$(echo "${BOOK}" | xq "
|
||||
#echo "BOOK: ${BOOK}" >&2
|
||||
#echo "PARSED_BOOK: ${PARSED_BOOK}" >&2
|
||||
|
||||
echo "${PARSED_BOOK}" | xq '.'
|
||||
printf '%s' "${PARSED_BOOK}"
|
||||
+2
-6
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# (The MIT License)
|
||||
#
|
||||
# Copyright (c) 2020 - 2025 SenseGlove
|
||||
# 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
|
||||
@@ -28,7 +28,6 @@
|
||||
authors = ["Mamadou Babaei"]
|
||||
description = "Welcome to the SenseGlove Unreal Engine documentation!"
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
text-direction = "ltr"
|
||||
title = "The SenseGlove Unreal Engine Handbook"
|
||||
@@ -43,8 +42,6 @@ use-default-preprocessors = true
|
||||
|
||||
[preprocessor.links]
|
||||
|
||||
[preprocessor.alerts]
|
||||
|
||||
[preprocessor.senseglove]
|
||||
after = [ "index", "links" ]
|
||||
command = "sh ./bin/sg-preprocessor.sh"
|
||||
@@ -59,10 +56,9 @@ footnote-backrefs = true
|
||||
additional-css = ["styles/custom.css"]
|
||||
additional-js = []
|
||||
cname = "unreal.docs.senseglove.com"
|
||||
copy-fonts = true
|
||||
default-theme = "navy"
|
||||
git-repository-url = "https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal"
|
||||
git-repository-icon = "fa-git"
|
||||
git-repository-icon = "fab-git"
|
||||
input-404 = "404.md"
|
||||
mathjax-support = false
|
||||
no-section-label = false
|
||||
|
||||
+15
-4
@@ -25,6 +25,7 @@ Welcom to the SenseGlove Unreal Engine Handbook!
|
||||
- [Connect to Nova gloves using Blueman Bluetooth Manager](getting-started/sensecom/bluetooth-serial/gnu-linux/blueman-bluetooth-manager.md)
|
||||
- [Connect to Nova gloves using Command-line](getting-started/sensecom/bluetooth-serial/gnu-linux/command-line.md)
|
||||
- [SenseCom on Microsoft Windows](getting-started/sensecom/bluetooth-serial/microsoft-windows.md)
|
||||
- [FakeShm Module Tuning](getting-started/sensecom/fakeshm-tuning/README.md)
|
||||
- [Enabling XR_EXT_hand_tracking on VR Headsets](getting-started/enabling-xr-ext-hand-tracking-vr-headsets/README.md)
|
||||
- [PCVR Mode](getting-started/enabling-xr-ext-hand-tracking-vr-headsets/pcvr-mode/README.md)
|
||||
- [Standalone Mode](getting-started/enabling-xr-ext-hand-tracking-vr-headsets/standalone-mode/README.md)
|
||||
@@ -73,13 +74,14 @@ Welcom to the SenseGlove Unreal Engine Handbook!
|
||||
# 🛠️ Advanced Topics
|
||||
|
||||
- [Safe Glove Access in Blueprint](advanced-topics/safe-glove-access-blueprint/README.md)
|
||||
- [Roll Your Own Hand Manipulation System](advanced-topics/roll-your-own-hand-manipulation-system/README.md)
|
||||
- [SGPawn Events](advanced-topics/roll-your-own-hand-manipulation-system/sgpawn-events-puppeteer-puppet-architecture/README.md)
|
||||
- [SGHandTrackerComponent](advanced-topics/roll-your-own-hand-manipulation-system/sghandtrackercomponent/README.md)
|
||||
- [SGHapticsComponent](advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/README.md)
|
||||
- [OpenXR](advanced-topics/openxr/README.md)
|
||||
- [Consuming FXRHandTrackingState](advanced-topics/openxr/consuming-fxrhandtrackingstate/README.md)
|
||||
- [Blueprint](advanced-topics/openxr/consuming-fxrhandtrackingstate/blueprint.md)
|
||||
- [C++](advanced-topics/openxr/consuming-fxrhandtrackingstate/cpp.md)
|
||||
- [Consuming FXRMotionControllerData](advanced-topics/openxr/consuming-fxrmotioncontrollerdata/README.md)
|
||||
- [Blueprint](advanced-topics/openxr/consuming-fxrmotioncontrollerdata/blueprint.md)
|
||||
- [C++](advanced-topics/openxr/consuming-fxrmotioncontrollerdata/cpp.md)
|
||||
- [Third-Party Integrations](advanced-topics/openxr/third-party-integrations/README.md)
|
||||
- [Third-Party Tutorials](advanced-topics/openxr/third-party-tutorials/README.md)
|
||||
|
||||
@@ -101,11 +103,20 @@ Welcom to the SenseGlove Unreal Engine Handbook!
|
||||
- [SenseGlove Unreal Engine Plugin License](license/senseglove-unreal-engine-plugin.md)
|
||||
- [SenseGlove Unreal Engine Handbook License](license/senseglove-unreal-engine-handbook.md)
|
||||
- [Third-Party Licenses](license/third-party.md)
|
||||
- [Argh Frustration-free Command-line Processing Library License](license/argh-command-line-processing-library.md)
|
||||
- [SenseGlove SDK License](license/senseglove-sdk.md)
|
||||
- [SGBLE and SGBLExx Rust Dependencies Licenses](license/sgble-sgblexx-rust-dependencies.md)
|
||||
- [Boost C++ Libraries License](license/boost-cpp-libraries.md)
|
||||
- [c4core C++ Library Licence](license/c4core-library.md)
|
||||
- [c4project CMake Scripts Licence](license/c4project-cmake-scripts.md)
|
||||
- [Debug Break Library Licence](license/debugbreak-library.md)
|
||||
- [fast_float Number Parsing Library](license/fast-float-number-parsing-library.md)
|
||||
- [FlatBuffers Memory Efficient Serialization Library Licence](license/flatbuffers-serialization-library.md)
|
||||
- [{fmt} Formatting Library License](license/fmt-formatting-library.md)
|
||||
- [Loguru Logging Library License](license/loguru-loggin-library.md)
|
||||
- [FTXUI Functional Terminal (X) User Interface Library](license/ftxui-functional-tui-library.md)
|
||||
- [Loguru Logging Library License](license/loguru-logging-library.md)
|
||||
- [nanomsg-next-generation Lightweight Brokerless Messaging Library License](license/nng-messaging-library.md)
|
||||
- [Rapid YAML Parsing and Emitting Library](license/rapidyaml-parsing-emitting-library.md)
|
||||
- [Serial Communication Library License](license/serial-communication-library.md)
|
||||
|
||||
-----------
|
||||
|
||||
@@ -1,37 +1,156 @@
|
||||
# OpenXR
|
||||
|
||||
The SenseGlove Unreal Engine Plugin has provided OpenXR-compatible hand tracking by implementing `XR_EXT_hand_tracking` since `v2.1.0`.
|
||||
The SenseGlove Unreal Engine Plugin has provided OpenXR-compatible hand tracking by implementing `XR_EXT_hand_tracking` since [`v2.1.0`](../../appendix/changelog.html#210---2024-08-16).
|
||||
|
||||
Typically a user does not need to know anything about OpenXR to use the plugin, so this section of the handbook is for advanced users who are looking for a way to directly consume the OpenXR data coming directly from either a SenseGlove device or if enabled in the plugin settings from hand-tracking.
|
||||
|
||||
Since the SenseGlove Unreal Engine Plugin registers itself as an `OpenXRHandTracking` motion controller device it becomes a hand-tracking provider for Unreal Engine, thus the OpenXR data from SenseGlove could always be retrieved from the Unreal Engine's `IXTrackingSystem` with one caveat. The caveat is if another OpenXR-compatible hand-tracking plugin, e.g. Epic's own OpenXRHandTracking, is enabled simultaneously it's not guaranteed that the `FXRMotionControllerData` and `FXRHandTrackingState` structs retrieved from the `IXTrackingSystem::GetMotionControllerData()` and `IXTrackingSystem::GetHandTrackingState()` methods are coming from SenseGlove, as these methods return the first hand-tracking plugin they could find. Thus, SenseGlove provides its own implementation of `GetMotionControllerData()` and `GetHandTrackingState()` which guarantee the retrieved `FXRMotionControllerData` or `FXRHandTrackingState` are coming from the SenseGlove Unreal Engine Plugin; and this is the preferred way to that.
|
||||
Since the SenseGlove Unreal Engine Plugin registers itself as an `OpenXRHandTracking` device it becomes a hand-tracking provider for Unreal Engine, thus the OpenXR data from SenseGlove could always be retrieved from the Unreal Engine's `IXTrackingSystem` with two caveats:
|
||||
|
||||
> [!NOTE]
|
||||
> In order to retrieve the latest `FXRMotionControllerState` available, The
|
||||
> SenseGlove Unreal Engine Plugin provides an alternative implementation for
|
||||
> `IXTrackingSystem::GetMotionControllerState()` as well . However, since this
|
||||
> method does not rely on the `OpenXRHandTracking` provider, it may become
|
||||
> redundant. As a result, we might consider removing this functionality in
|
||||
> future updates in favor of the one that Unreal Engine provides.
|
||||
- The first caveat is, if another OpenXR-compatible hand-tracking plugin, e.g. Epic's own OpenXRHandTracking, is enabled simultaneously it's not guaranteed that the `FXRHandTrackingState` struct retrieved from the `IXTrackingSystem::GetHandTrackingState()` method is coming from SenseGlove, as these methods return the first hand-tracking plugin they could find. Thus, SenseGlove provides its own implementation of `GetHandTrackingState()` which guarantees the retrieved `FXRHandTrackingState` is coming from the SenseGlove Unreal Engine Plugin; and this is the preferred way to that.
|
||||
- The second caveat is, Unreal `IXTrackingSystem::GetHandTrackingState()` method is blind to SenseGlove's [wrist-tracking settings and offsets](../../plugin-configuration/plugin-settings/tracking/wrist-tracking/), therefore the returned `FXRHandTrackingState` retrieved using this engine function won't take into account the wrist-tracker offsets, and the hands end up at the wrong orientation or position. In contrast,
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
> In order to retrieve the latest `FXRHandTrackingState` available, The
|
||||
> SenseGlove Unreal Engine Plugin provides an alternative implementation for
|
||||
> `IXTrackingSystem::GetHandTrackingState()`, which guarantees the OpenXR
|
||||
> hand-tracking data is coming from a SenseGlove device and also takes into
|
||||
> account the SenseGlove's wrist-tracker offsets automatically.
|
||||
>
|
||||
> This is the recommended approach over Unreal Engine's own
|
||||
> `IXTrackingSystem::GetHandTrackingState()` or you have to ensure the data
|
||||
> received is coming from a SenseGlove device yourself, and also take into
|
||||
> account the SenseGlove's wrist-tracker settings and calculate the wrist
|
||||
> offsets either using one of the `SGHapticGlove::GetWristLocation()` variants,
|
||||
> or manually.
|
||||
>
|
||||
> In short `IXTrackingSystem::GetHandTrackingState()` does not respect the
|
||||
> offsets from
|
||||
> `Project Settings > SenseGlove > Tracking > Wrist-Tracking Settings`
|
||||
|
||||
> [!CAUTION]
|
||||
> In order to retrieve the latest `FXRHandTrackingState` available, The
|
||||
> SenseGlove Unreal Engine Plugin provides an alternative implementation for
|
||||
> `IXTrackingSystem::GetHandTrackingState()`, which guarantees the OpenXR
|
||||
> hand-tracking data is coming from a SenseGlove device and also takes into
|
||||
> account the SenseGlove's wrist-tracker offsets automatically.
|
||||
>
|
||||
> This is the recommended approach over Unreal Engine's own
|
||||
> `IXTrackingSystem::GetHandTrackingState()` or you have to ensure the data
|
||||
> received is coming from a SenseGlove device yourself, and also take into
|
||||
> account the SenseGlove's wrist-tracker settings and calculate the wrist
|
||||
> offsets either using one of the `GetWristLocation()` variants, e.g.,
|
||||
> `SGHandLayer::GetWristLocation()`, `SGHapticGlove::GetWristLocation()`, etc,
|
||||
> or manually.
|
||||
>
|
||||
> In short `IXTrackingSystem::GetHandTrackingState()` does not respect the
|
||||
> offsets from
|
||||
> `Project Settings > SenseGlove > Tracking Settings > Wrist Tracking Settings`.
|
||||
|
||||
> [!TIP]
|
||||
> Starting with version `v2.8.0`, the SenseGlove Unreal Engine Plugin provides
|
||||
> a highly convenient, high-level abstraction that eliminates all of the manual
|
||||
> steps described above. By using
|
||||
> [`SGHandTrackerComponent`](../roll-your-own-hand-manipulation-system/sghandtrackercomponent/)
|
||||
> you can simply add the component to your Pawn class (or any actor that
|
||||
> requires hand-tracking data), configure its properties, and retrieve the
|
||||
> tracking data with a single function call when needed.
|
||||
>
|
||||
> The SenseGlove UE Plugin automatically handles all required settings and
|
||||
> offset calculations for your positional tracking hardware, whether you are
|
||||
> using pure hand tracking or a SenseGlove device. It also provides an optional
|
||||
> debug hand out of the box, allowing you to instantly visualize hand-tracking
|
||||
> data without writing a single line of code.
|
||||
>
|
||||
> Furthermore, version `v2.8.0` ships with a companion component,
|
||||
> [`SGHapticsComponent`](../roll-your-own-hand-manipulation-system/sghapticscomponent/),
|
||||
> which makes it easy to add haptic feedback to your own custom or third-party
|
||||
> hand manipulation systems.
|
||||
|
||||
> [!TIP]
|
||||
> Sometimes, using `IXTrackingSystem::GetHandTrackingState()` is unavoidable,
|
||||
> e.g., when using a third-party hand manipulation system such as
|
||||
> [**VR Expansion Plugin (VRE)**](https://vreue4.com/), which internally relies
|
||||
> on `IXTrackingSystem::GetHandTrackingState()` to retrieve the hand-tracking
|
||||
> data. In that case SenseGlove provides methods such as
|
||||
> `SGHandLayer::GetWristLocation()`, `SGHapticGlove::GetWristLocation()`, etc,
|
||||
> which you can use to reliably calculate the wrist offsets:
|
||||
>
|
||||
> ```cpp
|
||||
> // Get the OpenXR hand-tracking data for the right hand
|
||||
> FXRHandTrackingState HandTrackingState;
|
||||
> IXTrackingSystem::GetHandTrackingState(
|
||||
> GetWorld(),
|
||||
> EXRSpaceType::UnrealWorldSpace,
|
||||
> EControllerHand::Right,
|
||||
> HandTrackingState);
|
||||
>
|
||||
> // Return if the struct data is invalid!
|
||||
> if (!bGotHandTrackingState || !HandTrackingState.bValid)
|
||||
> {
|
||||
> return;
|
||||
> }
|
||||
>
|
||||
> // Return if the device is not being tracked!
|
||||
> if (HandTrackingState.TrackingStatus == ETrackingStatus::NotTracked)
|
||||
> {
|
||||
> return;
|
||||
> }
|
||||
>
|
||||
> // Ensure that HandTrackingState.HandKeyLocations has the location data
|
||||
> // for 26 joints!
|
||||
> if (!ensureAlwaysMsgf(HandTrackingState.HandKeyLocations.Num()
|
||||
> == EHandKeypointCount,
|
||||
> TEXT("Invalid HandKeyLocations count!")))
|
||||
> {
|
||||
> return;
|
||||
> }
|
||||
>
|
||||
> // Ensure that HandTrackingState.HandKeyRotations has the rotation data
|
||||
> // for 26 joints!
|
||||
> if (!ensureAlwaysMsgf(HandTrackingState.HandKeyRotations.Num()
|
||||
> == EHandKeypointCount,
|
||||
> TEXT("Invalid HandKeyRotations count!")))
|
||||
> {
|
||||
> return;
|
||||
> }
|
||||
>
|
||||
> {
|
||||
> // FQuat variant of FSGHandLayer::GetWristLocation()
|
||||
> // Get the OpenXR hand-tracking data with the correct wrist offsets for
|
||||
> // Meta Quest 3 Controllers
|
||||
> FVector WristLocation;
|
||||
> FQuat WristRotation;
|
||||
> FSGHandLayer::GetWristLocation(
|
||||
> true, // true for a right-handed glove and false for a left-handed one
|
||||
> HandTrackingState.HandKeyLocations[0], // 0 is the Wrist joint location
|
||||
> HandTrackingState.HandKeyRotations[0], // 0 is the wrist joint rotation
|
||||
> ESGPositionalTrackingHardware::Quest3Controller,
|
||||
> WristLocation, WristRotation);
|
||||
>
|
||||
> // WristLocation and WristRotation variables now contain the correct
|
||||
> // OpenXR hand-tracking data with the wrist offsets applied correctly
|
||||
> }
|
||||
>
|
||||
> {
|
||||
> // FRotator variant of FSGHandLayer::GetWristLocation()
|
||||
> // Get the OpenXR hand-tracking data with the correct wrist offsets for
|
||||
> // Meta Quest 3 Controllers
|
||||
> FVector WristLocation;
|
||||
> FRotator WristRotation;
|
||||
> FSGHandLayer::GetWristLocation(
|
||||
> true, // true for a right-handed glove and false for a left-handed one
|
||||
> HandTrackingState.HandKeyLocations[0], // 0 is the Wrist joint location
|
||||
> HandTrackingState.HandKeyRotations[0].Rotator, // 0 is the wrist joint rotation
|
||||
> ESGPositionalTrackingHardware::Quest3Controller,
|
||||
> WristLocation, WristRotation);
|
||||
>
|
||||
> // WristLocation and WristRotation variables now contain the correct
|
||||
> // OpenXR hand-tracking data with the wrist offsets applied correctly
|
||||
> }
|
||||
> ```
|
||||
|
||||
In the next sections we'll see:
|
||||
|
||||
- [How we can directly consume the `FXRMotionControllerData`](consuming-fxrmotioncontrollerdata/) on UE `5.2`, `5.3`, `5.4`, and `5.5` to draw and animate debug virtual hands in both [Blueprint](consuming-fxrmotioncontrollerdata/blueprint.md) and [C++](consuming-fxrmotioncontrollerdata/cpp.md).
|
||||
|
||||
- [How we can directly consume the `FXRHandTrackingState`](consuming-fxrhandtrackingstate/) on UE `5.5` to draw and animate debug virtual hands in both [Blueprint](consuming-fxrhandtrackingstate/blueprint.md) and [C++](consuming-fxrhandtrackingstate/cpp.md).
|
||||
|
||||
- [The Consuming OpenXR Hand-Tracking Data tutorial series](third-party-tutorials/) provides a comprehensive introduction to virtual reality, OpenXR hand-tracking, and gesture detection in Unreal Engine. Additionally, this tutorial series covers procedural virtual hand mesh animation using the OpenXR hand-tracking data.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# Consuming FXRHandTrackingState
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand-tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
> [!TIP]
|
||||
> Starting with version `v2.8.0`, a highly convenient high-level approach for
|
||||
> retrieving `FXRHandTrackingState` has been introduced. It allows you to obtain
|
||||
> OpenXR hand-tracking data without worrying about the caveats mentioned in the
|
||||
> [`main OpenXR section`](../).
|
||||
>
|
||||
> [`SGHandTrackerComponent`](../../roll-your-own-hand-manipulation-system/sghandtrackercomponent/)
|
||||
> abstracts these complexities away and provides correctly adjusted
|
||||
> hand-tracking data with a single function call.
|
||||
>
|
||||
> This version also introduces a companion
|
||||
> [`SGHapticsComponent`](../../roll-your-own-hand-manipulation-system/sghapticscomponent/)
|
||||
> is also available for easily integrating haptic feedback.
|
||||
|
||||
Taking a closer look at the `FXRHandTrackingState` declaration inside the Unreal Engine's `HeadMountedDisplay` module at [`Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h`](https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h), figuring out the data structure might not seem very straightforward:
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Consuming FXRHandTrackingState in Blueprint
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
|
||||
Before continuing this section, please ensure you've studied the [Consuming FXRHandTrackingState](./) section, first.
|
||||
|
||||
> [!TIP]
|
||||
> Starting with version `v2.8.0`, a highly convenient high-level approach for
|
||||
> retrieving `FXRHandTrackingState` has been introduced. It allows you to obtain
|
||||
> OpenXR hand-tracking data without worrying about the caveats mentioned in the
|
||||
> [`main OpenXR section`](../).
|
||||
>
|
||||
> [`SGHandTrackerComponent`](../../roll-your-own-hand-manipulation-system/sghandtrackercomponent/)
|
||||
> abstracts these complexities away and provides correctly adjusted
|
||||
> hand-tracking data with a single function call.
|
||||
>
|
||||
> This version also introduces a companion
|
||||
> [`SGHapticsComponent`](../../roll-your-own-hand-manipulation-system/sghapticscomponent/)
|
||||
> is also available for easily integrating haptic feedback.
|
||||
|
||||
## Drawing and Animating Virtual Hands
|
||||
|
||||
1. Create a new Virtual Reality project based [the Unreal VR Template](https://dev.epicgames.com/documentation/en-us/unreal-engine/vr-template-in-unreal-engine).
|
||||
@@ -62,4 +62,4 @@ Before continuing this section, please ensure you've studied the [Consuming FXRH
|
||||
|
||||
13. Now, go back to the `VRTemplateMap` and use the VR Preview button to run the game. If everything's done correctly, you should be able to see the virtual hands inside your VR simulation.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Consuming FXRHandTrackingState in C++
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
|
||||
Before continuing this section, please ensure you've first studied the [Consuming FXRHandTrackingState](./) section.
|
||||
|
||||
> [!TIP]
|
||||
> Starting with version `v2.8.0`, a highly convenient high-level approach for
|
||||
> retrieving `FXRHandTrackingState` has been introduced. It allows you to obtain
|
||||
> OpenXR hand-tracking data without worrying about the caveats mentioned in the
|
||||
> [`main OpenXR section`](../).
|
||||
>
|
||||
> [`SGHandTrackerComponent`](../../roll-your-own-hand-manipulation-system/sghandtrackercomponent/)
|
||||
> abstracts these complexities away and provides correctly adjusted
|
||||
> hand-tracking data with a single function call.
|
||||
>
|
||||
> This version also introduces a companion
|
||||
> [`SGHapticsComponent`](../../roll-your-own-hand-manipulation-system/sghapticscomponent/)
|
||||
> is also available for easily integrating haptic feedback.
|
||||
|
||||
## Drawing and Animating Virtual Hands
|
||||
|
||||
1. Create a new Virtual Reality project based [the Unreal VR Template](https://dev.epicgames.com/documentation/en-us/unreal-engine/vr-template-in-unreal-engine).
|
||||
@@ -256,4 +256,4 @@ void ADebugPawn::DrawHand(const EControllerHand Hand) const
|
||||
|
||||
15. Now, rebuild the source code and go back to the `VRTemplateMap`, then use the VR Preview button to run the game. If everything's done correctly, you should be able to see the virtual hands inside your VR simulation.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
# Consuming FXRMotionControllerData
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
|
||||
Taking a closer look at the `FXRMotionControllerData` declaration inside the Unreal Engine's `HeadMountedDisplay` module at [`Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h`](https://github.com/EpicGames/UnrealEngine/blob/5.5/Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h), figuring out the data structure might not seem very straightforward:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct FXRMotionControllerData
|
||||
{
|
||||
GENERATED_USTRUCT_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
bool bValid = false;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FName DeviceName;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FGuid ApplicationInstanceID;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
EXRVisualType DeviceVisualType = EXRVisualType::Controller;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
EControllerHand HandIndex = EControllerHand::Left;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
ETrackingStatus TrackingStatus = ETrackingStatus::NotTracked;
|
||||
|
||||
// Vector representing an object being held in the player's hand
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FVector GripPosition = FVector(0.0f);
|
||||
// Quaternion representing an object being held in the player's hand
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FQuat GripRotation = FQuat(EForceInit::ForceInitToZero);
|
||||
|
||||
// For handheld controllers, gives a vector for pointing at objects
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FVector AimPosition = FVector(0.0f);
|
||||
// For handheld controllers, gives a quaternion for pointing at objects
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FQuat AimRotation = FQuat(EForceInit::ForceInitToZero);
|
||||
|
||||
// For handheld controllers, gives a vector for representing the hand
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FVector PalmPosition = FVector(0.0f);
|
||||
// For handheld controllers, gives a quaternion for representing the hand
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
FQuat PalmRotation = FQuat(EForceInit::ForceInitToZero);
|
||||
|
||||
// The indices of this array are the values of EHandKeypoint (Palm, Wrist, ThumbMetacarpal, etc).
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
TArray<FVector> HandKeyPositions;
|
||||
// The indices of this array are the values of EHandKeypoint (Palm, Wrist, ThumbMetacarpal, etc).
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
TArray<FQuat> HandKeyRotations;
|
||||
// The indices of this array are the values of EHandKeypoint (Palm, Wrist, ThumbMetacarpal, etc).
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
TArray<float> HandKeyRadii;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "XR")
|
||||
bool bIsGrasped = false;
|
||||
};
|
||||
```
|
||||
|
||||
Which on the Blueprint side it looks like this:
|
||||
|
||||

|
||||
|
||||
But, fear not, we've got you covered!
|
||||
|
||||
## FXRMotionControllerData in Unreal Engine
|
||||
|
||||
`FXRMotionControllerData` is a structure in Unreal Engine designed to hold detailed information about the state of a motion controller device at a given moment. This structure is essential for handling motion controller inputs in virtual reality (VR) applications, providing the necessary data to accurately track and represent the user's hand movements and actions within the virtual environment.
|
||||
|
||||
### Structure Members of FXRMotionControllerData
|
||||
|
||||
- **bValid**
|
||||
- **Description**: A boolean flag indicating whether the data is valid or not.
|
||||
- **Usage**: This is used to check if the motion controller data is correctly initialized and can be used for further processing.
|
||||
|
||||
- **DeviceName**
|
||||
- **Type**: `FName`
|
||||
- **Description**: The name of the device.
|
||||
- **Usage**: Identifies which motion controller device the data is coming from, useful when multiple devices are in use.
|
||||
|
||||
- **ApplicationInstanceID**
|
||||
- **Type**: `FString`
|
||||
- **Description**: A unique identifier for the application instance.
|
||||
- **Usage**: Helps in differentiating data from different instances of an application, ensuring the correct instance processes the data.
|
||||
|
||||
- **DeviceVisualType**
|
||||
- **Type**: `EXRVisualType`
|
||||
- **Description**: Enum specifying the visual type of the device (e.g., controller, hand).
|
||||
- **Usage**: Used to differentiate between various motion controller devices or hand-tracking representations for rendering and interaction purposes.
|
||||
|
||||
- **HandIndex**
|
||||
- **Type**: `EControllerHand`
|
||||
- **Description**: Enum indicating which hand is being tracked (left or right).
|
||||
- **Usage**: Helps identify whether the motion data pertains to the left or right hand, essential for hand-specific actions or interactions.
|
||||
|
||||
- **TrackingStatus**
|
||||
- **Type**: `EXRTrackingStatus`
|
||||
- **Description**: Enum indicating the tracking status of the motion controller.
|
||||
- **Usage**: Shows whether the controller is being tracked accurately, with possible statuses like `Tracked`, `NotTracked`, etc.
|
||||
|
||||
- **GripPosition**
|
||||
- **Type**: `FVector`
|
||||
- **Description**: The position of the grip in world coordinates.
|
||||
- **Usage**: Provides the 3D coordinates of the controller's grip, essential for positioning the virtual representation of the controller.
|
||||
|
||||
- **GripRotation**
|
||||
- **Type**: `FQuat`
|
||||
- **Description**: The rotation of the grip in world coordinates.
|
||||
- **Usage**: Provides the orientation of the controller's grip, allowing for accurate rotation and alignment in the virtual space.
|
||||
|
||||
- **AimPosition**
|
||||
- **Type**: `FVector`
|
||||
- **Description**: The position of the aim point in world coordinates.
|
||||
- **Usage**: Specifies where the controller is aiming, useful for aiming or pointing actions.
|
||||
|
||||
- **AimRotation**
|
||||
- **Type**: `FQuat`
|
||||
- **Description**: The rotation of the aim point in world coordinates.
|
||||
- **Usage**: Determines the orientation of the aim direction, important for actions like shooting or selecting objects in VR.
|
||||
|
||||
- **PalmPosition**
|
||||
- **Type**: `FVector`
|
||||
- **Description**: The position of the palm in world coordinates.
|
||||
- **Usage**: Provides the 3D location of the palm, important for determining hand gestures or interactions in VR.
|
||||
|
||||
- **PalmRotation**
|
||||
- **Type**: `FQuat`
|
||||
- **Description**: The rotation of the palm in world coordinates.
|
||||
- **Usage**: Defines the orientation of the palm, crucial for hand-based interaction accuracy and realism in VR experiences.
|
||||
|
||||
- **HandKeyPositions**
|
||||
- **Type**: `TArray<FVector>`
|
||||
- **Description**: An array of vectors representing key positions of the hand.
|
||||
- **Usage**: Provides detailed positions of key points on the hand, useful for precise hand tracking and interaction.
|
||||
|
||||
- **HandKeyRotations**
|
||||
- **Type**: `TArray<FQuat>`
|
||||
- **Description**: An array of quaternions representing key rotations of the hand.
|
||||
- **Usage**: Complements the hand key positions with rotational data, ensuring accurate representation of hand movements.
|
||||
|
||||
- **HandKeyRadii**
|
||||
- **Type**: `TArray<float>`
|
||||
- **Description**: An array of floats representing the radii of key points of the hand.
|
||||
- **Usage**: Gives the size of the hand key points, aiding in collision detection and interaction fidelity.
|
||||
|
||||
- **bIsGrasped**
|
||||
- **Type**: `bool`
|
||||
- **Description**: A boolean indicating whether the controller is currently grasping an object.
|
||||
- **Usage**: Determines if the user is holding something, affecting interactions and animations.
|
||||
|
||||
### Organization of FXRMotionControllerData
|
||||
|
||||
The structure is organized to encapsulate all relevant data needed for hand and motion controller tracking in a coherent and accessible manner. Boolean flags `bValid` and `bIsGrasped` provide quick checks on the state of the controller data. Identifiers `DeviceName` and `ApplicationInstanceID` ensure the correct association of data. Positional and rotational data `GripPosition`, `GripRotation`, `AimPosition`, and `AimRotation` offer precise tracking of the controller's movement. Arrays `HandKeyPositions`, `HandKeyRotations`, and `HandKeyRadii` allow detailed hand tracking, which is critical for immersive VR experiences. Lastly, the tracking status `TrackingStatus` informs the system of the reliability of the data being processed and whether the motion controller is actively being tracked or it's inactive at the moment.
|
||||
|
||||
### Processing the Data for Drawing and Animating a Virtual Hand
|
||||
|
||||
In order to draw and animate a virtual hand in real-time whether the data is coming from hand-tracking or a SenseGlove device, we could consume the data from the `HandKeyPositions` and `HandKeyRotations` fields of the `FXRMotionControllerData` struct.
|
||||
|
||||
Both `HandKeyPositions` and `HandKeyRotations` contain 26 elements as defined by OpenXR's [`XR_HAND_JOINT_COUNT_EXT`](https://registry.khronos.org/OpenXR/specs/1.1/man/html/XR_HAND_JOINT_COUNT_EXT.html) and [`XrHandJointLocationsEXT`](https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrHandJointLocationsEXT.html), etc.
|
||||
|
||||
Unreal Engine also provides an enum called `EHandKeypoint` naming the 26 joints, and the equivalent of `XR_HAND_JOINT_COUNT_EXT` as `EHandKeypointCount` inside [`Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h`](https://github.com/EpicGames/UnrealEngine/blob/5.5/Engine/Source/Runtime/HeadMountedDisplay/Public/HeadMountedDisplayTypes.h) as follows:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* Transforms that are tracked on the hand.
|
||||
* Matches the enums from WMR to make it a direct mapping
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EHandKeypoint : uint8
|
||||
{
|
||||
Palm,
|
||||
Wrist,
|
||||
ThumbMetacarpal,
|
||||
ThumbProximal,
|
||||
ThumbDistal,
|
||||
ThumbTip,
|
||||
IndexMetacarpal,
|
||||
IndexProximal,
|
||||
IndexIntermediate,
|
||||
IndexDistal,
|
||||
IndexTip,
|
||||
MiddleMetacarpal,
|
||||
MiddleProximal,
|
||||
MiddleIntermediate,
|
||||
MiddleDistal,
|
||||
MiddleTip,
|
||||
RingMetacarpal,
|
||||
RingProximal,
|
||||
RingIntermediate,
|
||||
RingDistal,
|
||||
RingTip,
|
||||
LittleMetacarpal,
|
||||
LittleProximal,
|
||||
LittleIntermediate,
|
||||
LittleDistal,
|
||||
LittleTip
|
||||
};
|
||||
|
||||
const int32 EHandKeypointCount = static_cast<int32>(EHandKeypoint::LittleTip) + 1;
|
||||
|
||||
```
|
||||
|
||||
So, getting the any joint's position or rotation is as easy as casting the enum value and passing it as the array index.
|
||||
|
||||
```cpp
|
||||
FXRMotionControllerData MotionControllerData;
|
||||
const bool bGotMotionControllerData = FSGXRTracker::GetMotionControllerData(
|
||||
GetWorld(), EControllerHand::Left, MotionControllerData);
|
||||
|
||||
// Return if the struct data is invalid!
|
||||
if (!bGotMotionControllerData || !MotionControllerData.bValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if the device is not being tracked!
|
||||
if (MotionControllerData.TrackingStatus == ETrackingStatus::NotTracked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.DeviceVisualType is a hand!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.DeviceVisualType
|
||||
== EXRVisualType::Hand,
|
||||
TEXT("Invalid DeviceVisualType type!")))
|
||||
{
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.HandKeyPositions has the position data
|
||||
// for 26 joints!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.HandKeyPositions.Num()
|
||||
== EHandKeypointCount,
|
||||
TEXT("Invalid HandKeyPositions count!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.HandKeyRotations has the rotation data
|
||||
// for 26 joints!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.HandKeyRotations.Num()
|
||||
== EHandKeypointCount,
|
||||
TEXT("Invalid HandKeyRotations count!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr int32 PalmIndex = static_cast<int32>(EHandKeypoint::Palm);
|
||||
|
||||
const FVector& PalmPosition{
|
||||
MotionControllerData.HandKeyPositions[PalmIndex]
|
||||
};
|
||||
const FRotator& PalmRotation{
|
||||
MotionControllerData.HandKeyRotations[PalmIndex].Rotator()
|
||||
};
|
||||
```
|
||||
|
||||
The equivalent Blueprint code for the above looks something like this:
|
||||
|
||||

|
||||
|
||||
OK, now that we've got a glimpse of how the virtual hand's joint data could be processed we are going to draw and animate a virtual hand in both [Blueprint](blueprint.md) and [C++](cpp.md) in the upcoming sections.
|
||||
@@ -1,65 +0,0 @@
|
||||
# Consuming FXRMotionControllerData in Blueprint
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
|
||||
Before continuing this section, please ensure you've studied the [Consuming FXRMotionControllerData](./) section, first.
|
||||
|
||||
## Drawing and Animating Virtual Hands
|
||||
|
||||
1. Create a new Virtual Reality project based [the Unreal VR Template](https://dev.epicgames.com/documentation/en-us/unreal-engine/vr-template-in-unreal-engine).
|
||||
|
||||
2. Make sure the [SenseGlove UnrealEngine plugin is installed and enabled](/getting-started/installation.md) inside your new project.
|
||||
|
||||

|
||||
|
||||
3. You could use either hand-tracking or a SenseGlove device as the input data, or both of the inside the same project. Whether you would like to use hand-tracking or a SenseGlove device, please make sure the required steps are taken for each of those first.
|
||||
|
||||
4. You could add the required Blueprint code for drawing virtual hands to either your Level Buleprint or the VRPawn Blueprint Class located at `/Content/VRTemplate/Blueprints/VRPawn`. In this guide we are going to add the code to our VRPawn.
|
||||
|
||||
5. Add a new function named `Draw Hand` with an input parameter of type `EController Hand` named `Hand`.
|
||||
|
||||

|
||||
|
||||
6. Inside this function's event graph add a `Get Motion Controller Data` node from `SenseGlove > Tracking > XR Tracker > Get Motion Controller Data`.
|
||||
|
||||

|
||||
|
||||
7. Then connect the functions `Hand` input parameter to the `Get Motion Controller Data`'s `Hand` input and right-click on the `OutMotionControllerData` parameter and use the `Break XRMotionControllerData` node to break the struct to it's fields.
|
||||
|
||||

|
||||
|
||||
8. After this, we need to perform data validation by checking the return status of the `Get Motion Controller Data` function and `FXRMotionControllerData`'s `Valid` field. Then, we check if the motion controller device is being tracked and indeed coming from a hand-tracking source. And, finally, we check whether we have the positions and rotations for exactly `26` joints or not.
|
||||
|
||||

|
||||
|
||||
9. OK, now it's time to draw the joints! If we check out the SenseGlove Debug module's draw option, we notice there are various ways to draw the debug virtual hand. Drawing a cube or a gizmo per joint, or draw the whole hand all at once by passing the retrieved `FXRMotionControllerData` to the `DebugVirtualHand::Draw` function! But, since the point of this tutorial is to learn how to consume the `FXRMotionControllerData` we ignore the last option. Between the debug cubes or gizmos, we are going to choose the gizmos since they better represent the rotations than the cubes.
|
||||
|
||||

|
||||
|
||||
10. In the last step inside the `Draw Hand` function, in order to draw a virtual hand with `26` joints, we have to first iterate through either of the `Hand Key Positions` or `Hand Key Rotations` arrays from the `FXRMotionControllerData` struct. Since we made sure both arrays have `26` elements before we reached this step, it's safe to just iterate over one and use the `Array Index` inside a `For Each Loop` or a `For Loop` to access the position and rotation of every joint. Then we use each array `Get (a ref)` method to access the position and rotation data inside the loop and call the `Draw` function from `SenseGlove > Debug > Gizmo` per every joint. Please note that there are two `Draw` functions and the only difference between the two is that one accepts an `FQuat` and the other a `FRotator` for its `Rotation` input parameter. In this case, we use the `FQuat` variant to avoid an extra conversion to `FRotator`. Also, please adjust the `Thickness` option for the `Settings` parameter from `1.0` to `0.2`, as the default value might be too thick for drawing a joint gizmo.
|
||||
|
||||

|
||||
|
||||
11. Well, now the full implementation for the `Draw Hand` function insde the `VRPawn` should look something like this:
|
||||
|
||||

|
||||
|
||||
12. Finally, go back to `VRPawn`'s event graph and the following code to the `Tick` event. Basically what we do here is call our newly implemented `Draw Hand` twice, once for each hand.
|
||||
|
||||

|
||||
|
||||
13. Now, go back to the `VRTemplateMap` and use the VR Preview button to run the game. If everything's done correctly, you should be able to see the virtual hands inside your VR simulation.
|
||||
|
||||

|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,266 +0,0 @@
|
||||
# Consuming FXRMotionControllerData in C++
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Unreal Engine versions `5.2`, `5.3`, and `5.4` are limited to
|
||||
> `FXRMotionControllerData` since at the time of their release no
|
||||
> `FXRHandTrackingState` was available.
|
||||
> Also please keep in mind that, while `FXRMotionControllerData` is pretty much
|
||||
> usable and functional in Unreal Engine `5.5`, it is recommended to utilize
|
||||
> `FXRHandTrackingState` instead. This is because this version of UE has
|
||||
> deprecated `FXRMotionControllerData` in favor of the
|
||||
> `FXRMotionControllerState` and `FXRHandTrackingState` structs. Prior to
|
||||
> version `5.5`, `FXRMotionControllerData` handled both motion controller and
|
||||
> hand tracking data. From `5.5` onward, these responsibilities have been
|
||||
> separated into the two distinct structs, providing clearer and more
|
||||
> specialized handling of each.
|
||||
|
||||
Before continuing this section, please ensure you've first studied the [Consuming FXRMotionControllerData](./) section.
|
||||
|
||||
## Drawing and Animating Virtual Hands
|
||||
|
||||
1. Create a new Virtual Reality project based [the Unreal VR Template](https://dev.epicgames.com/documentation/en-us/unreal-engine/vr-template-in-unreal-engine).
|
||||
|
||||
2. Make sure the [SenseGlove UnrealEngine plugin is installed and enabled](/getting-started/installation.md) inside your new project.
|
||||
|
||||

|
||||
|
||||
3. You could use either hand-tracking or a SenseGlove device as the input data, or both of the inside the same project. Whether you would like to use hand-tracking or a SenseGlove device, please make sure the required steps are taken for each of those first.
|
||||
|
||||
4. From the `Tools` menu choose `New C++ class...`.
|
||||
|
||||

|
||||
|
||||
5. Choose the Unreal Engine's `APawn` class as the parent class for the new C++ pawn class.
|
||||
|
||||

|
||||
|
||||
6. Name the new pawn class `DebugPawn`.
|
||||
|
||||

|
||||
|
||||
7. Since we have created a new C++ class, this converts the current Blueprint VRTemplateMap project to a C++ one. That's why the Unreal Editor will give us a few prompts regarding opening the project in the default IDE and rebuilding the code. It might be simpler to just close the editor, then rebuild the source code inside your favorite IDE, and then start the editor with the converted project again.
|
||||
|
||||
8. Find and open the VRPawn Blueprint Class located at `/Content/VRTemplate/Blueprints/VRPawn` inside the Blueprint Editor and from the `File` menu choose the `Reparent Blueprint` class.
|
||||
|
||||

|
||||
|
||||
9. In the new `Reparent blueprint` window choose `DebugPawn` as the new parent.
|
||||
|
||||

|
||||
|
||||
10. By looking at the `Parent Class` label located under the Blueprint Editor window control buttons verify that the `ADebugPawn` class has been set as the new parent.
|
||||
|
||||

|
||||
|
||||
11. Locate the project's main Build file, in our case `VirtualHandCpp/Source/VirtualHandCpp/VirtualHandCpp.Build.cs` and add the `InputDevice`, `OpenXRHMD`, `SenseGloveBuildHacks`, `SenseGloveDebug`, `SenseGloveSettings`, and `SenseGloveTracking` modules as either a private or public dependency.
|
||||
|
||||
```csharp
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class VirtualHandCpp : ModuleRules
|
||||
{
|
||||
public VirtualHandCpp(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"InputDevice",
|
||||
"OpenXRHMD",
|
||||
"SenseGloveBuildHacks",
|
||||
"SenseGloveDebug",
|
||||
"SenseGloveSettings",
|
||||
"SenseGloveTracking"
|
||||
});
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
||||
// Uncomment if you are using online features
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
|
||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
12. Locate the C++ header and source file for the `ADebugPawn` inside the project in your C++ IDE. In our case they are located at `VirtualHandCpp/Source/VirtualHandCpp/DebugPawn.h` and `VirtualHandCpp/Source/VirtualHandCpp/DebugPawn.cpp`.
|
||||
|
||||
13. Modify the `DebugPawn.h` header file to look like this:
|
||||
|
||||
```cpp
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
|
||||
#include "SGSettings/SGDebugGizmoSettings.h"
|
||||
|
||||
#include "DebugPawn.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class VIRTUALHANDCPP_API ADebugPawn : public APawn
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
// The virtual hand drawing settings.
|
||||
UPROPERTY(EditDefaultsOnly, Category="DebugPawn",
|
||||
meta=(AllowPrivateAccess="false"))
|
||||
FSGDebugGizmoSettings HandDrawingSettings;
|
||||
|
||||
public:
|
||||
// Sets default values for this pawn's properties
|
||||
ADebugPawn();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
private:
|
||||
// The method responsible for drawing a virtual hand.
|
||||
void DrawHand(EControllerHand Hand) const;
|
||||
};
|
||||
```
|
||||
|
||||
14. Modify the `DebugPawn.cpp` implementation file to look like this:
|
||||
|
||||
|
||||
```cpp
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "DebugPawn.h"
|
||||
|
||||
#include "SGDebug/SGDebugGizmo.h"
|
||||
#include "SGTracking/SGXRTracker.h"
|
||||
|
||||
// Sets default values
|
||||
ADebugPawn::ADebugPawn()
|
||||
{
|
||||
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// Set the default virtual hand drawing settings.
|
||||
HandDrawingSettings = FSGDebugGizmoSettings{
|
||||
1.0f,
|
||||
FColor{255, 0, 0, 255},
|
||||
FColor{0, 255, 0, 255},
|
||||
FColor{0, 0, 255, 255},
|
||||
false,
|
||||
1.1f,
|
||||
0,
|
||||
0.2f,
|
||||
};
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ADebugPawn::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void ADebugPawn::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Attempt at drawing the left/right virtual hands every frame.
|
||||
DrawHand(EControllerHand::Left);
|
||||
DrawHand(EControllerHand::Right);
|
||||
}
|
||||
|
||||
// Called to bind functionality to input
|
||||
void ADebugPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
}
|
||||
|
||||
void ADebugPawn::DrawHand(const EControllerHand Hand) const
|
||||
{
|
||||
// Get the world and cache it, if it's null we return early.
|
||||
UWorld* World{GetWorld()};
|
||||
if (!IsValid(World))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FXRMotionControllerData MotionControllerData;
|
||||
const bool bGotMotionControllerData = FSGXRTracker::GetMotionControllerData(
|
||||
World, Hand, MotionControllerData);
|
||||
|
||||
// Return if the struct data is invalid!
|
||||
if (!bGotMotionControllerData || !MotionControllerData.bValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if the device is not being tracked!
|
||||
if (MotionControllerData.TrackingStatus == ETrackingStatus::NotTracked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.DeviceVisualType is a hand!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.DeviceVisualType
|
||||
== EXRVisualType::Hand,
|
||||
TEXT("Invalid DeviceVisualType type!")))
|
||||
{
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.HandKeyPositions has the position data
|
||||
// for 26 joints!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.HandKeyPositions.Num()
|
||||
== EHandKeypointCount,
|
||||
TEXT("Invalid HandKeyPositions count!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that MotionControllerData.HandKeyRotations has the rotation data
|
||||
// for 26 joints!
|
||||
if (!ensureAlwaysMsgf(MotionControllerData.HandKeyRotations.Num()
|
||||
== EHandKeypointCount,
|
||||
TEXT("Invalid HandKeyRotations count!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over the hand joint positions and rotations!
|
||||
for (int32 JointIndex = 0; JointIndex < EHandKeypointCount; ++JointIndex)
|
||||
{
|
||||
const FVector& JointPosition{
|
||||
MotionControllerData.HandKeyPositions[JointIndex]
|
||||
};
|
||||
const FQuat& JointRotation{
|
||||
MotionControllerData.HandKeyRotations[JointIndex]
|
||||
};
|
||||
|
||||
// Draw a single joint's gizmo!
|
||||
// Please note that we could alternatively:
|
||||
// Use FSGDebugCube::Draw() to draw a cube.
|
||||
// Or use the FSGDebugVirtualHand::Draw() method and pass the
|
||||
// MotionControllerData directly to draw the virtual hand
|
||||
// all at once without iterating the joints. But, that's not
|
||||
// goal of this tutorial.
|
||||
FSGDebugGizmo::Draw(World, JointPosition, JointRotation, HandDrawingSettings);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
15. Now, rebuild the source code and go back to the `VRTemplateMap`, then use the VR Preview button to run the game. If everything's done correctly, you should be able to see the virtual hands inside your VR simulation.
|
||||
|
||||

|
||||
BIN
Binary file not shown.
@@ -41,7 +41,7 @@ As the SenseGlove Unreal Engine Plugin is fully OpenXR-compliant, it provides Op
|
||||
> need to figure out how to manually apply the appropriate offsets within your
|
||||
> chosen OpenXR hand interaction system.
|
||||
>
|
||||
> For example, the VRE plugin provide similar configurations in their plugin's
|
||||
> For example, the VRE plugin provides similar configurations in their plugin's
|
||||
> settings section. For more information refer to the
|
||||
> [Changing Wrist-Tracker Offsets](#changing-wrist-tracker-offsets) section
|
||||
> below.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Roll Your Own Customized Hand Manipulation and Interaction System
|
||||
|
||||
The default hand interaction system shipped with the **SenseGlove Unreal Engine Plugin** consists of various components, including `SGPawn`, `SGPlayerController`, `SGVirtualHandComponent`, `SGGrabComponent`, `SGTouchComponent`, and others. This system is very easy to get started with and is thoroughly documented throughout this handbook.
|
||||
|
||||
However, this simplicity comes at a cost: limited functionality. At SenseGlove, we prioritize usability and practicality. That said, developing a comprehensive hand interaction system that suits every possible use case is not an easy task. For example, projects such as the [VR Expansion Plugin (VRE)](https://vreue4.com/) — an Epic MegaGrants recipient — have been in development for over a decade, and development is still ongoing. Naturally, such depth also comes with a trade-off: a steep learning curve and reduced beginner-friendliness.
|
||||
|
||||
To bridge this gap and serve different groups of users, we provide the `SGPawn` system as a simple, intentionally limited, and beginner-friendly default solution that allows anyone to get up and running quickly.
|
||||
|
||||
At the same time, to support intermediate and advanced users, we have aimed for full OpenXR compatibility, opening the door to a wide range of advanced possibilities. Once enabled and loaded in Unreal Engine, the SenseGlove Unreal Engine Plugin registers itself as an `OpenXRHandTracking` provider. This makes it a fully compatible, drop-in replacement for Epic’s **OpenXRHandTracking** plugin.
|
||||
|
||||
As a result, [it can integrate seamlessly with any third-party system or plugin that consumes OpenXR hand-tracking data](../openxr/third-party-integrations/). Because the SenseGlove plugin is fully OpenXR-compliant, it provides hand-tracking data in the expected OpenXR format and becomes the active provider within Unreal. If your existing interaction system (for example, the VRE plugin) already relies on OpenXR hand-tracking, SenseGlove can function as a direct tracking source instead of a physical hand.
|
||||
|
||||
Furthermore, the SenseGlove OpenXR backend allows you to [develop and build your own hand interaction system from scratch](../openxr/third-party-tutorials/). This system can operate either via standard OpenXR hand-tracking or with a SenseGlove device interchangeably.
|
||||
|
||||
## Comparison of Available Approaches
|
||||
|
||||
The following table provides an overview and comparison of different hand interaction approaches available within the SenseGlove Unreal Engine Plugin ecosystem when it comes to hand-interaction systems:
|
||||
|
||||
| | **Built-in?** | **Works out of the box?** | **Beginner-friendly?** | **Learning Curve** | **Featureful** | **Customizable?** | **Supports Custom Gestures?** |
|
||||
|-------------------------------------|---------------|------------------------------------|--------------------------------------------------|-----------------------------|--------------------------------------------------------------------------|-----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| **SGPawn** | ✅ Yes | ✅ Yes | ✅ Most beginer-friendly | ✅ Very easy | ⚠️ Very basic | ⚠️ Very limited | ❌ Not yet, maybe added in the future |
|
||||
| **SenseGlove OpenXR** | ✅ Yes | ❌ Requires Blueprint or C++ coding | ✅ Requires a few hours of watching tutotrials | ✅ Moderate | ❌ You need to develop your own features and functionalities from scratch | ✅ Your imagination, creativity, and skill level are your limits | ✅ You have to implement your own recognition logic; example pinch detection algorithm is taught in the tutorial series |
|
||||
| **VR Expansion Plugin** | ❌ No | ⚠️ Partially – requires setup | ❌ Best suited for intermediate or advanced users | ⚠️ Steep | ✅ Diverse features and functionalities | ✅ Highly customizable | ✅ Via custom logic |
|
||||
| **Other OpenXR-compatible Plugins** | ❌ No | ❓ Check their documentation | ❓ Check their documentation | ❓ Check their documentation | ❓ Check their documentation | ❓ Check their documentation | ❓ Check their documentation |
|
||||
|
||||
## Going Beyond SGPawn
|
||||
|
||||
In the following sections, we will cover:
|
||||
|
||||
- [**The Puppeteer (Controller) / Puppet (Pawn) Architecture**](./sgpawn-events-puppeteer-puppet-architecture/): how to customize and control `SGPawn` through events.
|
||||
- [**SGHandTrackerComponent**](./sghandtrackercomponent/): how to obtain and consume SenseGlove hand-tracking data, the easy way.
|
||||
- [**SGHapticsComponent**](./sghapticscomponent/): how to add haptic feedback to your own or third-party hand interaction systems.
|
||||
|
||||
We have also covered more in-depth and advanced topics in other parts of this handbook, available in the following sections:
|
||||
|
||||
- [**OpenXR**](../advanced-topics/openxr/): an introduction to OpenXR fundamentals in Unreal Engine.
|
||||
- [**Consuming FXRHandTrackingState**](../advanced-topics/openxr/consuming-fxrhandtrackingstate/): explains the data layout of Unreal Engine’s `FXRHandTrackingState`.
|
||||
- [**Blueprint**](../advanced-topics/openxr/consuming-fxrhandtrackingstate/blueprint.md): demonstrates how to use `FXRHandTrackingState` data and render a debug hand in Blueprint.
|
||||
- [**C++**](../advanced-topics/openxr/consuming-fxrhandtrackingstate/cpp.md): demonstrates how to use `FXRHandTrackingState` data and render a debug hand in C++.
|
||||
- [**Third-Party Integrations**](../advanced-topics/openxr/third-party-integrations/): provides a sample Unreal Engine `5.4` project demonstrating how to integrate SenseGlove with the VR Expansion (VRE) Plugin.
|
||||
- [**Third-Party Tutorials**](../advanced-topics/openxr/third-party-tutorials/):— a tutorial series that guides you from beginner to advanced level in using `FXRHandTrackingState` to build your own hand interaction system by animating virtual hand meshes.
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
# SGHandTrackerComponent
|
||||
|
||||
Since [`v2.1.0`](../../../appendix/changelog.html#210---2024-08-16), the first version to introduce OpenXR support, the **SenseGlove Unreal Engine Plugin** has provided a convenient way to retrieve `FXRHandTrackingState` for SenseGlove devices. This eliminated the need to manually calculate and apply [SenseGlove wrist-tracker settings and offsets](../../../plugin-configuration/plugin-settings/tracking/wrist-tracking/), or to fetch the `Project Settings > SenseGlove > Tracking Settings > Wrist Tracking Settings` and pass them to `GetWristLocation()` in an additional step, [as described in the relevant documentation](../openxr).
|
||||
|
||||
`SGHandTrackerComponent` simplifies this process even further by abstracting all of that away entirely in a high-level manner:
|
||||
|
||||
1. Simply add this component to your Pawn class (or any actor that requires hand-tracking data).
|
||||
2. Configure and adjust its properties.
|
||||
3. Retrieve the tracking data with a single function call when needed.
|
||||
|
||||
The SenseGlove UE Plugin automatically handles all required settings and offset calculations for your positional tracking hardware, regardless of whether you are using pure hand tracking or a SenseGlove device. It also provides an optional debug hand for free, allowing you to visualize the hand-tracking data instantly, without writing a single line of code.
|
||||
|
||||
## Adding the Component to Your Actors
|
||||
|
||||
Adding `SGHandTrackerComponent` is straightforward. In the `Components` panel, click the `Add` button and locate it under the `SenseGlove` section:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Blueprint Properties
|
||||
|
||||
`SGHandTrackerComponent` exposes the following properties through the `Details` panel in Unreal’s Blueprint Editor:
|
||||
|
||||

|
||||
|
||||
- `Right`: If enabled, the component tracks and provides hand-tracking data for the **right** hand. If disabled, it tracks the **left** hand instead.
|
||||
- `Visualize`: If enabled, the component visualizes the hand-tracking data by rendering a debug hand. The appearance of this debug hand can be further customized, as shown below.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## C++ and Blueprint Functions
|
||||
|
||||
`SGHandTrackerComponent` 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 IsVisualized() const
|
||||
{
|
||||
return bVisualize;
|
||||
}
|
||||
|
||||
FORCEINLINE void SetVisualize(const bool bInVisualize)
|
||||
{
|
||||
bVisualize = bInVisualize;
|
||||
}
|
||||
|
||||
public:
|
||||
const FXRHandTrackingState& GetHandTrackingState() const;
|
||||
```
|
||||
|
||||
The same set of functions are also exposed to Blueprint:
|
||||
|
||||

|
||||
|
||||
## GetHandTrackingState
|
||||
|
||||
The most important function accessible via `SGHandTrackerComponent` is `GetHandTrackingState()`:
|
||||
|
||||

|
||||
|
||||
This function returns a snapshot of the OpenXR hand-tracking data as an `FXRHandTrackingState` struct.
|
||||
|
||||
For more details on [what this data contains and how to use it, please refer to the Consuming FXRHandTrackingState section](../../../advanced-topics/openxr/consuming-fxrhandtrackingstate/).
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+574
@@ -0,0 +1,574 @@
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Blueprint Properties
|
||||
|
||||
`SGHapticsComponent` exposes the following properties through the `Details` panel in Unreal’s Blueprint Editor:
|
||||
|
||||

|
||||
|
||||
- `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<float>& Levels01);
|
||||
|
||||
/**
|
||||
* Set the Force-Feedback value of a particular finger to a specific level </summary>
|
||||
*
|
||||
* @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<float>& 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:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Quick Blueprint Functions Reference
|
||||
|
||||
Here is a brief at-a-glance reference of all `SGHapticsComponent` Blueprint functions related to haptic feedback.
|
||||
|
||||
### Stop Haptics
|
||||
|
||||
Stops **all active haptic effects** currently playing on the glove.
|
||||
|
||||
This includes:
|
||||
|
||||
- Vibrations.
|
||||
- Force-feedback (FFB).
|
||||
- Wrist-squeeze.
|
||||
- Any queued but unsent haptic commands.
|
||||
|
||||

|
||||
|
||||
**Typical Use Cases:**
|
||||
|
||||
- Resetting the glove at the end of a simulation.
|
||||
- Restarting a level.
|
||||
- Emergency stop logic.
|
||||
- Cleaning up when disabling an actor.
|
||||
|
||||
**Returns:**
|
||||
|
||||
This Blueprint node does not return a value.
|
||||
|
||||
### Stop Vibrations
|
||||
|
||||
Stops only **vibration feedback**, without affecting:
|
||||
|
||||
- Force-feedback
|
||||
- Wrist-squeeze
|
||||
|
||||

|
||||
|
||||
**Typical Use Cases:**
|
||||
|
||||
It is useful for example if you want to keep finger resistance active while stopping tactile feedback.
|
||||
|
||||
**Returns:**
|
||||
|
||||
This Blueprint node does not return a value.
|
||||
|
||||
### Send Haptics
|
||||
|
||||
Compiles all currently queued haptic commands and sends them to the glove.
|
||||
|
||||

|
||||
|
||||
The component works using a **queue-based system**:
|
||||
|
||||
1. You queue multiple commands (Force-feedback, Vibro, Wrist, etc..)
|
||||
2. You call **Send Haptics**.
|
||||
3. Everything is compiled into one device message.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: Indicates message has been successfully sent to SenseCom.
|
||||
- `false`: Failed to send haptics.
|
||||
|
||||
> [!CAUTION]
|
||||
> Avoid calling `Send Haptics` repeatedly in rapid succession.
|
||||
>
|
||||
> For optimal performance, queue all required haptic commands first
|
||||
> (Force-Feedback, Vibro, Wrist, etc.), then call `Send Haptics`
|
||||
> **once per logical update cycle**.
|
||||
>
|
||||
> Continuously queueing commands and flushing them every frame (or multiple
|
||||
> times per frame) increases device communication frequency and computational
|
||||
> overhead. It may also cause Bluetooth instability and, in extreme cases, lead
|
||||
> to the glove disconnecting.
|
||||
>
|
||||
> Instead, batch multiple haptic updates together and send them in a single
|
||||
> compiled message whenever possible. This reduces processing cost, lowers
|
||||
> communication load, and results in more stable and efficient haptic
|
||||
> performance.
|
||||
|
||||
### Supports Custom Waveform
|
||||
|
||||
Checks whether the glove supports **custom waveform vibration** at a specific location.
|
||||
|
||||

|
||||
|
||||
**Parameters**:
|
||||
|
||||
- **At Location:** The vibration location to test (e.g., Thumb Tip, Index Tip, Palm Index Side, etc.).
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: Custom waveform is supported.
|
||||
- `false`: Not supported at this location.
|
||||
|
||||
You can call this before using `Send Custom Waveform` to see if your glove model at the specified location supports vibration.
|
||||
|
||||
### Send Custom Waveform
|
||||
|
||||
Sends a **custom vibration waveform** to a specific haptic location.
|
||||
|
||||

|
||||
|
||||
This function has **three overloads** in C++ and is exposed accordingly in Blueprint.
|
||||
|
||||
#### 1) Send a Custom Waveform Asset
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **OutWaveform:** A predefined waveform asset that allows you to configure additional custom waveform parameters not available in the other two overloads, giving you more fine-grained control over the vibration’s behavior and timing.
|
||||
|
||||
| Name | Unit | Range | Description |
|
||||
|------------------------|-----------|--------------|-----------------------------------------------------------------------------|
|
||||
| Amplitude | | 0.0 … 1.0 | Vibration intensity |
|
||||
| Start Frequency | Hz | 10 … 500 | Vibration Frequency at the start of the vibration |
|
||||
| End Frequency | Hz | 10 … 500 | Vibration Frequency at the end of the vibration |
|
||||
| Attack Time | s | 0.0 … 1.0 | Time to reach from 0.0 to Amplitude |
|
||||
| Sustain Time | s | 0.0 … 1.0 | Time for which the signal will stay at Amplitude |
|
||||
| Decay Time | s | 0.0 … 1.0 | Time to reach from Amplitude down to 0.0. |
|
||||
| Pause Time | s | 0.0 … 1.0 | Time between each vibration, when repeating the waveform. |
|
||||
| Repeat Amount | | 1 .. 100 | How often the waveform is repeated before stopping. |
|
||||
| Infinite | | True / False | If true, the glove will keep playing this waveform until a new one is played. |
|
||||
| Waveform Type | EWaveType | 0 .. 5 | The shape of the waveform: Sine / Square / SawUp / SawDown / Triangle / Noise. |
|
||||
| FrequencySwitchTime* | | 0.0 … 1.0 | At this position in the waveform (0.0 being start, 1.0 being the end), we start multiply the Frequency by FrequencySwitchFactor |
|
||||
| FrequencySwitchFactor* | | 1.0 .. 3.0 | How much to multiply the frequency by, after FrequencySwitchTime has passed. |
|
||||
|
||||
- **Location:** Where to play the waveform.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If command successfully sent.
|
||||
- `false`: If it fails.
|
||||
|
||||
#### 2) Send Amplitude + Duration
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Amplitude:** Vibration strength (0.0 – 1.0).
|
||||
- **Duration:** Duration in seconds.
|
||||
- **Location:** Target haptic location.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If command successfully sent.
|
||||
- `false`: If it fails.
|
||||
|
||||
#### 3) Send Amplitude + Duration + Frequency
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Amplitude** — Vibration strength (0.0 – 1.0).
|
||||
- **Duration** — Duration in seconds.
|
||||
- **Frequency** — Vibration frequency in Hz.
|
||||
- **Location** — Target haptic location.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If command successfully sent.
|
||||
- `false`: If it fails.
|
||||
|
||||
### Queue Force Feedback Levels
|
||||
|
||||
Queues force-feedback levels for **all fingers** at once.
|
||||
|
||||

|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Levels 01:** Array containing the Force-Feedback levels between `0.0` (no FFB) to `1.0` (full FFB); ordered from **Thumb → Index → Middle → Ring → Pinky**.
|
||||
|
||||
> [!NOTE]
|
||||
> Force-feedback value behavior:
|
||||
>
|
||||
> - `0.0` = No resistance.
|
||||
> - `1.0` = Full resistance.
|
||||
> - Values `< 0.0` are ignored.
|
||||
> - Devices that only support on/off FFB treat any value > `0.0` as full force.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If queued successfully.
|
||||
- `false`: If it fails.
|
||||
|
||||
### Queue Force Feedback Level
|
||||
|
||||
Queues force-feedback on a **particular finger** to a specific level.
|
||||
|
||||

|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Finger:** Index of the finger; indexed from **Thumb → Index → Middle → Ring → Pinky**.
|
||||
- **Level 01:** Value clamped between `0.0` (no FFB) to `1.0` (full FFB).
|
||||
|
||||
> [!NOTE]
|
||||
> Force-feedback value behavior:
|
||||
>
|
||||
> - `0.0` = No resistance.
|
||||
> - `1.0` = Full resistance.
|
||||
> - Values `< 0.0` are ignored.
|
||||
> - Devices that only support on/off FFB treat any value > `0.0` as full force.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If queued successfully.
|
||||
- `false`: If it fails.
|
||||
|
||||
### Queue Vibro Levels
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Legacy Function – Use Custom Waveforms Instead**
|
||||
>
|
||||
> `Queue Vibro Levels` is retained for backward compatibility with older API
|
||||
> releases.
|
||||
>
|
||||
> Internally, it delegates to `Send Custom Waveform`, which is the recommended
|
||||
> method for applying vibrotactile feedback.
|
||||
>
|
||||
> For new projects, prefer `Send Custom Waveform`, as it provides more
|
||||
> fine-grained control over amplitude, frequency, timing, and waveform shaping.
|
||||
|
||||
Queues continuous vibrotactile levels for **all fingers** at once to a set amplitude.
|
||||
|
||||

|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Levels 01:** Array containing the vibro levels between `0.0` (no vibration) to `1.0` (full vibration); ordered from **Thumb → Index → Middle → Ring → Pinky**.
|
||||
|
||||
> [!NOTE]
|
||||
> Force-feedback value behavior:
|
||||
>
|
||||
> - `0.0` = No vibration.
|
||||
> - `1.0` = Full vibration.
|
||||
> - Values `< 0.0` are ignored.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If queued successfully.
|
||||
- `false`: If it fails.
|
||||
|
||||
### Queue Vibro Level
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Legacy Function – Use Custom Waveforms Instead**
|
||||
>
|
||||
> `Queue Vibro Level` is retained for backward compatibility with older API
|
||||
> releases.
|
||||
>
|
||||
> Internally, it delegates to `Send Custom Waveform`, which is the recommended
|
||||
> method for applying vibrotactile feedback.
|
||||
>
|
||||
> For new projects, prefer `Send Custom Waveform`, as it provides more
|
||||
> fine-grained control over amplitude, frequency, timing, and waveform shaping.
|
||||
|
||||
Queues continuous vibration at a **specific location** to a set amplitude.
|
||||
|
||||

|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Location:** Target location to apply vibration.
|
||||
- **Level01 (float):** Value clamped between `0.0` (no vibration) to `1.0` (full vibration).
|
||||
|
||||
> [!NOTE]
|
||||
> Force-feedback value behavior:
|
||||
>
|
||||
> - `0.0` = No vibration.
|
||||
> - `1.0` = Full vibration.
|
||||
> - Values `< 0.0` are ignored.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If queued successfully.
|
||||
- `false`: If it fails.
|
||||
|
||||
### Supports Wrist Squeeze
|
||||
|
||||
Checks if the connected glove supports **active wrist-squeeze feedback**.
|
||||
|
||||

|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If wrist-squeeze is supported.
|
||||
- `false`: If it's not supported.
|
||||
|
||||
### Queue Wrist Squeeze
|
||||
|
||||
Queues a wrist-squeeze feedback at the desired level, and optionally if chosen, sends it right away.
|
||||
|
||||

|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **Squeeze Level 01:** Value clamped between `0.0` (no squeeze) to `1.0` (full squeeze).
|
||||
- **Send Immediate** If set to `true`, immediately sends the command, otherwise only queues until `Send Haptics` function is called.
|
||||
|
||||
> [!NOTE]
|
||||
> Wrist-squeeze value behavior:
|
||||
>
|
||||
> - `0.0` = No squeeze.
|
||||
> - `1.0` = Full squeeze.
|
||||
|
||||
> [!CAUTION]
|
||||
> Avoid using `Send Immediate` unless absolutely necessary.
|
||||
>
|
||||
> For optimal performance, queue all haptic commands first and call the
|
||||
> `Send Haptics` function once after all commands are prepared.
|
||||
>
|
||||
> Sending commands immediately increases device communication frequency and
|
||||
> computational overhead. Batching commands using `Send Haptics` reduces
|
||||
> processing cost and improves performance.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `true`: If queued successfully.
|
||||
- `false`: If it fails.
|
||||
|
||||
## Blueprint Haptics Examples
|
||||
|
||||
Below are practical Blueprint examples demonstrating how to combine the different `SGHapticsComponent` functions into complete interaction flows.
|
||||
|
||||
### Force-Feedback Example
|
||||
|
||||
This example demonstrates:
|
||||
|
||||
- How to queue force-feedback with **full resistance on all fingers**.
|
||||
- How to flush all queued haptics (including the recently queued force-feedback) using `Send Haptics`.
|
||||
- How to stop all haptic effects after `2` seconds, if the send operation succeeds.
|
||||
|
||||

|
||||
|
||||
In this flow:
|
||||
|
||||
1. Force-feedback levels are queued for all fingers.
|
||||
2. `Send Haptics` compiles and sends the command to the glove.
|
||||
3. If successful, `Stop Haptics` is used to clear all active effects after `2` seconds.
|
||||
|
||||
### Vibrotactile Example
|
||||
|
||||
This example demonstrates:
|
||||
|
||||
- How to check if the glove at the current hand supports custom wave forms at the `Plam Pinky Side`.
|
||||
- If so, it constructs a `SGCustomWaveform` with a duration of `500` milliseconds, amplitude of `1.0` at the frequency of `180.0` (maximum vibration on Nova 2).
|
||||
- It then sets other parameters such as the `WaveType` to `Square` and the `RepeatAmount` to `10`.
|
||||
- And, finally sends the custom waveforms to the glove, which is going to stop after `10` times playing.
|
||||
|
||||
This example demonstrates:
|
||||
|
||||
- How to check whether the current glove supports **custom waveforms** at the `Palm Pinky Side` location.
|
||||
- How to construct a `SGCustomWaveform` with:
|
||||
- `Duration` → `500 ms`
|
||||
- `Amplitude` → `1.0`
|
||||
- `Frequency` → `180.0 Hz` (maximum vibration on Nova 2)
|
||||
- How to configure additional parameters such as:
|
||||
- `Wave Type` → `Square`
|
||||
- `Repeat Amount` → `10`
|
||||
- How to send the custom waveform to the glove.
|
||||
|
||||

|
||||
|
||||
The waveform will automatically stop after playing **10 repetitions**.
|
||||
|
||||
### Wrist-Squeeze Example
|
||||
|
||||
This example demonstrates:
|
||||
|
||||
- How to check whether the connected glove supports **wrist squeeze feedback**.
|
||||
- How to apply a wrist squeeze at **50% intensity**.
|
||||
- How to send the command immediately without requiring an additional `Send Haptics` call.
|
||||
|
||||

|
||||
|
||||
Because `Send Immediate` is enabled, the squeeze is transmitted instantly instead of being queued.
|
||||
|
||||
> [!CAUTION]
|
||||
> Avoid using `Send Immediate` unless absolutely necessary.
|
||||
>
|
||||
> For optimal performance, queue all haptic commands first and call the
|
||||
> `Send Haptics` function once after all commands are prepared.
|
||||
>
|
||||
> Sending commands immediately increases device communication frequency and
|
||||
> computational overhead. Batching commands using `Send Haptics` reduces
|
||||
> processing cost and improves performance.
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+594
@@ -0,0 +1,594 @@
|
||||
# SGPawn Events: The Puppeteer (Controller) / Puppet (Pawn) Architecture
|
||||
|
||||
The `SGPawn` (SenseGlove Pawn) is intentionally designed as a **data/event-driven puppet**. It detects touch, grab candidates, and hand state, but it does **not make gameplay decisions** on its own. Instead, it delegates the decisions via firing events
|
||||
|
||||
Usually these decisions are delegated to the `SGPlayerController` (or your own controller if you want to customize the behaviors), which acts as the **puppeteer** for SGPawn (the **puppet**):
|
||||
|
||||
- It registers to `SGPawn` events at the `BeginPlay` event.
|
||||
- It listens to `SGPawn` events.
|
||||
- It decides when to grab or release when certain conditions are met.
|
||||
- It applies gameplay logic.
|
||||
- It drives haptics or other responses.
|
||||
|
||||
That's [how `SGPlayerController` works under the hood](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp).
|
||||
|
||||
This separation ensures:
|
||||
|
||||
- Clean architecture.
|
||||
- Full and exnsible customization.
|
||||
- No hidden behavior inside `SGPawn`.
|
||||
- Deterministic control over interaction rules.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
SGPawn ---> Emits State Events ---> SGPlayerController decides what to do
|
||||
```
|
||||
|
||||
`SGPawn`:
|
||||
|
||||
- Tracks touch state.
|
||||
- Tracks grab candidates.
|
||||
- Tracks grabbed actors.
|
||||
- Emits events.
|
||||
|
||||
`SGPlayerController`:
|
||||
|
||||
- Subscribes to events.
|
||||
- Calls `Grab()` / `Release()`.
|
||||
- Applies custom interaction logic.
|
||||
- Updates haptics.
|
||||
|
||||
## Exposed Events
|
||||
|
||||
`SGPawn` provides the following event exposed to both C++ and Blueprint:
|
||||
|
||||
- `OnGrabStateUpdated`
|
||||
- `OnTouchStateUpdated`
|
||||
- `OnActorGrabbed`
|
||||
- `OnActorReleased`
|
||||
- `OnActorBeginTouch`
|
||||
- `OnActorEndTouch`
|
||||
|
||||

|
||||
|
||||
> [!IMPORTANT]
|
||||
> The current implementation of `SGPawn` relies on `3` grab colliders and `5`
|
||||
> touch colliders for grab and touch detection.
|
||||
|
||||

|
||||
|
||||
### On Grab State Updated Event
|
||||
|
||||
This is the **main decision event** for grabbing logic. The Pawn informs you:
|
||||
|
||||
> "Here is the current grab state. You decide what to do."
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_OneParam(ASGPawn, FGrabStateUpdatedEvent, const FSGGrabState& GrabState);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
This event is triggered only when the hand is visible and when any finger on the left or right hand, equipped with a grab collider, begins overlapping (colliding with) or ends overlapping (stops colliding with) an actor that owns an [`SGGrabComponent`](../../../getting-started/setup-grab-release-system/). So in summary it fires when the following conditions are met:
|
||||
|
||||
- The hand is **visible**.
|
||||
- Any finger (left or right hand) equipped with a **grab collider**:
|
||||
- **Begins overlapping** (starts colliding with), or
|
||||
- **Ends overlapping** (stops colliding with).
|
||||
- The overlapped actor owns an `SGGrabComponent`.
|
||||
|
||||
Subscribers to this event receive a snapshot of the `FSGGrabState` struct. At the moment the event is fired, the struct contains the following data:
|
||||
|
||||
- `Hand`: The `SGVirtualHandComponent` whose grab state was updated due to a finger beginning or ending an overlap with another actor.
|
||||
- `PreviousHandLocation`: `SGPawn` continuously records hand movement every engine tick. This field stores the hand’s location from the previous tick. It can be used to calculate object velocity or apply impulse forces when an object is thrown.
|
||||
- `HandVelocityHistory`: A history of previous hand locations, up to `SGPawn::MaxNumberOfHandVelocitySamples`. `MaxNumberOfHandVelocitySamples` is a `UPROPERTY` in `SGPawn` that defaults to `10` but can be adjusted as needed.
|
||||
- `ActorThumbCanGrab`: The actor currently overlapping with the thumb’s grab collider. If `null`, the thumb is not overlapping any grabbable actor (which means the actor has an `SGGrabComponent`).
|
||||
- `ActorIndexCanGrab`: The actor currently overlapping with the index finger’s grab collider. If `null`, the index finger is not overlapping any grabbable actor.
|
||||
- `ActorMiddleCanGrab`: The actor currently overlapping with the middle finger’s grab collider. If `null`, the middle finger is not overlapping any grabbable actor.
|
||||
- `GrabbedActor`: The actor currently being grabbed by this hand. If `null`, the hand is not grabbing anything at that moment.
|
||||
|
||||
Here is how the current `SGPlayerController` performs grab detection and instructs the `SGPawn` it controls to execute grab and release actions:
|
||||
|
||||
```cpp
|
||||
void ASGPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
ASGPawn* SGPawn{Cast<ASGPawn>(GetPawn())};
|
||||
if (!ensureAlwaysMsgf(IsValid(SGPawn), TEXT("%s"), TEXT("ERROR: invalid SenseGlove pawn!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SGPawn->OnGrabStateUpdated().AddWeakLambda(
|
||||
this, [= SG_CAPTURE_THIS](const FSGGrabState& GrabState) -> void
|
||||
{
|
||||
if (!IsValid(SGPawn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid(GrabState.Hand))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bHandVisible = GrabState.Hand->IsVisible();
|
||||
if (!bHandVisible)
|
||||
{
|
||||
if (SGPawn->IsGrabbing(GrabState.Hand))
|
||||
{
|
||||
SGPawn->Release(GrabState.Hand);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (SGPawn->IsGrabbing(GrabState.Hand))
|
||||
{
|
||||
if (!IsValid(GrabState.ActorThumbCanGrab) ||
|
||||
(GrabState.ActorIndexCanGrab != GrabState.ActorThumbCanGrab
|
||||
&& GrabState.ActorMiddleCanGrab != GrabState.ActorThumbCanGrab))
|
||||
{
|
||||
SGPawn->Release(GrabState.Hand);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SGPawn->CanGrab(GrabState.Hand, GrabState.ActorThumbCanGrab))
|
||||
{
|
||||
SGPawn->Grab(GrabState.Hand, GrabState.ActorThumbCanGrab);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In this implementation, the `SGPlayerController` listens for grab state updates and determines whether the hand should grab or release an actor based on visibility and finger overlap conditions.
|
||||
|
||||
> [!TIP]
|
||||
> Haptic feedback is also handled and enforced through `SGPlayerController`.
|
||||
> You can
|
||||
> [review the full implementation in the plguin source code for `SGPlayerController`](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp).
|
||||
>
|
||||
> In general, with the current version of the plugin, you can integrate haptic
|
||||
> feedback into your own hand interaction system in several ways:
|
||||
>
|
||||
> - The [`SGHapticsComponent`](../sghapticscomponent/) high-level approach.
|
||||
> - The SenseGlove 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).
|
||||
> - The 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.
|
||||
|
||||
### Touch State Updated Event
|
||||
|
||||
This is the **main decision event** for controlling the touch logic. The Pawn informs you:
|
||||
|
||||
> "Here is the current touch state. You decide what to do."
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_OneParam(ASGPawn, FTouchStateUpdatedEvent, const FSGTouchState& TouchState);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
This event is triggered only when the hand is visible and when any finger on the left or right hand, equipped with a grab collider, begins overlapping (colliding with) or ends overlapping (stops colliding with) an actor that owns an [`SGGrabComponent`](../../../getting-started/setup-grab-release-system/). So in summary it fires when the following conditions are met:
|
||||
|
||||
- The hand is **visible**.
|
||||
- Any finger (left or right hand) equipped with a **touch collider**:
|
||||
- **Begins overlapping** (starts colliding with), or
|
||||
- **Ends overlapping** (stops colliding with).
|
||||
- The overlapped actor owns an `SGTouchComponent`.
|
||||
|
||||
Subscribers to this event receive a snapshot of the `FSGTouchState` struct. At the moment the event is fired, the struct contains the following data:
|
||||
|
||||
- `Hand`: The `SGVirtualHandComponent` whose touch state was updated due to a finger beginning or ending an overlap with another actor.
|
||||
- `ActorThumbTouching`: The actor currently overlapping with the thumb’s touch collider. If `null`, the thumb is not overlapping any touchable actor (which means the actor has an `SGTouchComponent`).
|
||||
- `ActorIndexTouching`: The actor currently overlapping with the index’s touch collider. If `null`, the index is not overlapping any touchable actor.
|
||||
- `ActorMiddleTouching`: The actor currently overlapping with the middle’s touch collider. If `null`, the middle is not overlapping any touchable actor.
|
||||
- `ActorRingTouching`: The actor currently overlapping with the ring’s touch collider. If `null`, the ring is not overlapping any touchable actor.
|
||||
- `ActorPinkyTouching`: The actor currently overlapping with the pinky’s touch collider. If `null`, the pinky is not overlapping any touchable actor.
|
||||
|
||||
Here is how the current `SGPlayerController` performs touch detection and instructs the `SGPawn` it controls to apply haptics feedback:
|
||||
|
||||
```cpp
|
||||
void ASGPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
ASGPawn* SGPawn{Cast<ASGPawn>(GetPawn())};
|
||||
if (!ensureAlwaysMsgf(IsValid(SGPawn), TEXT("%s"), TEXT("ERROR: invalid SenseGlove pawn!")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SGPawn->OnTouchStateUpdated().AddWeakLambda(
|
||||
this, [= SG_CAPTURE_THIS](const FSGTouchState& TouchState) -> void
|
||||
{
|
||||
if (!IsValid(SGPawn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid(TouchState.Hand))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bHandVisible = TouchState.Hand->IsVisible();
|
||||
if (!bHandVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bGloveConnected = TouchState.Hand->IsGloveConnected();
|
||||
if (!bGloveConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pimpl->UpdateHapticsFeedback(TouchState);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In this implementation, the `SGPlayerController` listens for touch state updates and determines whether the haptic feedbacks should be applied to the glove on that hand, or not. This decision is determined based on various conditions such as hand visibility and finger overlap conditions. Since each fingers haptic feedback application and the type of haptic feedback is decided individually, for the sake of readability the logic has been offloaded to an `SGPlayerController`'s internal function `Pimpl->UpdateHapticsFeedback()`. For example, it applies vibrotactile feedback to eligible fingers like this:
|
||||
|
||||
In this implementation, the `SGPlayerController` listens for touch state updates and determines whether haptic feedback should be applied to the glove on that hand. This decision is based on several conditions, such as hand visibility and finger overlap states. Since each finger’s haptic feedback and feedback type are evaluated individually, the detailed logic has been offloaded to the internal `SGPlayerController` function `Pimpl->UpdateHapticsFeedback()` for readability and separation of concerns.
|
||||
|
||||
For example, vibrotactile feedback is applied to eligible fingers as follows:
|
||||
|
||||
```cpp
|
||||
void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& TouchState)
|
||||
{
|
||||
if (!IsValid(TouchState.Hand))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USGHapticGlove* Glove{TouchState.Hand->GetConnectedGlove()};
|
||||
if (!IsValid(Glove))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bGloveConnected = Glove->IsConnected();
|
||||
if (!bGloveConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// some omitted code due to irrelevance
|
||||
....
|
||||
|
||||
// Send Vibrotactile to the thumb finger if it's touching an actor...
|
||||
if (IsValid(TouchState.ActorThumbTouching))
|
||||
{
|
||||
USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorThumbTouching));
|
||||
Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::ThumbTip);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As can be seen from the above code, the `SGCustomWaveform` is constructed via a separate helper function:
|
||||
|
||||
```cpp
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
When it comes to force-feedback, the controller sends force-feedback to all fingers at once, while still constructing the force-feedback levels array via a separate function. `5` elements for `5` fingers indexed from thumb to pinky, where element `0` corresponds to the thumb, `1` to the index finger, and so on, with `4` representing the pinky; [see the `SGTouchComponent` documentation for more details](../../../getting-started/setup-touch-system/). This is how `UpdateHapticsFeedback()` sends force-feedback to the glove:
|
||||
|
||||
```cpp
|
||||
void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& TouchState)
|
||||
{
|
||||
if (!IsValid(TouchState.Hand))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USGHapticGlove* Glove{TouchState.Hand->GetConnectedGlove()};
|
||||
if (!IsValid(Glove))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bGloveConnected = Glove->IsConnected();
|
||||
if (!bGloveConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue the Force-Feedback command...
|
||||
TArray<float> ForceFeedbackLevels{
|
||||
GetForceFeedbackLevels(
|
||||
TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching,
|
||||
TouchState.ActorRingTouching, TouchState.ActorPinkyTouching)
|
||||
};
|
||||
Glove->QueueForceFeedbackLevels(MoveTemp(ForceFeedbackLevels));
|
||||
|
||||
// Send the haptics commands!
|
||||
Glove->SendHaptics();
|
||||
}
|
||||
```
|
||||
|
||||
Here is the current implementation for `GetForceFeedbackLevels()`:
|
||||
|
||||
```cpp
|
||||
TArray<float> ASGPlayerController::FImpl::GetForceFeedbackLevels(
|
||||
const AActor* ActorThumbTouching,
|
||||
const AActor* ActorIndexTouching,
|
||||
const AActor* ActorMiddleTouching,
|
||||
const AActor* ActorRingTouching,
|
||||
const AActor* ActorPinkyTouching)
|
||||
{
|
||||
const float ThumbForceFeedbackLevel = GetForceFeedbackLevel(ActorThumbTouching);
|
||||
const float IndexForceFeedbackLevel = GetForceFeedbackLevel(ActorIndexTouching);
|
||||
const float MiddleForceFeedbackLevel = GetForceFeedbackLevel(ActorMiddleTouching);
|
||||
const float RingForceFeedbackLevel = GetForceFeedbackLevel(ActorRingTouching);
|
||||
const float PinkyForceFeedbackLevel = GetForceFeedbackLevel(ActorPinkyTouching);
|
||||
|
||||
const TArray<float> ForceFeedbackLevels{
|
||||
ThumbForceFeedbackLevel,
|
||||
IndexForceFeedbackLevel,
|
||||
MiddleForceFeedbackLevel,
|
||||
RingForceFeedbackLevel,
|
||||
PinkyForceFeedbackLevel,
|
||||
};
|
||||
|
||||
return ForceFeedbackLevels;
|
||||
}
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> You can
|
||||
> [review the full implementation in the plguin source code for `SGPlayerController`](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp).
|
||||
>
|
||||
> In general, with the current version of the plugin, you can integrate haptic
|
||||
> feedback into your own hand interaction system in several ways:
|
||||
>
|
||||
> - The [`SGHapticsComponent`](../sghapticscomponent/) high-level approach.
|
||||
> - The SenseGlove 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).
|
||||
> - The SenseGlove Blueprint API:
|
||||
> [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.
|
||||
|
||||
### Actor Grabbed Event
|
||||
|
||||
This event is triggered whenever a grab is successfully performed by either the left or right hand. Subscribers to this event are notified about **which hand** performed the grab and **which grabbable actor** (an actor that owns an `SGGrabComponent`) was grabbed.
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_TwoParams(ASGPawn, FActorGrabbedEvent,
|
||||
const USGVirtualHandComponent* Hand,
|
||||
const AActor* Actor);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
### Actor Released Event
|
||||
|
||||
This event is triggered whenever a release is successfully performed by either the left or right hand. Subscribers to this event are notified about **which hand** performed the release and **which grabbable actor** (an actor that owns an `SGGrabComponent`) was released.
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_TwoParams(ASGPawn, FActorReleasedEvent,
|
||||
const USGVirtualHandComponent* Hand,
|
||||
const AActor* Actor);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
### Actor Begin Touch Event
|
||||
|
||||
This event is triggered whenever **any finger** on the left or right hand comes into contact with another actor. Subscribers to this event are notified about **which hand** initiated the overlap and **which touchable actor** (an actor that owns an `SGTouchComponent`) was touched.
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_TwoParams(ASGPawn, FActorBeginTouchEvent,
|
||||
const USGVirtualHandComponent* Hand,
|
||||
const AActor* Actor);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
### Actor End Touch Event
|
||||
|
||||
This event is triggered whenever **any finger** on the left or right hand ends contact with another actor that was previously touched by that finger. Subscribers to this event are notified about **which hand**'s finger ended the overlap and **which touchable actor** (an actor that owns an `SGTouchComponent`) is no longer being touched by that finger.
|
||||
|
||||
It is defined in C++ like this:
|
||||
|
||||
```cpp
|
||||
DECLARE_EVENT_TwoParams(ASGPawn, FActorEndTouchEvent,
|
||||
const USGVirtualHandComponent* Hand,
|
||||
const AActor* Actor);
|
||||
```
|
||||
|
||||
In Blueprint, the event appears as shown below:
|
||||
|
||||

|
||||
|
||||
## SGPawn Grab/Realase-Related Functions
|
||||
|
||||
In addition to the events described above, `SGPawn` provides a set of helper functions related to grabbing and releasing actors.
|
||||
|
||||
These functions allow you to:
|
||||
|
||||
- Check whether a specific hand can grab a given actor.
|
||||
- Determine whether a hand is currently grabbing an actor.
|
||||
- Retrieve the currently grabbed actor via an output parameter.
|
||||
- Trigger grab and release actions programmatically for either hand.
|
||||
|
||||
These helper functions are exposed to both [C++](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h) and [Blueprint](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Source/SenseGloveKismet/Public/SGKismet/SGPawnKismetLibrary.h):
|
||||
|
||||
```cpp
|
||||
public:
|
||||
FORCEINLINE bool CanLeftHandGrab(const AActor* Actor) const
|
||||
{
|
||||
return !IsLeftHandGrabbing() && ((IsValid(Actor) && Actor == LeftHandGrabState.ActorThumbCanGrab)
|
||||
&& (Actor == LeftHandGrabState.ActorIndexCanGrab || Actor == LeftHandGrabState.ActorMiddleCanGrab));
|
||||
}
|
||||
|
||||
FORCEINLINE bool IsLeftHandGrabbing(const AActor* Actor) const
|
||||
{
|
||||
return IsValid(Actor) && LeftHandGrabState.GrabbedActor == Actor;
|
||||
}
|
||||
|
||||
bool IsLeftHandGrabbing(AActor*& OutActor) const;
|
||||
|
||||
FORCEINLINE bool IsLeftHandGrabbing() const
|
||||
{
|
||||
return IsValid(LeftHandGrabState.GrabbedActor);
|
||||
}
|
||||
|
||||
FORCEINLINE bool CanRightHandGrab(const AActor* Actor) const
|
||||
{
|
||||
return !IsRightHandGrabbing() && ((IsValid(Actor) && Actor == RightHandGrabState.ActorThumbCanGrab)
|
||||
&& (Actor == RightHandGrabState.ActorIndexCanGrab || Actor == RightHandGrabState.ActorMiddleCanGrab));
|
||||
}
|
||||
|
||||
FORCEINLINE bool IsRightHandGrabbing(const AActor* Actor) const
|
||||
{
|
||||
return IsValid(Actor) && RightHandGrabState.GrabbedActor == Actor;
|
||||
}
|
||||
|
||||
FORCEINLINE bool IsRightHandGrabbing() const
|
||||
{
|
||||
return IsValid(RightHandGrabState.GrabbedActor);
|
||||
}
|
||||
|
||||
bool IsRightHandGrabbing(AActor*& OutActor) const;
|
||||
|
||||
FORCEINLINE bool CanGrab(const USGVirtualHandComponent* Hand, const AActor* Actor) const
|
||||
{
|
||||
return Hand == HandRight ? CanRightHandGrab(Actor) : CanLeftHandGrab(Actor);
|
||||
}
|
||||
|
||||
FORCEINLINE bool IsGrabbing(const USGVirtualHandComponent* Hand, const AActor* Actor) const
|
||||
{
|
||||
return Hand == HandRight ? IsRightHandGrabbing(Actor) : IsLeftHandGrabbing(Actor);
|
||||
}
|
||||
|
||||
bool IsGrabbing(const USGVirtualHandComponent* Hand, AActor*& OutActor) const;
|
||||
|
||||
FORCEINLINE bool IsGrabbing(const USGVirtualHandComponent* Hand) const
|
||||
{
|
||||
return Hand == HandRight ? IsRightHandGrabbing() : IsLeftHandGrabbing();
|
||||
}
|
||||
|
||||
public:
|
||||
FORCEINLINE void GrabLeft(AActor* Actor)
|
||||
{
|
||||
Grab(HandLeft, Actor);
|
||||
}
|
||||
|
||||
FORCEINLINE void GrabRight(AActor* Actor)
|
||||
{
|
||||
Grab(HandRight, Actor);
|
||||
}
|
||||
|
||||
void Grab(USGVirtualHandComponent* Hand, AActor* Actor);
|
||||
|
||||
void ReleaseLeft();
|
||||
|
||||
void ReleaseRight();
|
||||
|
||||
FORCEINLINE void Release(const USGVirtualHandComponent* Hand)
|
||||
{
|
||||
return Hand == HandRight ? ReleaseRight() : ReleaseLeft();
|
||||
}
|
||||
```
|
||||
|
||||
The same functions can be searched within the Blueprint Editor or accessed under the `SenseGlove > Game Framework > Pawn` category:
|
||||
|
||||

|
||||
|
||||

|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -5,6 +5,211 @@ 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).
|
||||
|
||||
## [2.9.1] - 2026-07-17
|
||||
|
||||
This patch release addresses `v2.9.x` build issues on Microsoft Windows and MSVC.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Attempt at fixing build issues in certain MSVC configurations that also affects Fab Store submissions due to: `MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance fatal error C1900: Il mismatch between 'P1' version '20250730' and 'P2' version '20240319'; LINK : fatal error LNK1257: code generation failed`.
|
||||
- Fix `ThirdParty/SGBleThirdPartyLibs`'s module `.Build.cs` warnings.
|
||||
|
||||
### Changed
|
||||
|
||||
- Bumped the SenseGlove libraries to `v2.400.3-28889647d`.
|
||||
- Updated the [Microsoft Windows and GNU/Linux binary distribution of **SenseGlove FakeShm Module Tuning Utilities**](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Tuning&version=GBmaster&_a=contents) to `v2.400.3-28889647d`.
|
||||
|
||||
## [2.9.0] - 2026-07-13
|
||||
|
||||
This minor release introduces a major IPC overhaul, significant inter-process communication performance improvements and bug fixes, along with Unreal Engine `5.8` and Microsoft Visual Studio 2026 support. This release also introduces breaking Blueprint API changes.
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for Unreal Engine `5.8`.
|
||||
- Added support for Microsoft Visual Studio 2026 to allow building UE `5.7` and `5.8` projects with the latest Visual Studio version.
|
||||
- Added support for MSVC Toolsets `v145: 14.50.35717` (UE `5.8`) and `v143: 14.44.35207` (UE `5.8` and `5.7`). Previously, the plugin shipped only `v143: 14.38.33130`, which has now been declared out of support by Microsoft. `14.38.33130` binaries are still shipped, as it remains Epic Games' preferred MSVC toolchain for building UE `5.5` and `5.6` on Microsoft Windows.
|
||||
- Added support for the new SenseGlove inter-process communication module `SGFakeShm`, enabling communication with SenseCom via `IPC` (named pipes on Microsoft Windows and Unix Domain Sockets on GNU/Linux) and `TCP over loopback/localhost` mechanisms, in addition to the previously supported `Boost::SharedMem`. This release also reduces `Boost::SharedMem` latency and increases data throughput, which should help improve frame rate and overall performance. Please note that communication over SharedMem is still blocking (non-async) in nature, so in enterprise environments where security software typically restricts `SharedMem` access, or in cases of conflicts with other applications, we recommend using either `IPC` or `TCP`. These transports have been implemented from scratch using an asynchronous, non-blocking architecture, although the new SenseCom also supports optional blocking communication modes and additional configuration tweaks if desired. `IPC` and `TCP over loopback` are also typically less restricted than `SharedMem` in enterprise environments and generally provide significantly lower latency. This helps maintain smooth frame rates by decoupling rendering and data exchange with SenseCom. Furthermore, the new SenseGlove UE plugin and SenseCom remain backward compatible with older releases of SenseCom and the UE plugin without requiring any specific configuration. They can automatically detect and fall back to the legacy `SharedMem` transport.
|
||||
- Added [nanomsg/nng](https://github.com/nanomsg/nng) third-party library binaries for Microsoft Windows and GNU/Linux. This is a requirement of the `SGFakeShm` module.
|
||||
- Added [google/flatbuffers](https://github.com/google/flatbuffers) third-party library binaries for Microsoft Windows and GNU/Linux. This is a requirement of the `SGFakeShm` module.
|
||||
- Added [biojppm/rapidyaml](https://github.com/biojppm/rapidyaml) third-party library binaries for Microsoft Windows and GNU/Linux. This is a requirement of the `SGFakeShm` module.
|
||||
- Added `SGCore`'s `Library::IsInitialized()`, `Library::Initialize()`, and `Library::Terminate()` wrapper functions to SenseGlove UE C++ and Blueprint API that are required for the underlying `SGCore` and `SGFakeShm` initialization and termination and automatically initialize or terminate the library at the relevant module's startup or shutdown, eliminating the need for manual initialization or termination by plugin users.
|
||||
- Added third-party module `SGFakeShmThirdPartyLibs`.
|
||||
- Added third-party module `SGFlatBuffersThirdPartyLibs`.
|
||||
- Added third-party module `SGNngThirdPartyLibs`.
|
||||
- Added third-party module `SGRapidYamlThirdPartyLibs`.
|
||||
- Added `SGBuildHacks/SGInclude_Core_DeviceTypes`.
|
||||
- Added missing `ESGBackendType::AndroidStrings` enumerator.
|
||||
- Added missing SenseGlove API Blueprint function `USGLibraryKismetLibrary::GetBackendType()`.
|
||||
- Added the [Microsoft Windows and GNU/Linux binary distribution of **SenseGlove FakeShm Module Tuning Utilities**](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal?path=/Tuning&version=GBmaster&_a=contents) to the SenseGlove Unreal Engine Plugin repository on Microsoft Azure repositories. These binaries are not part of the [official plugin release via the Fab Store](https://www.fab.com/listings/62bcdd2a-0bb1-4f34-b7b8-c5e7c9313099).
|
||||
|
||||
### Fixed
|
||||
|
||||
- This release ships a revamped version of the SenseGlove libraries with numerous bug fixes, multithreading improvements, SharedMem performance enhancements, BTSerial fixes, and many other improvements.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGDeviceKismetLibrary::GetHapticChannelCount()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGDeviceKismetLibrary::GetHapticChannelCount_CT()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::EndCalibration()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::GetCalibrationInstruction()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::GetCalibrationState()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::GetHandAngles()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::ResetCalibration()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGHapticGloveKismetLibrary::SetHandGeometry()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGNova2GloveKismetLibrary::GetHandAngles()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGNovaGloveKismetLibrary::GetHandAngles()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGSenseGloveKismetLibrary::CalculateHandPose()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGSenseGloveVarsKismetLibrary::GenerateGloveModel_HV_FV_SFV_bRH()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGSenseGloveVarsKismetLibrary::GenerateGloveModel_HV_FV_SFV_bRH_CI()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetNovaToWristRotationOffset_FQuat()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetNovaToWristRotationOffset_FRotator()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetRightNovaQuestProToAttachRotation_FQuat()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetRightNovaViveFocus3ToAttachRotation_FRotator()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetSenseGloveTrackerMountOffset_H_bRH_OutPO_FQuat_OutRO()`.
|
||||
- Fix the wrong Blueprint `DisplayName` UPROPERTY specifier for `USGTrackingKismetLibrary::GetSenseGloveTrackerMountOffset_H_bRH_OutPO_FRotator_OutRO()`.
|
||||
- Fix the wrong Blueprint `Category` UPROPERTY specifier for `USGNova2GloveKismetLibrary::SendHaptics()`.
|
||||
- Fix the wrong Blueprint `Category` UPROPERTY specifier for `USGNovaGloveKismetLibrary::SendHaptics()`.
|
||||
- Fix `USGHandInterpolatorKismetLibrary::InterpolateHandAngles()` Blueprint access by making it static and removing its erroneous `const` qualifier.
|
||||
- Fix `SGCustomWaveformKismetLibrary::GetWaveType()` Blueprint access by making it static.
|
||||
- Fix `SGDeviceKismetLibrary::FirmwareNewerThan_FM_FS_bI()` Blueprint access by making it static.
|
||||
- Fix `SGInterpolationSetKismetLibrary::GetX1()` Blueprint access by making it static.
|
||||
- Some code quality and build time optimizations by removing redundant headers and relying on more forward declarations where/when possible.
|
||||
- Additional minor fixes and improvements that may not be listed here.
|
||||
|
||||
### Changed
|
||||
|
||||
- Bumped the SenseGlove libraries to `v2.400.1-302a8aa67`. This release introduces new Inter-Process Communication (IPC) capabilities and delivers significant improvements and bug fixes, particularly in multithreaded code. The new `FakeShm` back-end significantly improves the performance of the existing `SharedMem` transport while retaining compatibility with older releases of SenseCom. It also introduces two new transports (communication mechanisms), namely `IPC` and `TCP`, on both GNU/Linux and Microsoft Windows.
|
||||
- The `ThirdParty/lib/windows` directory structure has been revamped to support multiple MSVC toolsets/toolchains and the distribution of binaries built for every MSVC configuration supported by Epic Games on supported engine/plugin versions.
|
||||
- The following third-party binaries/libraries are no longer distributed or linked on Android: `SGBleThirdPartyLibs`, `SGConnectShmThirdPartyLibs`, `SGCoreShmThirdPartyLibs`, `SGSerialThirdPartyLibs`, and `SGWjwwoodSerialThirdPartyLibs`. This helps keep the plugin and generated Android APKs smaller while also reducing linking and build times.
|
||||
- `SenseGloveConnectImpl` module exported C functions have been renamed from `SGConnect_Init()`, `SGConnect_Dispose()`, and `SGConnect_GetLibraryVersion()` to `SGConnectImpl_Init()`, `SGConnectImpl_Dispose()`, and `SGConnectImpl_GetLibraryVersion()` respectively, as the underlying `SGConnect` library exports functions with the exact same names. This change resolves link-time issues caused by duplicate symbols. This should not affect the existing SenseGlove Blueprint or C++ APIs, as these functions are used internally.
|
||||
- Changed `USGBasicHandModelKismetLibrary::SetStartJointPosition_uint8_F_P()` from `BlueprintPure` to `BlueprintCallable`.
|
||||
- Changed `USGThresholdCommandKismetLibrary::GetActiveFingers()` from `BlueprintCallable` to `BlueprintPure`.
|
||||
- Renamed `USGHandLayerKismetLibrary::GetWristLocation_bRH_FQuat_RR_TH_OutWP_OutWR()` to `USGHandLayerKismetLibrary::GetWristLocation_bRH_RP_FQuat_RR_TH_OutWP_OutWR()` to adhere to the standard SenseGlove name mangling.
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed SenseGlove library binaries built with Epic Native/Cross Toolchains `v22` from the distributed third-party binaries shipped with the plugin. Support for the `v22` toolchain, which was previously used to build UE `5.3` and `5.4` Linux dependencies, has been dropped since plugin release `v2.8.0`.
|
||||
- Removed `btleplug` Android dependencies, as the SenseGlove libraries for Android no longer rely on `btleplug` for BLE communication.
|
||||
- Removed invalid `WorldContext` Blueprint metadata from `USGThresholdCommandKismetLibrary::GetFingerActive()`.
|
||||
- Removed invalid `WorldContext` Blueprint metadata from `USGThresholdCommandKismetLibrary::GetThreshold()`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- This is the last minor release to support Unreal Engine `5.5` and its support will be removed from the next minor or major releases.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Added the [SenseCom > FakeShm Module Tuning](../getting-started/sensecom/fakeshm-tuning/) documentation section.
|
||||
- List a new [OpenXR Hand Interactions System From Scratch (Pure Blueprint)](https://gitlab.com/senseglove-unreal-engine-public/senseglove-unreal-openxr-hand-from-scratch-bp-demo) demo and its companion [Video Documentation: Building OpenXR Hand Interactions from Scratch in Unreal Engine](https://www.youtube.com/watch?v=J18c99vl6dE) inside the [Extra Resources](../appendix/extra-resources.html#developer-spotlight-demos) section.
|
||||
- Added [The c4core C++ Library Licence](../license/c4core-library.html) section.
|
||||
- Added [The c4project CMake Scripts Licence](../license/c4project-cmake-scripts.html) section.
|
||||
- Added [The Debug Break Library Licence](../license/debugbreak-library.html) section.
|
||||
- Added [The fast_float Number Parsing Library Licence](../license/fast-float-number-parsing-library.html) section.
|
||||
- Added [FlatBuffers Memory Efficient Serialization Library Licence](../license/flatbuffers-serialization-library.html) section.
|
||||
- Added [nanomsg-next-generation Lightweight Brokerless Messaging Library License](../license/nng-messaging-library.html) section.
|
||||
- Added [The Rapid YAML Parsing and Emitting Library Licence](../license/rapidyaml-parsing-emitting-library.html) section.
|
||||
- Fixed a typo in the URL of the [Loguru Logging Library License](../license/loguru-logging-library.html) section.
|
||||
- Updated the [SGBLE and SGBLExx Rust Dependencies License](../license/sgble-sgblexx-rust-dependencies.html) section.
|
||||
- Updated the [Plugin Directory Structure](./directory-structure.html) section to reflect the recent changes.
|
||||
- Updated the [Platform Support Matrix](./platform-support-matrix.html) section to reflect the recent changes.
|
||||
- Additional minor fixes and improvements that may not be listed here.
|
||||
- Fix `USGBetaDevice::Parse()` and `USGBetaDeviceKismetLibrary::Parse()` documentation.
|
||||
- Fix swapped in-header documentation for `USGHapticGlove::ResetCalibration()` and `USGHapticGlove::EndCalibration()`.
|
||||
- Fix incorrect class description in `USGSenseGloveSensorNormalizer` and `USGSenseGloveSensorNormalizerKismetLibrary`.
|
||||
- Bumped [mdBook](https://rust-lang.github.io/mdBook/) crate to `v0.5.4`
|
||||
- Bumped the [Michael-F-Bryan/mdbook-epub](https://github.com/Michael-F-Bryan/mdbook-epub) crate to `v0.5.4`.
|
||||
- Bumped [HollowMan6/mdbook-pdf](https://github.com/HollowMan6/mdbook-pdf) to `v0.1.13`.
|
||||
- Bumped Handbook's Rust toolchain to `v1.96.1`.
|
||||
|
||||
## [2.8.1] - 2026-05-11
|
||||
|
||||
This patch release brings minor non-breaking improvements for Blueprint and the Unreal Editor.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix inconsistent Unreal Engine Blueprint `UFUNCTION` category paths in `Nova` and `Nova 2` Kismet libraries.
|
||||
|
||||
## [2.8.0] - 2026-02-24
|
||||
|
||||
This minor release introduces high-level haptics and hand-tracking components, simplifies OpenXR integration around `FXRHandTrackingState`, and removes legacy `FXRMotionControllerData` APIs. It also includes ABI-breaking changes, Unreal Engine deprecations, documentation expansions, and various internal improvements.
|
||||
|
||||
### Added
|
||||
|
||||
- Added a `USGHapticsComponent` to allow sending a variety of haptic feedbacks (force-feedback, vibrotactile, custom waveforms, and wrist-squeeze) 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.
|
||||
- Added a new `FSGHandLayer::GetWristLocation()` overload to allow passing `FRotator`s instead of `FQuat`s as input or output parameters.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix some copyright notices with wrong copyright owner. These propably has happend during bulk replaces with class or struct names.
|
||||
- Additional minor fixes and improvements that may not be listed here.
|
||||
|
||||
### Changed
|
||||
|
||||
- The [rename of `SGDeviceList::SenseCommRunning()` to `SGDeviceList::SenseComRunning()`](https://dev.azure.com/SenseGlove/SenseGlove-Unreal/_git/SenseGlove-Unreal/commit/663ea6a2e7b7d84991005f33ddced8d8abdc11c3?refName=refs/heads/dev&path=/Source/SenseGloveCore/Private/SGCore/SGDeviceList.cpp&_a=contents) — previously listed as part of the [v2.7.0 release](#270---2025-11-18) — was not actually included in the `master` branch due to a missed commit during cherry-picking from `dev` to `master`. Since Microsoft Azure DevOps Repositories tags are always created from `master`, the `v2.7.0`, `v2.7.1`, `v2.7.2`, and `v2.7.3` tags also do not contain this change. The rename is, however, included in the Unreal Engine `5.7`, `5.6`, `5.5`, and `5.4` packages submitted to [Epic's Fab Store](https://www.fab.com/), as those archives were built from their respective engine-specific branches, which already contained the change. The rename is now correctly applied to the `master` branch and the `v2.8.0` tag as part of the `v2.8.0` release. If you are using the source code directly from `master`, or from one of the mentioned `v2.7.x` tags obtained via Microsoft Azure DevOps Repositories (instead of the version distributed via Fab or other engine-specific branches), this introduces an ABI and API breaking change affecting both C++ and Blueprint code if your exsiting plugin version `v2.7.x` does not already include this rename.
|
||||
- List existing SenseGlove components under `SenseGlove` `ClassGroup` inside Unreal's Blueprint Editor. This chagnes `ClassGroup` for `USGGrabComponent`, `USGTouchComponent`, `USGVirtualHandComponent`, and `USGWristTrackerComponent`.
|
||||
- Force `USGVirtualHandComponent` and `USGWristTrackerComponent` to update their XR hand-tracking data when their handedness is updated.
|
||||
- Bumped the SenseGlove Unreal Engine Marketplace Packager to `v0.6.2-b675bab`.
|
||||
- Bumped the copyright years.
|
||||
|
||||
### Removed
|
||||
|
||||
- Dropped support for Unreal Engine `5.4`, which was already deprecated via the `v2.7.x` release series.
|
||||
- Dropped support for Epic Native/Cross Toolchains `v22` (previously used for building UE `5.3` and `5.4` Linux dependencies), as they were already deprecated via previous releases.
|
||||
- Removed support for the deprecated `FXRMotionControllerData`. The plugin now exclusively uses `FXRHandTrackingState` (introduced in Unreal Engine 5.5+ and supported by The SenseGlove Unreal Engine Plugin since [`v2.2.0`](#220---2024-10-22) for OpenXR hand tracking. This affects only projects that directly consume `FXRMotionControllerData` from the SenseGlove plugin in their own custom hand-tracking or interaction systems. Please see [the v2.7.x to v2.8.x migration guide](../misc/upgrade-guide/#upgrading-from-v27x-to-v28x) for more details.
|
||||
- Removed SenseGlove's `GetMotionControllerData()`; the alternative implementation to `IXTrackingSystem::GetMotionControllerData()`. You can now use SenseGlove's `GetHandTrackingState()` instead of Unreal's `IXTrackingSystem::GetHandTrackingState()` which guarantees the OpenXR hand-tracking data is coming from a SenseGlove device and also takes into account the SenseGlove's wrist-tracker offsets automatically.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Added a [v2.7.x to v2.8.x migration guide](../misc/upgrade-guide/#upgrading-from-v27x-to-v28x) for transitioning from `FXRMotionControllerData` to `FXRHandTrackingState` for projects that directly consume SenseGlove OpenXR data.
|
||||
- Added documentation section [Roll Your Own Hand Manipulation System](../advanced-topics/roll-your-own-hand-manipulation-system/).
|
||||
- Added documentation section [SGPawn Events](../advanced-topics/roll-your-own-hand-manipulation-system/sgpawn-events-puppeteer-puppet-architecture/).
|
||||
- Added documentation section [SGHandTrackerComponent](../advanced-topics/roll-your-own-hand-manipulation-system/sghandtrackercomponent/).
|
||||
- Added documentation section [SGHapticsComponent](../advanced-topics/roll-your-own-hand-manipulation-system/sghapticscomponent/).
|
||||
- Updated [Enabling XR_EXT_hand_tracking on VR Headsets](../getting-started/enabling-xr-ext-hand-tracking-vr-headsets/) documentation, replacing `FXRMotionControllerData` usage with `FXRHandTrackingState`.
|
||||
- Updated [Setup the Virtual Hand Meshes](../getting-started/setup-virtual-hand-meshes/) documentation, replacing `FXRMotionControllerData` usage with `FXRHandTrackingState`.
|
||||
- Updated [Plugin Configuration > Plugin Settings > Virtual Hand > Mesh](../plugin-configuration/plugin-settings/virtual-hand/mesh.md) documentation, replacing `FXRMotionControllerData` usage with `FXRHandTrackingState`.
|
||||
- Updated [Advanced Topics > OpenXR](../advanced-topics/openxr/) documentation section with more relevant and plenty of useful information reflecting the recent changes.
|
||||
- Updated [Advanced Topics > OpenXR > Consuming FXRHandTrackingState](../advanced-topics/openxr/consuming-fxrhandtrackingstate/) documentation section with information reflecting the recent changes.
|
||||
- Updated [Advanced Topics > OpenXR > Consuming FXRHandTrackingState > Blueprint](../advanced-topics/openxr/consuming-fxrhandtrackingstate/blueprint.html) documentation section with information reflecting the recent changes.
|
||||
- Updated [Advanced Topics > OpenXR > Consuming FXRHandTrackingState > C++](../advanced-topics/openxr/consuming-fxrhandtrackingstate/cpp.html) documentation section with information reflecting the recent changes.
|
||||
- Removed the **Consuming FXRMotionControllerData** documentation section as it's no longer relevant.
|
||||
- Removed the **Consuming FXRMotionControllerData > Blueprint** documentation section as it's no longer relevant.
|
||||
- Removed the **Consuming FXRMotionControllerData > C++** documentation section as it's no longer relevant.
|
||||
- Ensure deterministic dependency builds by pinning Rust toolchain.
|
||||
- Changelog errata fixes.
|
||||
- Minor changelog formatting fixes.
|
||||
- Additional minor fixes and improvements that may not be listed here.
|
||||
|
||||
## [2.7.3] - 2026-02-17
|
||||
|
||||
This patch release resolves documentation build pipeline failures by updating broken mdBook tooling.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Bumped [mdBook](https://rust-lang.github.io/mdBook/) to `v0.5.2`
|
||||
- Bumped the [Michael-F-Bryan/mdbook-epub](https://github.com/Michael-F-Bryan/mdbook-epub) crate to [`21a1c8134134201a2d555313447c96e56e2a8996`](https://github.com/Michael-F-Bryan/mdbook-epub/commit/21a1c8134134201a2d555313447c96e56e2a8996), which addresses issue [#133](https://github.com/Michael-F-Bryan/mdbook-epub/issues/133). This allowed upgrading mdBook from `0.4.49` to `v0.5.2` without breaking images inside the generated ePub version of the handbook.
|
||||
- Bumped [HollowMan6/mdbook-pdf](https://github.com/HollowMan6/mdbook-pdf) to `v0.1.13`.
|
||||
- Removed [lambdalisue/rs-mdbook-alerts](https://github.com/lambdalisue/rs-mdbook-alerts) as it's incompatible with mdBook `v0.5.x`. The good news is that mdBook itself is now able to support this feature natively.
|
||||
|
||||
## [2.7.2] - 2026-02-17
|
||||
|
||||
This patch release resolves build issues affecting Unreal Engine `5.7` on Linux `Arm64` architecture.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue in UE `5.7` projects where Linux `Arm64` builds were incorrectly linking against third-party libraries compiled with Epic Native/Cross Toolchain `v25` (shipped with UE `5.6`), instead of the required Toolchain `v26` binaries (shipped with UE `5.7`).
|
||||
|
||||
## [2.7.1] - 2025-12-09
|
||||
|
||||
This patch release addresses a severe regression affecting UE `5.5` projects with specific settings.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Resolved a critical deadlock between the rendering and game threads in Unreal Engine `5.5` that when `IHeadMountedDisplay::GetHMDMonitorInfo()` is invoked from `FSGXRTracker::GetControllerTransform()` in certain conditions; specifically while SenseCom is running and gloves are present and connected. This issue is [related to critical deadlock issue (UE-212224), occurring during PipelinedFrameState acquisition, which had already been addressed for the plugin versions running on UE `5.6+`](#260---2025-06-04). Because it only gets triggered under specific project settings, we were previously unaware that it also affected IHeadMountedDisplay::GetHMDMonitorInfo() in UE `5.5`.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Locked the [mdbook-pdf](https://github.com/HollowMan6/mdbook-pdf) crate to [`v0.1.11`](https://github.com/HollowMan6/mdbook-pdf/releases/tag/v0.1.11) due to the following `v0.1.12` panick during PDF generation: called `Result::unwrap()` on an `Err` value: Unable to deserialize the `RenderContext`.
|
||||
|
||||
## [2.7.0] - 2025-11-18
|
||||
|
||||
This minor release focuses on delivering performance improvements, made possible by major optimizations in the underlying proprietary SenseGlove libraries, while also introducing some breaking changes.
|
||||
@@ -79,7 +284,7 @@ This minor release focuses on delivering performance improvements, made possible
|
||||
### Documentation
|
||||
|
||||
- Added [{fmt} Formatting Library License](../license/fmt-formatting-library.html) section.
|
||||
- Added [Loguru Logging Library License](../license/loguru-loggin-library.html) section.
|
||||
- Added [Loguru Logging Library License](../license/loguru-logging-library.html) section.
|
||||
- Updated [Setting up the Touch System](../getting-started/setup-touch-system/index.html) section to reflect the recent touch system changes.
|
||||
- Added [a third-party tutorial to the Android Standalone Mode Deployment](../misc/android-standalone-deployment/third-party-tutorials/) section for UE `5.7` which provides a one-click Android development and build environment setup, instructions on how to setup and use the new UE `5.7` project launcher, and how to enabled OpenXR hand-tracking support on Meta Quest and HTC VIVE devices.
|
||||
|
||||
@@ -151,7 +356,7 @@ This minor release delivers broad compatibility, stability, and maintainability
|
||||
- `SGLog` now relies on `TAtomic<bool>` for thread-safe initialization.
|
||||
- `SGBackend` now relies on `TAtomic<bool>` for thread-safe initialization.
|
||||
- `USGBackend::IsBackendInitialized()` is no longer inlined and the initialization flag has been moved to the private implementation of `USGBackend`.
|
||||
- Bumped the SenseGlove Unreal Engine Marketplace Packager `v0.6.0-4108c6f`.
|
||||
- Bumped the SenseGlove Unreal Engine Marketplace Packager to `v0.6.0-4108c6f`.
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -247,7 +452,7 @@ This minor release brings various improvements and, notably, the first version t
|
||||
- Now the motion sources for the wrist-tracking hardware or hand-tracking are queried and populated dynamically rather than relying on the hardcoded `EControllerHand` enum. This allows the SenseGlove Unreal Engine Plugin to integrate better into other plugins such as `ViveOpenXR`, which when enabled, provides many more options as the motion source for their various wrist-tracking hardware.
|
||||
- `FSGWristTrackingSettings::LeftHandMotionSource` and `FSGWristTrackingSettings::RightHandMotionSource` types have changed from `EControllerHand` to `FName`.
|
||||
- Bumped the SenseGlove libraries to `v2.105.3-97ea18cb`.
|
||||
- Bumped the SenseGlove Unreal Engine Marketplace Packager `v0.5.0-7df1183`.
|
||||
- Bumped the SenseGlove Unreal Engine Marketplace Packager to `v0.5.0-7df1183`.
|
||||
- Bumped the copyright years.
|
||||
- This is the last release to support Unreal Engine `5.2`. From `v2.5.x` onwards only UE `5.3` and newer will be supported.
|
||||
- The `ESGViveHMDDetectionPriority` enum items have changed and are no longer backward-compatible.
|
||||
|
||||
@@ -159,7 +159,23 @@
|
||||
│ │
|
||||
│ └── windows
|
||||
│ │
|
||||
│ ├── v143 (Microsoft Visual Studio 2022 dependencies)
|
||||
│ ├── v143-1438 (Microsoft Visual Studio 2022 dependencies for UE 5.5 and 5.6)
|
||||
│ │ │
|
||||
│ │ └── x86-64 (dependencies targeting Microsoft Windows x86-64 architecture)
|
||||
│ │ │
|
||||
│ │ ├── debug
|
||||
│ │ │
|
||||
│ │ └── release
|
||||
│ │
|
||||
│ ├── v143-1444 (Microsoft Visual Studio 2022 dependencies for UE 5.7 and 5.8)
|
||||
│ │ │
|
||||
│ │ └── x86-64 (dependencies targeting Microsoft Windows x86-64 architecture)
|
||||
│ │ │
|
||||
│ │ ├── debug
|
||||
│ │ │
|
||||
│ │ └── release
|
||||
│ │
|
||||
│ ├── v145-1450 (Microsoft Visual Studio 2026 dependencies for UE 5.8)
|
||||
│ │ │
|
||||
│ │ └── x86-64 (dependencies targeting Microsoft Windows x86-64 architecture)
|
||||
│ │ │
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
# Extra Resources
|
||||
|
||||
There are various resources available for older versions of the SenseGlove Unreal Engine Plugin prior to `v2.1.x` that might still be partially relevant. These include example projects, demo scenes, and tutorials. Plans are underway to provide new example projects, demo scenes, and tutorials for the latest release. In the meantime, the outdated resources can still be beneficial
|
||||
There are various resources available for older versions of the SenseGlove Unreal Engine Plugin prior to [`v2.1.x`](changelog.html#210---2024-08-16) that might still be partially relevant. These include example projects, demo scenes, and tutorials. Plans are underway to provide new example projects, demo scenes, and tutorials for the latest release. In the meantime, the outdated resources can still be beneficial
|
||||
|
||||
## Examples and Demo Projects
|
||||
|
||||
- [A basic OpenXR-compatible Blueprint demo demonstrating basic functionality such as grab/release, touch with buzz and force-feedback, etc (compatible with versions v2.1.0+).](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGBasicDemo-OpenXR)
|
||||
- [A basic Blueprint demo demonstrating basic functionality such as grab/release, touch with buzz and force-feedback, etc (compatible with versions >= v1.4.x and <= v2.0.x).](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGBasicDemo)
|
||||
- [Example C++ API Project (only compatible with early v1.x.x releases)](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGPlaygroundCpp)
|
||||
- [Example Blueprint API Project (only compatible with early v1.x.x releases)](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGPlaygroundBP)
|
||||
- [A basic OpenXR-compatible Blueprint demo demonstrating basic functionality such as grab/release, touch with buzz and force-feedback, etc (compatible with versions `v2.1.0+`).](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGBasicDemo-OpenXR)
|
||||
- [A basic Blueprint demo demonstrating basic functionality such as grab/release, touch with buzz and force-feedback, etc (compatible with versions >= `v1.4.x` and <= `v2.0.x)`.](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGBasicDemo)
|
||||
- [Example C++ API Project (only compatible with early `v1.x.x` releases)](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGPlaygroundCpp)
|
||||
- [Example Blueprint API Project (only compatible with early `v1.x.x` releases)](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal-SGPlaygroundBP)
|
||||
|
||||
### Developer Spotlight Demos
|
||||
|
||||
- OpenXR Hand Interactions System From Scratch (Pure Blueprint)
|
||||
- [Source Code Repository](https://gitlab.com/senseglove-unreal-engine-public/senseglove-unreal-openxr-hand-from-scratch-bp-demo)
|
||||
- [Video Documentation: Building OpenXR Hand Interactions from Scratch in Unreal Engine](https://youtu.be/J18c99vl6dE)
|
||||
|
||||
### Third-Party OpenXR Integration Demos
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
## Planned Features Completion Status
|
||||
|
||||
### Implemented as of v2.7.x
|
||||
### Implemented as of v2.9.x
|
||||
|
||||
- [X] SenseGlove Unreal Engine Handbook, a comprehensive guide to the SenseGlove Unreal Engine Plugin accessible online via [unreal.docs.senseglove.com](http://unreal.docs.senseglove.com/), and also in PDF and EPUB formats.
|
||||
- [X] Full SenseGlove low-level core API access through Unreal C++.
|
||||
- [X] Full SenseGlove low-level core API access through Blueprint.
|
||||
- [X] DK 1 Support.
|
||||
@@ -41,8 +42,10 @@
|
||||
- [X] Ability to manage the Engine Scalability Settings through the SenseGlove plugin in order to change the graphics settings on the fly.
|
||||
- [X] Ability to automatically ask for the required permissions on Android when the plugin is enabled in any UE project.
|
||||
- [X] `ViveOpenXR` plugin compatibility.
|
||||
- [X] A highly convenient OpenXR hand-tracking data provider component to easily feed SenseGlove data to your own custom or third-party hand-manipulation systems.
|
||||
- [X] A highly convenient haptics component to easily add haptics to your own custom or third-party hand-manipulation systems.
|
||||
|
||||
### Upcoming features planned for the v2.7.x release
|
||||
### Upcoming features planned for the v2.10.x release
|
||||
|
||||
### Planned features long-term
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
## Platform Support Matrix
|
||||
|
||||
| | **Windows (MSVC 2017)** | **Windows (MSVC 2019)** | **Windows (MSVC 2022)** | **Linux x86-64 (Native Toolchain)** | **Linux AArch64 (Native Toolchain)** | **Meta Quest Standalone (Android NDK)** | **HTC VIVE Standalone (Android NDK)** | **Fab** | **Azure DevOps Version** |
|
||||
|:--------:|-------------------------|-------------------------|-------------------------|:-----------------------------------:|:------------------------------------:|:---------------------------------------:|:-------------------------------------:|:--------:|:------------------------:|
|
||||
| **5.7** | ❌ | ❌ | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x (r25b) | ❓ | ✅ v2.7.0 | ✅ v2.7.0 |
|
||||
| **5.6** | ❌ | ❌ | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x (r25b) | ❓ | ✅ v2.7.0 | ✅ v2.7.0 |
|
||||
| **5.5** | ❌ | ❌ | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x | ✅ v2.7.x (r25b) | ❓ | ✅ v2.7.0 | ✅ v2.7.0 |
|
||||
| **5.4** | ❌ | ❌ | ⚠️ v2.7.x | ⚠️ v2.7.x | ⚠️ v2.7.x | ⚠️ v2.7.x (r25b) | ⚠️ v2.7.x (r25b) | ⚠️ v2.7.0 | ⚠️ v2.7.0 |
|
||||
| **5.3** | ❌ | ⚠️ v2.6.x | ⚠️ v2.6.x | ⚠️ v2.6.x | ⚠️ v2.6.x | ⚠️ v2.6.x (r25b) | ⚠️ v2.6.x (r25b) | ⚠️ v2.6.3 | ⚠️ v2.6.3 |
|
||||
| **5.2** | ❌ | ⚠️ v2.4.x | ⚠️ v2.4.x | ⚠️ v2.4.x | ⚠️ v2.4.x | ⚠️ v2.4.x (r25b) | ❓ | ⚠️ v2.4.2 | ⚠️ v2.4.2 |
|
||||
| **5.1** | ❌ | ⚠️ v2.0.x | ⚠️ v2.0.x | ⚠️ v2.0.x | ⚠️ v2.0.x | ⚠️ v2.0.x (r25b) | ❓ | ⚠️ v2.0.0 | ⚠️ v2.0.2 |
|
||||
| **5.0** | ❌ | ⚠️ v1.6.x | ⚠️ v1.6.x | ⚠️ v1.6.x | ⚠️ v1.6.x | ⚠️ v1.6.x (r21e) | ❓ | ⚠️ v1.6.1 | ⚠️ v1.6.1 |
|
||||
| **4.27** | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x (r21e) | ❓ | ⚠️ v1.3.1 | ⚠️ v1.4.3 |
|
||||
| **4.26** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.25** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.24** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.23** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.22** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| | **Windows (MSVC 2017)** | **Windows (MSVC 2019)** | **Windows (MSVC 2022)** | **Windows (MSVC 2026)** | **Linux x86-64 (Native Toolchain)** | **Linux AArch64 (Native Toolchain)** | **Meta Quest Standalone (Android NDK)** | **HTC VIVE Standalone (Android NDK)** | **Fab** | **Azure DevOps Version** |
|
||||
|:--------:|:-----------------------:|:-----------------------:|:-----------------------:|:-----------------------:|:-----------------------------------:|:------------------------------------:|:---------------------------------------:|---------------------------------------|----------|--------------------------|
|
||||
| **5.8** | ❌ | ❌ | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x (r25b) | ❓ | ✅ v2.9.1 | ✅ v2.9.1 |
|
||||
| **5.7** | ❌ | ❌ | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x (r25b) | ❓ | ✅ v2.9.1 | ✅ v2.9.1 |
|
||||
| **5.6** | ❌ | ❌ | ✅ v2.9.x | ❌ | ✅ v2.9.x | ✅ v2.9.x | ✅ v2.9.x (r25b) | ❓ | ✅ v2.9.1 | ✅ v2.9.1 |
|
||||
| **5.5** | ❌ | ❌ | ⚠️ v2.9.x | ❌ | ⚠️ v2.9.x | ⚠️ v2.9.x | ⚠️ v2.9.x (r25b) | ❓ | ⚠️ v2.9.1 | ⚠️ v2.9.1 |
|
||||
| **5.4** | ❌ | ❌ | ⚠️ v2.7.x | ❌ | ⚠️ v2.7.x | ⚠️ v2.7.x | ⚠️ v2.7.x (r25b) | ⚠️ v2.7.x (r25b) | ⚠️ v2.7.3 | ⚠️ v2.7.3 |
|
||||
| **5.3** | ❌ | ⚠️ v2.6.x | ⚠️ v2.6.x | ❌ | ⚠️ v2.6.x | ⚠️ v2.6.x | ⚠️ v2.6.x (r25b) | ⚠️ v2.6.x (r25b) | ⚠️ v2.6.3 | ⚠️ v2.6.3 |
|
||||
| **5.2** | ❌ | ⚠️ v2.4.x | ⚠️ v2.4.x | ❌ | ⚠️ v2.4.x | ⚠️ v2.4.x | ⚠️ v2.4.x (r25b) | ❓ | ⚠️ v2.4.2 | ⚠️ v2.4.2 |
|
||||
| **5.1** | ❌ | ⚠️ v2.0.x | ⚠️ v2.0.x | ❌ | ⚠️ v2.0.x | ⚠️ v2.0.x | ⚠️ v2.0.x (r25b) | ❓ | ⚠️ v2.0.0 | ⚠️ v2.0.2 |
|
||||
| **5.0** | ❌ | ⚠️ v1.6.x | ⚠️ v1.6.x | ❌ | ⚠️ v1.6.x | ⚠️ v1.6.x | ⚠️ v1.6.x (r21e) | ❓ | ⚠️ v1.6.1 | ⚠️ v1.6.1 |
|
||||
| **4.27** | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x | ❌ | ⚠️ v1.4.x | ⚠️ v1.4.x | ⚠️ v1.4.x (r21e) | ❓ | ⚠️ v1.3.1 | ⚠️ v1.4.3 |
|
||||
| **4.26** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | ❌ | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.25** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.24** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.23** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
| **4.22** | ⚠️ v1.0.x | ⚠️ v1.0.x | ❌ | | ⚠️ v1.0.x | ❌ | ❌ | ❌ | ❌ | ⚠️ v1.0.4 |
|
||||
|
||||
* <code>✅</code> Supported
|
||||
* <code>⚠️</code> End-of-life (EOL) release that is not supported anymore and might be lacking features
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Enabling XR_EXT_hand_tracking OpenXR Extension on VR Headsets
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Starting from version `v2.1.0`, the SenseGlove Unreal Engine Plugin requires
|
||||
> the `XR_EXT_hand_tracking` OpenXR extension to function. Without this
|
||||
> OpenXR extension the plugin won't output any glove data.
|
||||
> Starting from version
|
||||
> [`v2.1.0`](../../appendix/changelog.html#210---2024-08-16),
|
||||
> the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR
|
||||
> extension to function. Without this OpenXR extension the plugin won't output
|
||||
> any glove data.
|
||||
|
||||
Since version `v2.1.0`, the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. Whether you are streaming your immersive 3D VR application from your PC to your VR headset, or deploying it to your VR headset in standalone mode, enabling `XR_EXT_hand_tracking` support, might require additional plugins or settings depending on the HMD's vendor or model.
|
||||
Since version [`v2.1.0`](../../appendix/changelog.html#210---2024-08-16), the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. Whether you are streaming your immersive 3D VR application from your PC to your VR headset, or deploying it to your VR headset in standalone mode, enabling `XR_EXT_hand_tracking` support, might require additional plugins or settings depending on the HMD's vendor or model.
|
||||
|
||||
## PCVR Mode
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Enabling XR_EXT_hand_tracking OpenXR Extension on VR Headsets in PCVR Mode
|
||||
|
||||
Starting from version `v2.1.0`, the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. If you are streaming your immersive 3D VR application from your PC to your VR headset, enabling `XR_EXT_hand_tracking` support, requires additional plugins and settings depending on the HMD's vendor or model.
|
||||
Starting from version [`v2.1.0`](../../../appendix/changelog.html#210---2024-08-16), the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. If you are streaming your immersive 3D VR application from your PC to your VR headset, enabling `XR_EXT_hand_tracking` support, requires additional plugins and settings depending on the HMD's vendor or model.
|
||||
|
||||
## Enabling OpenXR Plugin and Disabling OpenXRHandTracking Plugin
|
||||
|
||||
@@ -61,7 +61,7 @@ For VIVE headsets relying on the VIVE Business Streaming application, ensure the
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
> Tracking and accessing `FXRMotionControllerData` output from SenseGlove
|
||||
> Tracking and accessing `FXRHandTrackingState` output from SenseGlove
|
||||
> devices do not require Hand and Body Tracking to be enabled on the HMD device.
|
||||
> Enabling this feature is only necessary if you wish to use hand-tracking as a
|
||||
> fallback option when no glove is connected to your PC.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Enabling XR_EXT_hand_tracking OpenXR Extension on VR Headsets in Standalone Mode
|
||||
|
||||
Starting from version `v2.1.0`, the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. If you are deploying your immersive 3D VR application to your VR headset in standalone mode, enabling `XR_EXT_hand_tracking` support, requires additional plugins and settings depending on the HMD's vendor or model.
|
||||
Starting from version [`v2.1.0`](../../../appendix/changelog.html#210---2024-08-16), the SenseGlove Unreal Engine Plugin requires the `XR_EXT_hand_tracking` OpenXR extension to function. If you are deploying your immersive 3D VR application to your VR headset in standalone mode, enabling `XR_EXT_hand_tracking` support, requires additional plugins and settings depending on the HMD's vendor or model.
|
||||
|
||||
## Enabling OpenXR Plugin and Disabling OpenXRHandTracking Plugin
|
||||
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
# SenseGlove FakeShm Module Tuning
|
||||
|
||||
Starting with **SenseGlove Libraries `v2.400.0-023e6b23c`**, which is at the heart of **SenseCom v1.10.x** and the **SenseGlove Unreal Engine Plugin v2.9.x**, SenseGlove has introduced a completely new communication backend called **`SGFakeShm`**.
|
||||
|
||||
Despite its name, `SGFakeShm` is **not** another shared memory implementation. Instead, it is a modern communication layer that supports multiple transports to imitate the legacy `SharedMem` behavior using alternative transports and mechanisms on both Microsoft Windows and GNU/Linux, while retaining full compatibility with the existing Shared Memory implementation.
|
||||
|
||||
The new module was designed to significantly improve latency, throughput, and frame rate stability while maintaining backward and forward compatibility with previous or future releases of SenseCom or the UE plugin.
|
||||
|
||||
## Overview
|
||||
|
||||
Prior to this release, communication between SenseCom and applications such as Unreal Engine or Unity relied exclusively on **Boost Shared Memory**.
|
||||
|
||||
While the legacy Shared Memory provides good performance, it has several drawbacks:
|
||||
|
||||
- Communication is fundamentally blocking.
|
||||
- Enterprise security software frequently interferes with Shared Memory.
|
||||
- Multiple applications competing for Shared Memory can negatively impact performance.
|
||||
- Due to its blocking/synchronous nature of the legacy `SharedMem` implementation, rendering and communication become more tightly coupled, potentially reducing frame rate where Boost `SharedMem` becomes the bottleneck.
|
||||
|
||||
The new `SGFakeShm` backend addresses these limitations by introducing two additional communication mechanisms/transports:
|
||||
|
||||
- **IPC**
|
||||
|
||||
- Named Pipes on Microsoft Windows.
|
||||
- Unix Domain Sockets on GNU/Linux.
|
||||
|
||||
- **TCP over loopback (localhost)**
|
||||
|
||||
Unlike the legacy Shared Memory implementation, both `IPC` and `TCP` transports support a fully asynchronous, non-blocking architecture, unless explicitly forced to fall back to synchronous/blocking mode via the user-exposed configuration.
|
||||
|
||||
This allows communication to happen independently from rendering, reducing stalls and improving frame time consistency.
|
||||
|
||||
## Backward and Forward Compatibility
|
||||
|
||||
One of the design goals of `SGFakeShm` was **complete backward and forward compatibility**.
|
||||
|
||||
Even if your application is still using an **older Unreal Engine plugin**, simply upgrading **SenseCom** allows you to benefit from the improvements made to the Shared Memory transport. The opposite is also correct, you can pair old SenseCom with newer UE plugin that relies on `SGFakeShm` and both still should function as expected.
|
||||
|
||||
> [!NOTE]
|
||||
> If a new version of SenseCom or the UE plugin paired with a version that lacks
|
||||
> support for `SGFakeShm`, using the default settings, it will always
|
||||
> automatically fall back to the legacy `SharedMem`. Thus, we recommend using
|
||||
> the recent versions of both SenseCom and the UE plugin with support for
|
||||
> `SGFakeShm` to be able to fully utilize the new capabilities and improvements
|
||||
> including the new IPC and TCP transports. Otherwise, due to lack of support
|
||||
> from one party in such setups where one side lacks `SGFakeShm` support, `IPC`
|
||||
> or `TCP` won't function as expected.
|
||||
|
||||
Since the preferred transport is only chosen and configured **centrally via SenseCom** and also the exchanged configurations are versioned, Unreal and Unity plugins automatically detect and follow the selected transport and their enforced settings; as a result the setting only needs to be changed in one place and no configuration from the plugin side is required at all.
|
||||
|
||||
This ability allows the plugins or applications to remain forward compatible with SenseCom when future versions are released, thus enables SenseCom to deliver further tweaking and performance improvements in the future.
|
||||
|
||||
## Shared Memory Improvements
|
||||
|
||||
Although Shared Memory remains a blocking transport by design, it has received numerous internal optimizations. The new implementation provides:
|
||||
|
||||
- Reduced latency.
|
||||
- Higher throughput.
|
||||
- Various bug fixes.
|
||||
|
||||
In our benchmarking, we observed that the legacy `SharedMem` overhaul has dramatically improved its performance and stability, boosting throughput from only `a few KB/s` to roughly `7.5 GB/s` in certain configurations, compared to the previous implementation.
|
||||
|
||||
Applications that still prefer to use `SharedMem` over `IPC` (**Named Pipes** on Microsoft Windows and **Unix Domain Sockets** on GNU/Linux), will therefore still benefit from upgrading SenseCom and the UE plugin, even without adopting the newer `IPC` or `TCP` transports.
|
||||
|
||||
These improvements are specifically designed to eliminate bottlenecks and improve reliability in demanding enterprise environments where shared memory can be impacted by security software.
|
||||
|
||||
## Transport Configuration
|
||||
|
||||
Transport configuration is done in two parts:
|
||||
|
||||
- Choosing an IPC mechanism via SenseCom settings.
|
||||
- Optionally, tweaking and tuning the desired transport for your hardware, operating system, and use case via `sgfakeshm-params.yml`.
|
||||
|
||||
### Choosing a Transport via SenseCom Settings
|
||||
|
||||
In order to choose an IPC transport/mechanism:
|
||||
|
||||
1. Open the click on the hamburger button inside the main SenseCom user interface.
|
||||
2. From the open menu, choose `Settings`.
|
||||
3. Navigate to the end of the settings section.
|
||||
4. Click on the `IPC Method` combo box.
|
||||
5. Choose your desired IPC method.
|
||||
|
||||

|
||||
|
||||
> [!TIP]
|
||||
> You do not have to close or restart SenseCom, Unreal Editor, or your
|
||||
> simulation to choose or change an an IPC method and its settings.
|
||||
>
|
||||
> Once a new IPC method is selected, both SenseCom and your simulation should
|
||||
> be able to pick it up almost instantaneously.
|
||||
|
||||
### Parameter Tuning
|
||||
|
||||
Every `SGFakeShm` supported transport, `IPC`, `TCP`, and `SharedMem` exposes certain configurations and parameters that can further be tweaked to acheive optimal performance. While we provide sane defaults that should work well for most people out of the box, there is no single configuration that is optimal for every system, hardware, or use case. Every operating system schedules threads differently. Likewise, different communication mechanisms have different performance characteristics. For example:
|
||||
|
||||
- Microsoft Windows generally benefits from slightly larger timeout values.
|
||||
- GNU/Linux often performs best with much lower timeout values.
|
||||
- `IPC` and `TCP` transports behave differently under high load. For example, while both work best when the `SocketReceiveNonBlocking` parameter set to `true`, their behavior differ from each other depending on the value set for `SocketSendNonBlocking`. Our benchmarking with a `10K` payload size reveals, on our test hardware, IPC performs better when the value for this parameter is set to `false`, while TCP performs better when it's been set to `true`.
|
||||
- Desktop applications and VR applications often have different latency requirements.
|
||||
- Low-power embedded devices may prefer lower CPU usage over absolute minimum latency.
|
||||
|
||||
As a result, the default configuration shipped with SenseCom has been carefully selected to provide excellent performance for the vast majority of users. So, most users **do not really need to modify these values**.
|
||||
|
||||
However, advanced users who wish to squeeze every last bit of performance out of their particular hardware can optionally do so.
|
||||
|
||||
### Some Notes Regarding Latency and Throughput
|
||||
|
||||
In our benchmarks and on our test hardware, using the payload size of `1 MB`, `SharedMem` consistently acheived the follwoing stats:
|
||||
|
||||
- Read Throughput: roughly `7.5 GB/s`.
|
||||
- Write Throughput: roughly `4.0 GB/s`.
|
||||
- Smoothed Latency on Microsoft Windows: roughly `15 ms`.
|
||||
- Smoothed Latency on GNU/Linux: roughly `5 ms`.
|
||||
|
||||
The same benchmark via the `TCP` transport and using fine-tuned settings consistently yielded:
|
||||
|
||||
- Read Throughput: roughly `2.1 GB/s`.
|
||||
- Write Throughput: roughly `2.1 GB/s`.
|
||||
- Smoothed Latency on Microsoft Windows: roughly `70 μs`.
|
||||
- Smoothed Latency on GNU/Linux: roughly `40 μs`.
|
||||
|
||||
The same benchmark via the `IPC` transport and using fine-tuned settings consistently yielded:
|
||||
|
||||
- Read Throughput: roughly `2.2 GB/s`.
|
||||
- Write Throughput: roughly `2.2 GB/s`.
|
||||
- Smoothed Latency on Microsoft Windows: roughly `100 μs`.
|
||||
- Smoothed Latency on GNU/Linux: roughly `50 μs`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> While various payload sizes are supported, e.g., `1 KB`, `10 KB`, `100 KB`,
|
||||
> `1 MB`, and `10 MB`, we recommend tuning your SenseCom installation by
|
||||
> choosing the `10 KB` payload size.
|
||||
>
|
||||
> The reason for this recommendation is, typically the SenseCom payload size
|
||||
> falls somewhere in between `1 KB` to `10 KB`. Thus, tuning using `10 KB`
|
||||
> payload size will be as close as possible to real-world SenseCom usage in
|
||||
> production.
|
||||
|
||||
> [!NOTE]
|
||||
> Regardless of the payload size on Microsoft Windows, in our tests, Boost
|
||||
> `SharedMem` latency mostly stayed above `15 ms`. Due to its blocking nature,
|
||||
> this makes it less suitable for simulations that need to exceed `60 FPS`,
|
||||
> especially when `SharedMem` is restricted or throttled by security software.
|
||||
> Since the rendering thread will be blocked while waiting on `SharedMem`, if
|
||||
> the latency rises above `1000 / 60 = 16.6666666667 ms`, the framerate will
|
||||
> suffer and you may experience jitter or lag, regardless of how powerful your
|
||||
> hardware is.
|
||||
>
|
||||
> Also, there have been some reports that when `SharedMem` is throttled, even if
|
||||
> your simulation is not running, this could still cause jitter inside the Unreal
|
||||
> Editor itself.
|
||||
>
|
||||
> In this case, we recommend switching to `IPC` or `TCP` due to their low
|
||||
> latency. Despite their lower throughput compared to Boost `SharedMem`, the
|
||||
> latency of these alternative transports is in the realm of microseconds,
|
||||
> making them significantly faster than Boost `SharedMem`, whose latency is
|
||||
> measured in milliseconds.
|
||||
|
||||
### Benchmarking and Tuning Utilities
|
||||
|
||||
To assist advanced users, SenseGlove provides two simple yet powerful benchmarking applications with versatile benchmarking capabilities:
|
||||
|
||||
- **sgfakeshm-connect-client**
|
||||
- **sgfakeshm-core-client**
|
||||
|
||||
These utilities are distributed through the [**SenseGlove Unreal Engine Plugin Azure DevOps repository**](https://dev.azure.com/SenseGlove/_git/SenseGlove-Unreal) and are **not included in the [official Fab Store release](ttps://www.fab.com/listings/62bcdd2a-0bb1-4f34-b7b8-c5e7c9313099)**.
|
||||
|
||||
The utilities allow developers to:
|
||||
|
||||
- Benchmark latency.
|
||||
- Benchmark throughput.
|
||||
- Compare IPC, TCP and Shared Memory performance side by side.
|
||||
- Experiment with different parameter values
|
||||
- Optimize configurations for a specific operating system or hardware.
|
||||
- Tune parameters for particular workloads.
|
||||
|
||||
Because communication performance depends on the operating system, CPU scheduler, hardware and transport type, these tools provide the easiest way to determine the optimal settings for your own environment.
|
||||
|
||||
#### sgfakeshm-connect-client
|
||||
|
||||
`sgfakeshm-connect-client` is the main benchmarking application, which acts like SenseCom. Between the two applications, the transport configuration happens on this app.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
`sgfakeshm-connect-client` supports the following keyboard shortcuts:
|
||||
|
||||
- `ESC`, `Q`, or `Ctrl+C` to exit the application.
|
||||
- `1` to toggle and enable or disable the `TCP` transport.
|
||||
- `2` to toggle and enable or disable the `IPC` transport.
|
||||
- `3` to toggle and enable or disable the `SharedMem` transport.
|
||||
- `S` to switch between various predefined payload sizes, e.g., `1 KB`, `10 KB`, `100 KB`, `1 MB`, and `10 MB`.
|
||||
|
||||
It also supports the following command-line parameters in order to change the tuning parameters via command-line:
|
||||
|
||||
```sh
|
||||
$ ./sgfakeshm-connect-client --help
|
||||
|
||||
Usage:
|
||||
sgfakeshm-connect-client [options]
|
||||
|
||||
Options:
|
||||
--help or -h
|
||||
|
||||
--tcp <yes|no>
|
||||
--ipc <yes|no>
|
||||
--sharedmem <yes|no>
|
||||
|
||||
--socket-receive-nonblocking <yes|no>
|
||||
--socket-receive-timeout-ms <int32>
|
||||
|
||||
--socket-send-nonblocking <yes|no>
|
||||
--socket-send-timeout-ms <int32>
|
||||
|
||||
--socket-reconnect-time-min-ms <int32>
|
||||
--socket-reconnect-time-max-ms <int32>
|
||||
|
||||
--worker-retry-interval-ms <uint64>
|
||||
|
||||
--heartbeat-interval-ms <uint64>
|
||||
|
||||
--liveness-timeout-ms <uint64>
|
||||
|
||||
--latency-smoothing-window-ms <uint64>
|
||||
--throughput-smoothing-window-ms <uint64>
|
||||
|
||||
--shared-mem-heartbeat-block-size <uint32>
|
||||
--shared-mem-liveness-timeout-ms <uint64>
|
||||
```
|
||||
|
||||
It is however, much faster and easier to modify the configuration file instead of passing arguments to `sgfakeshm-connect-client` as [it supports hot reloading](#hot-reloading).
|
||||
|
||||
The following symbols indicate:
|
||||
|
||||
- ✅: the specific transport is enabled.
|
||||
- ❌: the specific transport is disabled.
|
||||
- 🟢: the specific transport is connected and `./sgfakeshm-core-client` is prensent on the other side.
|
||||
- 🔴: the specific transport is disconnected and `./sgfakeshm-core-client` might not be prensent on the other side.
|
||||
|
||||
#### sgfakeshm-core-client
|
||||
|
||||
`sgfakeshm-core-client` is the companion benchmarking application, which acts like your Unreal Engine or Unity plugin, and consists of two tabs. The first one is the `Benchmark` tab to verify the benchmarking latency and throughput from the second peer app, and the second one is the `Tuning Info` tab to verify if the `sgfakeshm-connect-client`-enforced optimizations have indeed been applied.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
`sgfakeshm-core-client` supports the following keyboard shortcuts:
|
||||
|
||||
- `ESC`, `Q`, or `Ctrl+C` to exit the application.
|
||||
- `S` to toggle between various predefined payload sizes, e.g., `1 KB`, `10 KB`, `100 KB`, `1 MB`, and `10 MB`.
|
||||
- `B` to switch to the `Benchmark` tab.
|
||||
- `T` to switch to the `Tuning Info` tab.
|
||||
|
||||
The following symbols indicate:
|
||||
|
||||
- ✅: the specific transport is enabled.
|
||||
- ❌: the specific transport is disabled.
|
||||
- 🟢: the specific transport is connected and `./sgfakeshm-connect-client` is prensent on the other side.
|
||||
- 🔴: the specific transport is disconnected and `./sgfakeshm-connect-client` might not be prensent on the other side.
|
||||
|
||||
### Important Benchmarking Limitation
|
||||
|
||||
The benchmarking utilities use the **NNG `Pair0` protocol** for `IPC` and `TCP` transports.
|
||||
|
||||
`Pair0` is intentionally designed as a **one-to-one communication protocol**, meaning that only one peer (one listener and one dialer) may exist and run at any given time.
|
||||
|
||||
Because of this limitation:
|
||||
|
||||
- **Never run multiple instances of `sgfakeshm-connect-client` simultaneously.**
|
||||
|
||||
Likewise:
|
||||
|
||||
- **Never run multiple instances of `sgfakeshm-core-client` simultaneously.**
|
||||
|
||||
Running multiple instances of either application at the same time will cause the benchmark applications to fail or crash because they attempt to establish multiple `Pair0` connections.
|
||||
|
||||
So, this means at the time of benchmarking no instances of SenseCom or UE or Unity plugins should be running either.
|
||||
|
||||
### The Configuration File
|
||||
|
||||
Depending on the platform **`SGFakeShm`-dependant applications** (such as SenseCom, UE plugin, or the benchmarking/tuning apps) are running on, all tuning parameters are generated or loaded from:
|
||||
|
||||
- `${XDG_CONFIG_HOME:-$HOME/.config}/SenseGlove/sgfakeshm-params.yml` on GNU/Linux, which typically equates to `~/.config/SenseGlove/sgfakeshm-params.yml`.
|
||||
- `%USERPROFILE%\Documents\SenseGlove\sgfakeshm-params.yml` on Microsoft Windows.
|
||||
|
||||
When SenseCom or `sgfakeshm-connect-client` start:
|
||||
|
||||
- If the file does **not** exist, it will automatically be generated.
|
||||
- If the file exists and its version matches the currently supported version, its values override the built-in and hardcoded defaults.
|
||||
- If the file format is not a valid YAML, or it contains invalid values, or the file version does not match a version that is shipped with the current `SGFakeShm` version, the existing file is preserved by renaming it to `sgfakeshm-params.yml.YYYY-MM-DD-hh-mm-ss`, and then a new configuration file containing the latest defaults is then generated automatically.
|
||||
|
||||
This approach allows users to preserve previous configurations while safely switching between various versions, whether old or new.
|
||||
|
||||
### Hot Reloading
|
||||
|
||||
One particularly useful feature is that the configuration file supports **hot reloading**.
|
||||
|
||||
Whenever the file is saved:
|
||||
|
||||
- SenseCom automatically detects the changes.
|
||||
- The configuration is reloaded.
|
||||
- The new parameters take effect immediately.
|
||||
|
||||
Restarting SenseCom or applications using the SenseGlove libraries is therefore unnecessary.
|
||||
|
||||
This makes iterative tuning significantly easier.
|
||||
|
||||
### Exposed Tuning Parameters
|
||||
|
||||
The generated `sgfakeshm-params.yml` file always contains generic documentation, along with a detailed explanation of every exposed tuning parameter and its default value. The following is the latest version of `sgfakeshm-params.yml` generated by the latest release of `SGFakeShm` at the time of writing:
|
||||
|
||||
```yaml
|
||||
# File name used for tweaking and tuning the FakeShm parameters.
|
||||
# When this file does not exist it will be generated by the SGConnectShm upon
|
||||
# initialization or reconfiguration and gets populated from the hardcoded values
|
||||
# below.
|
||||
# If it does indeed exist and file version matches the version supported by the
|
||||
# current release of the SenseGlove libraries, the parameter values in this file
|
||||
# will override the hardcoded values from the libraries.
|
||||
# If the file has invalid values, or the version number does not match, it will
|
||||
# be renamed to sgfakeshm-params.yml.YYYY-MM-DD-hh-mm-ss to
|
||||
# preserve the user's chosen values and a new file will be generated containing
|
||||
# the updated values or newly introduced parameters.
|
||||
#
|
||||
# NOTE: This file supports hot-reloading upon saving the file. Thus, you do
|
||||
# not have to close SenseCom or plugin's relying on SenseGlove libraries in
|
||||
# order to apply the changes. The changes will be picked up automatically
|
||||
# as soon as a file change is detected.
|
||||
|
||||
# The current version of the the default tuning parameters configuration
|
||||
# file.
|
||||
#
|
||||
# Existing tuning configuration files are only loaded when their version
|
||||
# matches the version supported by the current FakeShm implementation.
|
||||
# Otherwise, the existing file is backed up with a timestamped filename
|
||||
# and a new file containing the current default parameters is generated.
|
||||
#
|
||||
# Please do not change this value if you want your changes to persist.
|
||||
Version: 1
|
||||
|
||||
# If enabled, socket receive operations are performed in non-blocking
|
||||
# mode.
|
||||
# nng_recvmsg will return immediately if no message is available:
|
||||
# - NNG_EAGAIN is returned instead of blocking
|
||||
# This improves responsiveness but may increase CPU usage due to
|
||||
# polling.
|
||||
# When disabled, SocketReceiveTimeout controls blocking behavior.
|
||||
SocketReceiveNonBlocking: true
|
||||
|
||||
# Receive timeout for the NNG socket in milliseconds.
|
||||
# Controls how long nng_recvmsg blocks while waiting for a message.
|
||||
# If no message is received within this interval, the call returns
|
||||
# with a timeout, allowing the worker thread to wake up periodically
|
||||
# to:
|
||||
# - send heartbeats
|
||||
# - detect peer liveness
|
||||
# - respond to shutdown requests
|
||||
# Lower values increase responsiveness but may slightly increase CPU
|
||||
# usage.
|
||||
# This only is taken into account when SocketReceiveNonBlocking is
|
||||
# disabled.
|
||||
SocketReceiveTimeout: 1
|
||||
|
||||
# If enabled, socket send operations are performed in non-blocking
|
||||
# mode.
|
||||
# nng_sendmsg will return immediately if the message cannot be sent:
|
||||
# - NNG_EAGAIN is returned instead of blocking
|
||||
# Useful for low-latency systems where blocking is undesirable.
|
||||
SocketSendNonBlocking: false
|
||||
|
||||
# Send timeout for the NNG socket in milliseconds.
|
||||
# Controls how long nng_sendmsg blocks while attempting to send a
|
||||
# message.
|
||||
# If the message cannot be sent within this interval, the call returns
|
||||
# with a timeout error, allowing the system to avoid long blocking
|
||||
# stalls.
|
||||
# This only is taken into account when SocketSendNonBlocking is
|
||||
# disabled.
|
||||
SocketSendTimeout: 1
|
||||
|
||||
# This is the minimum amount of time (milliseconds) to wait before
|
||||
# attempting to establish a connection after a previous attempt has
|
||||
# failed. This can be set on a socket, but it can also be overridden
|
||||
# on an individual dialer. The option is irrelevant for listeners.
|
||||
SocketReconnectTimeMin: 1
|
||||
|
||||
# This is the maximum amount of time (milliseconds) to wait before
|
||||
# attempting to establish a connection after a previous attempt has
|
||||
# failed. If this is non-zero, then the time between successive
|
||||
# connection attempts will start at the value of
|
||||
# SocketReconnectTimeMin, and grow exponentially, until it reaches
|
||||
# this value. If this value is zero, then no exponential back-off
|
||||
# between connection attempts is done, and each attempt will wait the
|
||||
# time specified by SocketReconnectTimeMin. This can be set on a
|
||||
# socket, but it can also be overridden on an individual dialer. The
|
||||
# option is irrelevant for listeners.
|
||||
SocketReconnectTimeMax: 10
|
||||
|
||||
# FakeShm transport worker retry interval in milliseconds.
|
||||
# Applied when:
|
||||
# - no peer is connected
|
||||
# - socket returns no data (timeout / EAGAIN)
|
||||
# Prevents busy-waiting loops and reduces CPU usage while idle.
|
||||
WorkerRetryInterval: 1
|
||||
|
||||
# FakeShm transport heartbeat interval in milliseconds.
|
||||
# Determines how often heartbeat messages are sent to the peer to:
|
||||
# - maintain connection activity
|
||||
# - update last-seen timestamps
|
||||
# Lower values improve liveness detection but increase traffic
|
||||
# overhead.
|
||||
HeartbeatInterval: 1
|
||||
|
||||
# FakeShm IPC and TCP transports liveness timeout in milliseconds.
|
||||
# Defines how long a peer can remain silent before being considered
|
||||
# dead.
|
||||
# Compared against the timestamp of the last received message.
|
||||
# If exceeded, the peer is treated as disconnected/unresponsive.
|
||||
LivenessTimeout: 5
|
||||
|
||||
# Latency smoothing window in milliseconds.
|
||||
# Defines the time range over which latency samples are accumulated
|
||||
# to compute a rolling average (smoothed latency).
|
||||
# Larger values produce more stable results but react slower to
|
||||
# changes.
|
||||
LatencySmoothingWindow: 1000
|
||||
|
||||
# Throughput smoothing window in milliseconds.
|
||||
# Defines the time range over which throughput snapshots are
|
||||
# accumulated to compute a rolling average (smoothed snapshots).
|
||||
# Larger values produce more stable results but react slower to
|
||||
# changes.
|
||||
ThroughputSmoothingWindow: 1000
|
||||
|
||||
# The legacy Boost SharedMem heartbeat block size.
|
||||
SharedMemHeartbeatBlockSize: 1024
|
||||
|
||||
# FakeShm SharedMem liveness timeout in milliseconds.
|
||||
# Defines how long a peer can remain silent before being considered
|
||||
# dead.
|
||||
# Compared against the timestamp of the last received message.
|
||||
# If exceeded, the peer is treated as disconnected/unresponsive.
|
||||
SharedMemLivenessTimeout: 50
|
||||
```
|
||||
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
LFS
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -282,6 +282,6 @@ Next, navigate to `Project Settings > Plugins > SenseGlove > Virtual Hand Settin
|
||||
- `Left Hand Reference Mesh`
|
||||
- `Right Hand Reference Mesh`
|
||||
|
||||
This configuration guarantees that the tracking system correctly interprets the bone transforms of the virtual hand meshes when generating `FXRMotionControllerData`. Additionally, it allows the animation system to accurately use these bone transforms when processing `FXRMotionControllerData` and animating the virtual hand meshes.
|
||||
This configuration guarantees that the tracking system correctly interprets the bone transforms of the virtual hand meshes when generating `FXRHandTrackingState`. Additionally, it allows the animation system to accurately use these bone transforms when processing `FXRHandTrackingState` and animating the virtual hand meshes.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# The Argh Frustration-free Command-line Processing Library Licence
|
||||
|
||||
```
|
||||
Copyright (c) 2016, Adi Shavit
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# c4core C++ Library License
|
||||
|
||||
```
|
||||
Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# c4project CMake Scripts License
|
||||
|
||||
```
|
||||
Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
# Debug Break Library License
|
||||
|
||||
```
|
||||
Copyright (c) 2011-2016, Scott Tsai
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
@@ -0,0 +1,250 @@
|
||||
# fast_float Number Parsing Library License
|
||||
|
||||
```
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2021 The fast_float authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
```
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
```
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 The fast_float authors
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -0,0 +1,206 @@
|
||||
# FlatBuffers Memory Efficient Serialization Library Licence
|
||||
|
||||
```
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
@@ -0,0 +1,25 @@
|
||||
# The FTXUI Functional Terminal (X) User Interface Library Licence
|
||||
|
||||
```
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Arthur Sonzogni.
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
# nanomsg-next-generation Lightweight Brokerless Messaging Library
|
||||
|
||||
```
|
||||
The MIT License
|
||||
|
||||
Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
|
||||
Copyright 2018 Capitar IT Group BV <info@capitar.com>
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
# Rapid YAML Parsing and Emitting Library License
|
||||
|
||||
```
|
||||
Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -3,7 +3,7 @@
|
||||
```
|
||||
SENSEGLOVE SDK LICENSE
|
||||
|
||||
Copyright (c) 2020 - 2025 SenseGlove
|
||||
Copyright (c) 2020 - 2026 SenseGlove
|
||||
|
||||
Purchase of the Product does not entitle you to ownership or a license to any
|
||||
software generated by SenseGlove for use with the Product (the “Software”).
|
||||
|
||||
@@ -5,7 +5,7 @@ The SenseGlove Unreal Engine Handbook is licensed under the terms of the CC BY (
|
||||
```
|
||||
Attribution 4.0 International
|
||||
|
||||
Copyright (c) 2020 - 2025 SenseGlove
|
||||
Copyright (c) 2020 - 2026 SenseGlove
|
||||
|
||||
=======================================================================
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ The SenseGlove Unreal Engine Plugin is licensed under the terms of the MIT Licen
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 - 2025 SenseGlove
|
||||
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user