The open source OpenXR runtime
at main 274 lines 12 kB view raw
1// Copyright 2019-2022, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief OpenGloves Alpha Encoding Decoding implementation. 6 * @author Daniel Willmott <web@dan-w.com> 7 * @ingroup drv_opengloves 8 */ 9 10#include <string> 11#include <stdexcept> 12 13#include <map> 14#include "util/u_logging.h" 15 16#include "alpha_encoding.h" 17#include "encoding.h" 18 19enum opengloves_alpha_encoding_key 20{ 21 OPENGLOVES_ALPHA_ENCODING_FinThumb, 22 OPENGLOVES_ALPHA_ENCODING_FinSplayThumb, 23 24 OPENGLOVES_ALPHA_ENCODING_FinIndex, 25 OPENGLOVES_ALPHA_ENCODING_FinSplayIndex, 26 27 OPENGLOVES_ALPHA_ENCODING_FinMiddle, 28 OPENGLOVES_ALPHA_ENCODING_FinSplayMiddle, 29 30 OPENGLOVES_ALPHA_ENCODING_FinRing, 31 OPENGLOVES_ALPHA_ENCODING_FinSplayRing, 32 33 OPENGLOVES_ALPHA_ENCODING_FinPinky, 34 OPENGLOVES_ALPHA_ENCODING_FinSplayPinky, 35 36 OPENGLOVES_ALPHA_ENCODING_FinJointThumb0, 37 OPENGLOVES_ALPHA_ENCODING_FinJointThumb1, 38 OPENGLOVES_ALPHA_ENCODING_FinJointThumb2, 39 OPENGLOVES_ALPHA_ENCODING_FinJointThumb3, // unused in input but used for parity to other fingers in the array 40 41 42 OPENGLOVES_ALPHA_ENCODING_FinJointIndex0, 43 OPENGLOVES_ALPHA_ENCODING_FinJointIndex1, 44 OPENGLOVES_ALPHA_ENCODING_FinJointIndex2, 45 OPENGLOVES_ALPHA_ENCODING_FinJointIndex3, 46 47 48 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle0, 49 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle1, 50 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle2, 51 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle3, 52 53 54 OPENGLOVES_ALPHA_ENCODING_FinJointRing0, 55 OPENGLOVES_ALPHA_ENCODING_FinJointRing1, 56 OPENGLOVES_ALPHA_ENCODING_FinJointRing2, 57 OPENGLOVES_ALPHA_ENCODING_FinJointRing3, 58 59 60 OPENGLOVES_ALPHA_ENCODING_FinJointPinky0, 61 OPENGLOVES_ALPHA_ENCODING_FinJointPinky1, 62 OPENGLOVES_ALPHA_ENCODING_FinJointPinky2, 63 OPENGLOVES_ALPHA_ENCODING_FinJointPinky3, 64 65 OPENGLOVES_ALPHA_ENCODING_JoyX, 66 OPENGLOVES_ALPHA_ENCODING_JoyY, 67 OPENGLOVES_ALPHA_ENCODING_JoyBtn, 68 69 OPENGLOVES_ALPHA_ENCODING_TrgValue, 70 OPENGLOVES_ALPHA_ENCODING_BtnTrg, 71 OPENGLOVES_ALPHA_ENCODING_BtnA, 72 OPENGLOVES_ALPHA_ENCODING_BtnB, 73 74 OPENGLOVES_ALPHA_ENCODING_GesGrab, 75 OPENGLOVES_ALPHA_ENCODING_GesPinch, 76 77 OPENGLOVES_ALPHA_ENCODING_BtnMenu, 78 OPENGLOVES_ALPHA_ENCODING_BtnCalib, 79 80 OPENGLOVES_ALPHA_ENCODING_MAX 81}; 82 83#define OPENGLOVES_ALPHA_ENCODING_VAL_IN_MAP_E_0 84 85static const std::string opengloves_alpha_encoding_key_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ()"; 86 87static bool 88opengloves_alpha_encoding_is_key_character(const char character) 89{ 90 return opengloves_alpha_encoding_key_characters.find(character) != std::string::npos; 91} 92 93static const std::map<std::string, int> opengloves_alpha_encoding_input_key_string{ 94 {"A", OPENGLOVES_ALPHA_ENCODING_FinThumb}, // whole thumb curl (default curl value for thumb joints) 95 {"(AB)", OPENGLOVES_ALPHA_ENCODING_FinSplayThumb}, // whole thumb splay thumb joint 3 (doesn't exist, but keeps 96 // consistency with the other fingers 97 {"B", OPENGLOVES_ALPHA_ENCODING_FinIndex}, // whole index curl (default curl value for index joints) 98 {"(BB)", OPENGLOVES_ALPHA_ENCODING_FinSplayIndex}, // whole index splay 99 100 {"C", OPENGLOVES_ALPHA_ENCODING_FinMiddle}, // whole middle curl (default curl value for middle joints) 101 {"(CB)", OPENGLOVES_ALPHA_ENCODING_FinSplayMiddle}, // whole middle splay 102 103 {"D", OPENGLOVES_ALPHA_ENCODING_FinRing}, // whole ring curl (default curl value for 104 {"(DB)", OPENGLOVES_ALPHA_ENCODING_FinSplayRing}, // whole ring splay 105 // ring joints) 106 {"E", OPENGLOVES_ALPHA_ENCODING_FinPinky}, // whole pinky curl (default curl value 107 {"(EB)", OPENGLOVES_ALPHA_ENCODING_FinSplayPinky}, // whole pinky splay 108 // for pinky joints 109 {"(AAA)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb0}, // thumb joint 0 110 {"(AAB)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb1}, // thumb joint 1 111 {"(AAC)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb2}, // thumb joint 2 112 {"(AAD)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb3}, 113 {"(BAA)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex0}, // index joint 0 114 {"(BAB)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex1}, // index joint 1 115 {"(BAC)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex2}, // index joint 2 116 {"(BAD)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex3}, // index joint 3 117 {"(CAA)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle0}, // middle joint 0 118 {"(CAB)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle1}, // middle joint 1 119 {"(CAC)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle2}, // middle joint 2 120 {"(CAD)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle3}, // middle joint 3 121 {"(DAA)", OPENGLOVES_ALPHA_ENCODING_FinJointRing0}, // ring joint 0 122 {"(DAB)", OPENGLOVES_ALPHA_ENCODING_FinJointRing1}, // ring joint 1 123 {"(DAC)", OPENGLOVES_ALPHA_ENCODING_FinJointRing2}, // ring joint 2 124 {"(DAD)", OPENGLOVES_ALPHA_ENCODING_FinJointRing3}, // ring joint 3 125 {"(EAA)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky0}, // pinky joint 0 126 {"(EAB)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky1}, // pinky joint 1 127 {"(EAC)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky2}, // pinky joint 2 128 {"(EAD)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky3}, // pinky joint 3 129 {"F", OPENGLOVES_ALPHA_ENCODING_JoyX}, // joystick x component 130 {"G", OPENGLOVES_ALPHA_ENCODING_JoyY}, // joystick y component 131 {"H", OPENGLOVES_ALPHA_ENCODING_JoyBtn}, // joystick button 132 {"I", OPENGLOVES_ALPHA_ENCODING_BtnTrg}, // trigger button 133 {"J", OPENGLOVES_ALPHA_ENCODING_BtnA}, // A button 134 {"K", OPENGLOVES_ALPHA_ENCODING_BtnB}, // B button 135 {"L", OPENGLOVES_ALPHA_ENCODING_GesGrab}, // grab gesture (boolean) 136 {"M", OPENGLOVES_ALPHA_ENCODING_GesPinch}, // pinch gesture (boolean) 137 {"N", OPENGLOVES_ALPHA_ENCODING_BtnMenu}, // system button pressed (opens SteamVR menu) 138 {"O", OPENGLOVES_ALPHA_ENCODING_BtnCalib}, // calibration button 139 {"P", OPENGLOVES_ALPHA_ENCODING_TrgValue}, // analog trigger value 140 {"", OPENGLOVES_ALPHA_ENCODING_MAX} // Junk key 141}; 142 143static const std::map<int, std::string> opengloves_alpha_encoding_output_key_string{ 144 {OPENGLOVES_ALPHA_ENCODING_FinThumb, "A"}, // thumb force feedback 145 {OPENGLOVES_ALPHA_ENCODING_FinIndex, "B"}, // index force feedback 146 {OPENGLOVES_ALPHA_ENCODING_FinMiddle, "C"}, // middle force feedback 147 {OPENGLOVES_ALPHA_ENCODING_FinRing, "D"}, // ring force feedback 148 {OPENGLOVES_ALPHA_ENCODING_FinPinky, "E"}, // pinky force feedback 149}; 150 151static std::map<int, std::string> 152opengloves_alpha_encoding_parse_to_map(const std::string &str) 153{ 154 std::map<int, std::string> result; 155 156 size_t i = 0; 157 while (i < str.length()) { 158 // Advance until we get an alphabetic character (no point in looking at values that don't have a key 159 // associated with them) 160 161 if (str[i] >= 0 && opengloves_alpha_encoding_is_key_character(str[i])) { 162 std::string key = {str[i]}; 163 i++; 164 165 // we're going to be parsing a "long key", i.e. (AB) for thumb finger splay. Long keys must 166 // always be enclosed in brackets 167 if (key[0] == '(') { 168 while (str[i] >= 0 && opengloves_alpha_encoding_is_key_character(str[i]) && 169 i < str.length()) { 170 key += str[i]; 171 i++; 172 } 173 } 174 175 std::string value; 176 while (str[i] >= 0 && isdigit(str[i]) && i < str.length()) { 177 value += str[i]; 178 i++; 179 } 180 181 // Even if the value is empty we still want to use the key, it means that we have a button that 182 // is pressed (it only appears in the packet if it is) 183 if (opengloves_alpha_encoding_input_key_string.find(key) != 184 opengloves_alpha_encoding_input_key_string.end()) 185 result.insert_or_assign(opengloves_alpha_encoding_input_key_string.at(key), value); 186 else 187 U_LOG_W("Unable to insert key: %s into input map as it was not found", key.c_str()); 188 } else 189 i++; 190 } 191 192 return result; 193} 194 195 196void 197opengloves_alpha_encoding_decode(const char *data, struct opengloves_input *out) 198{ 199 std::map<int, std::string> input_map = opengloves_alpha_encoding_parse_to_map(data); 200 201 try { 202 // five fingers, 2 (curl + splay) 203 for (int i = 0; i < 5; i++) { 204 int enum_position = i * 2; 205 // curls 206 if (input_map.find(enum_position) != input_map.end()) { 207 float fin_curl_value = std::stof(input_map.at(enum_position)); 208 std::fill(std::begin(out->flexion[i]), std::begin(out->flexion[i]) + 4, 209 fin_curl_value / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE); 210 } 211 212 // splay 213 if (input_map.find(enum_position + 1) != input_map.end()) 214 out->splay[i] = 215 (std::stof(input_map.at(enum_position + 1)) / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE - 216 0.5f) * 217 2.0f; 218 } 219 220 int current_finger_joint = OPENGLOVES_ALPHA_ENCODING_FinJointThumb0; 221 for (int i = 0; i < 5; i++) { 222 for (int j = 0; j < 4; j++) { 223 // individual joint curls 224 out->flexion[i][j] = input_map.find(current_finger_joint) != input_map.end() 225 ? (std::stof(input_map.at(current_finger_joint)) / 226 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE) 227 // use the curl of the previous joint 228 : out->flexion[i][j > 0 ? j - 1 : 0]; 229 current_finger_joint++; 230 } 231 } 232 233 // joysticks 234 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyX) != input_map.end()) 235 out->joysticks.main.x = 2 * std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_JoyX)) / 236 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE - 237 1; 238 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyY) != input_map.end()) 239 out->joysticks.main.y = 2 * std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_JoyY)) / 240 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE - 241 1; 242 out->joysticks.main.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyBtn) != input_map.end(); 243 244 } catch (std::invalid_argument &e) { 245 U_LOG_E("Error parsing input string: %s", e.what()); 246 } 247 248 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_TrgValue) != input_map.end()) 249 out->buttons.trigger.value = 250 std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_TrgValue)) / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE; 251 out->buttons.trigger.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnTrg) != input_map.end(); 252 253 out->buttons.A.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnA) != input_map.end(); 254 out->buttons.B.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnB) != input_map.end(); 255 out->gestures.grab.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesGrab) != input_map.end(); 256 out->gestures.pinch.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesPinch) != input_map.end(); 257 out->buttons.menu.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnMenu) != input_map.end(); 258} 259 260void 261opengloves_alpha_encoding_encode(const struct opengloves_output *output, char *out_buff) 262{ 263 sprintf(out_buff, "%s%d%s%d%s%d%s%d%s%d\n", 264 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinThumb).c_str(), 265 (int)(output->force_feedback.thumb * 1000), 266 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinIndex).c_str(), 267 (int)(output->force_feedback.index * 1000), 268 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinMiddle).c_str(), 269 (int)(output->force_feedback.middle * 1000), 270 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinRing).c_str(), 271 (int)(output->force_feedback.ring * 1000), 272 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinPinky).c_str(), 273 (int)(output->force_feedback.little * 1000)); 274}