diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..989c047e --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +ColumnLimit: 170 \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1ec77c84..00000000 --- a/.editorconfig +++ /dev/null @@ -1,69 +0,0 @@ -# Visual Studio generated .editorconfig file with C++ settings. -root = true - -[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] - -# Visual C++ Code Style settings - -cpp_generate_documentation_comments = xml - -# Visual C++ Formatting settings - -cpp_indent_braces = false -cpp_indent_multi_line_relative_to = statement_begin -cpp_indent_within_parentheses = align_to_parenthesis -cpp_indent_preserve_within_parentheses = true -cpp_indent_case_contents = true -cpp_indent_case_labels = false -cpp_indent_case_contents_when_block = false -cpp_indent_lambda_braces_when_parameter = true -cpp_indent_goto_labels = one_left -cpp_indent_preprocessor = leftmost_column -cpp_indent_access_specifiers = false -cpp_indent_namespace_contents = true -cpp_indent_preserve_comments = false -cpp_new_line_before_open_brace_namespace = same_line -cpp_new_line_before_open_brace_type = same_line -cpp_new_line_before_open_brace_function = same_line -cpp_new_line_before_open_brace_block = same_line -cpp_new_line_before_open_brace_lambda = same_line -cpp_new_line_scope_braces_on_separate_lines = true -cpp_new_line_close_brace_same_line_empty_type = false -cpp_new_line_close_brace_same_line_empty_function = false -cpp_new_line_before_catch = true -cpp_new_line_before_else = true -cpp_new_line_before_while_in_do_while = false -cpp_space_before_function_open_parenthesis = remove -cpp_space_within_parameter_list_parentheses = false -cpp_space_between_empty_parameter_list_parentheses = false -cpp_space_after_keywords_in_control_flow_statements = true -cpp_space_within_control_flow_statement_parentheses = false -cpp_space_before_lambda_open_parenthesis = false -cpp_space_within_cast_parentheses = false -cpp_space_after_cast_close_parenthesis = false -cpp_space_within_expression_parentheses = false -cpp_space_before_block_open_brace = true -cpp_space_between_empty_braces = false -cpp_space_before_initializer_list_open_brace = false -cpp_space_within_initializer_list_braces = true -cpp_space_preserve_in_initializer_list = true -cpp_space_before_open_square_bracket = false -cpp_space_within_square_brackets = false -cpp_space_before_empty_square_brackets = false -cpp_space_between_empty_square_brackets = false -cpp_space_group_square_brackets = true -cpp_space_within_lambda_brackets = false -cpp_space_between_empty_lambda_brackets = false -cpp_space_before_comma = false -cpp_space_after_comma = true -cpp_space_remove_around_member_operators = true -cpp_space_before_inheritance_colon = true -cpp_space_before_constructor_colon = true -cpp_space_remove_before_semicolon = true -cpp_space_after_semicolon = true -cpp_space_remove_around_unary_operator = true -cpp_space_around_binary_operator = insert -cpp_space_around_assignment_operator = insert -cpp_space_pointer_reference_alignment = left -cpp_space_around_ternary_operator = insert -cpp_wrap_preserve_blocks = all_one_line_scopes diff --git a/.gitignore b/.gitignore index 1f2587be..69181b72 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ Build/ *.vcxproj.* CMakeCache.txt -cmake_install.cmake \ No newline at end of file +cmake_install.cmake +clion_build/ \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index 1a42cb3d..4bd679b1 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -20,17 +20,19 @@ Binaries are (or will) be provided in the Releases in this Repository, but if yo This should generate Visual Studio project files in the `build/` folder, which you can then compile. -# Building with Visual Studio IDE -* Open the Visual Studio project (.sln file) in the `build/` folder -* You should already have the ability to build the driver by pressing `Ctrl + Shift + B` - * The artifacts of the build will be outputted to `build/Debug/`, or `build/Release/` depending on build configuration - # Building with Visual Studio Build Tools * run a cmake build in the `build/` folder * `cmake --build . --config Release` * The artifacts of the build will be outputted to `build/Debug/`, or `build/Release/` depending on build configuration +# Building with Visual Studio IDE +* Open the Visual Studio project (.sln file) in the `build/` folder +* You should already have the ability to build the driver by pressing `Ctrl + Shift + B` + * The artifacts of the build will be outputted to `build/Debug/`, or `build/Release/` depending on build configuration + # Adding driver to Steam +**Note:** For a more streamlined debugging environment, refer to [Debugging with Visual Studio](https://github.com/LucidVR/opengloves-driver/blob/develop/BUILDING.md#debugging-with-visual-studio). +This step is for people who may not necessarily want to setup a debugging environment, or are testing release builds. * Copy the `openglove` folder into the steamvr drivers folder * Usually located `C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers` * Running SteamVR you should see the driver activate two new controllers @@ -73,16 +75,20 @@ If you want to make changes to the code and would like to use a debugger/not hav * On the first row (with the process name ``, make sure that the `Action` is set to `Do not debug`. * Add a new row (double click on the empty `Process name` underneath ``. * Add `vrserver.exe` as the process name - * Ensure that `Action` is set to `Attach Debugger`. + * Ensure that `Action` is set to `Attach Debugger`. -## Launch SteamVR when building through Visual Studio -It's usually quite useful to build then automatically launch SteamVR for debugging purposes. +## Launch SteamVR/Overlay when building through Visual Studio +It's usually quite useful to build then automatically launch SteamVR/Overlay for debugging purposes. +The project includes both a SteamVR Driver & Overlay (found in `overlay/`). Running the overlay exe automatically launches SteamVR. +The overlay is used for utilising methods that are not found in the OpenVR Driver API, such as discovering controllers. If you do not need this functionality, you can simply launch SteamVR. To launch SteamVR for debugging: * Click on the arrow next to `Local Windows Debugger` * Select `ALL_BUILD Debug Properties` * Navigate to the `Debugger` Property (under Configuration Properties) -* Set `Command` to the location of `vrstartup.exe` - * This is usually located `C:\Program Files (x86)\Steam\steamapps\common\SteamVR\bin\win64\vrstartup.exe` +* Set `Command` to the location of `vrstartup.exe` (to start just SteamVR) or the Overlay exe (to start the Ovlerlay and SteamVR). + * SteamVR's entry point (`vrstartup.exe`) is usually located `C:\Program Files (x86)\Steam\steamapps\common\SteamVR\bin\win64\vrstartup.exe` + * OpenGlove's Overlay is usually found in the build folder, `overlay\Debug\openglove_overlay.exe` + * Provide the full or relative path to the executable, such as `E:\opengloves-driver\build\overlay\Debug\openglove_overlay.exe`, or `$(solutionDir)overlay\Debug\openglove_overlay.exe` ![Debug Properties](https://cdn.discordapp.com/attachments/790676300552994826/840985376679002172/unknown.png) ![Debugging Configuration Properties](https://cdn.discordapp.com/attachments/790676300552994826/840985404202549318/unknown.png) diff --git a/CMakeLists.txt b/CMakeLists.txt index f85334cb..7ddb365b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,8 @@ endif() find_library(OPENVR_LIB openvr_api HINTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/openvr/lib/${PLATFORM_NAME}${PROCESSOR_ARCH}/" NO_DEFAULT_PATH ) +add_subdirectory("overlay") + set(DRIVER_NAME "openglove") set(OPENGLOVE_PROJECT "driver_${DRIVER_NAME}") @@ -36,12 +38,13 @@ add_library("${OPENGLOVE_PROJECT}" SHARED "${HEADERS}" "${SOURCES}") target_include_directories("${OPENGLOVE_PROJECT}" PUBLIC "${OPENVR_INCLUDE_DIR}") target_include_directories("${OPENGLOVE_PROJECT}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/") -target_link_libraries("${OPENGLOVE_PROJECT}" PUBLIC "${OPENVR_LIB}") +target_link_libraries("${OPENGLOVE_PROJECT}" PUBLIC "${OPENVR_LIB}" setupapi wsock32 ws2_32 bthprops) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Header Files" FILES ${HEADERS}) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" PREFIX "Source Files" FILES ${SOURCES}) set_property(TARGET "${OPENGLOVE_PROJECT}" PROPERTY CXX_STANDARD 17) + # Copy driver assets to output folder add_custom_command( TARGET ${OPENGLOVE_PROJECT} @@ -58,4 +61,12 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy $ $/${DRIVER_NAME}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH}/$ +) + +add_custom_command( + TARGET ${OPENGLOVE_PROJECT} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + $ + $/${DRIVER_NAME}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH} ) \ No newline at end of file diff --git a/README.md b/README.md index 5834d2cb..1b2fc51e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# OpenGlove Driver +# OpenGloves Driver -OpenGlove is an OpenVR driver for DIY Virtual Reality Gloves. Using OpenVR's driver interface we are able to provide support for many SteamVR/OpenVR games. +OpenGloves is an OpenVR driver for DIY Virtual Reality Gloves. Using OpenVR's driver interface we are able to provide support for many SteamVR/OpenVR games. ## Installation and Usage -**Download the latest on GitHub:** - * https://github.com/LucidVR/opengloves-driver/releases -**Release on Steam (Not yet available):** - * https://store.steampowered.com/app/1574050/OpenGloves - * The driver will be available on the Steam store soon. Feel free to wishlist. +### Download on Steam: +[![Steam Release](https://cdn.discordapp.com/attachments/790676300552994826/845412304219537439/openglovessteam.png)](https://store.steampowered.com/app/1574050/OpenGloves) + * We strongly recommend downloading the driver from Steam, to recieve automatic updates and UI settings. + +*Or download the latest on GitHub:* + * https://github.com/LucidVR/opengloves-driver/releases **Follow the wiki guide for configuring the driver** * https://github.com/LucidVR/opengloves-driver/wiki/Configuring-the-Driver @@ -17,7 +18,7 @@ OpenGlove is an OpenVR driver for DIY Virtual Reality Gloves. Using OpenVR's dri **Problems?** * Check [Troubleshooting](https://github.com/LucidVR/opengloves-driver/wiki/Troubleshooting) - * Didn't help? Contact us on the [Discord Server](https://discord.com/invite/Y6XTvnHDUC) + * Didn't help? Contact us on the [Community Discord Server](https://discord.gg/lucidvr) ## Building If you want to use the driver as-is, refer to [Installation and Usage](#Installation-and-Usage). If you are planning on modifying source files, refer to [BUILDING.md](https://github.com/LucidVR/opengloves-driver/blob/develop/BUILDING.md). @@ -34,8 +35,11 @@ If you are planning on modifying source files, refer to [BUILDING.md](https://gi ### Current features included in the driver * Finger flexion tracking +* Force feedback haptics * Positioning from controllers + trackers + * Automatic Calibration * Button/Joystick inputs + * A/B/Menu buttons, Joystick X/Y/Click * Communication Protocols: * Serial USB * Serial over Bluetooth @@ -43,7 +47,6 @@ If you are planning on modifying source files, refer to [BUILDING.md](https://gi ### Planned features * BLE Communication * Finger splay tracking -* Force feedback haptics * Vibration haptics @@ -56,4 +59,4 @@ Pull requests are very welcome. For major changes, please open an issue first to * Lucas VRTech (`LucidVR#0001`) ## Discord -https://discord.gg/RjV9T8jN2G +https://discord.gg/lucidvr diff --git a/include/Calibration.h b/include/Calibration.h new file mode 100644 index 00000000..f0fcd346 --- /dev/null +++ b/include/Calibration.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include "DeviceConfiguration.h" + +class Calibration { +public: + Calibration(); + + void StartCalibration(vr::DriverPose_t maintainPose); + + VRPoseConfiguration_t FinishCalibration(vr::TrackedDevicePose_t controllerPose, VRPoseConfiguration_t poseConfiguration, bool isRightHand); + + void CancelCalibration(); + + bool isCalibrating(); + + vr::DriverPose_t GetMaintainPose(); + +private: + vr::DriverPose_t m_maintainPose; + bool m_isCalibrating = false; +}; \ No newline at end of file diff --git a/include/Communication/BTSerialCommunicationManager.h b/include/Communication/BTSerialCommunicationManager.h index a1fc1d78..e4256565 100644 --- a/include/Communication/BTSerialCommunicationManager.h +++ b/include/Communication/BTSerialCommunicationManager.h @@ -1,59 +1,59 @@ #pragma once +#include -#include "CommunicationManager.h" -#include "DeviceConfiguration.h" -#include -#include #include #include -#include +#include #include +#include +#include +#include + +#include "CommunicationManager.h" +#include "DeviceConfiguration.h" #include "DriverLog.h" -#include -#include -#include + +#include +#include #include -#include -#ifdef _WIN32 - #pragma comment(lib, "Ws2_32.lib") - #pragma comment(lib, "Bthprops.lib") -#endif #define ARDUINO_WAIT_TIME 1000 - class BTSerialCommunicationManager : public ICommunicationManager { -public: - BTSerialCommunicationManager(const VRBTSerialConfiguration_t& configuration, std::unique_ptr encodingManager); - //connect to the device using serial - void Connect(); - //start a thread that listens for updates from the device and calls the callback with data - void BeginListener(const std::function& callback); - //returns if connected or not - bool IsConnected(); - //close the serial port - void Disconnect(); -private: - void ListenerThread(const std::function& callback); - bool ReceiveNextPacket(std::string &buff); - bool PurgeBuffer(); - bool getPairedEsp32BtAddress(); - bool startupWindowsSocket(); - bool connectToEsp32(); - bool sendMessageToEsp32(); - - bool m_isConnected; - std::atomic m_threadActive; - std::thread m_serialThread; - - std::unique_ptr m_encodingManager; - - VRBTSerialConfiguration_t m_btSerialConfiguration; - - BTH_ADDR m_esp32BtAddress; - SOCKADDR_BTH m_btSocketAddress; - SOCKET m_btClientSocket; - WCHAR* m_wcDeviceName; - //std::unique_ptr m_wcDeviceName; - + public: + BTSerialCommunicationManager(const VRBTSerialConfiguration_t& configuration, std::unique_ptr encodingManager); + // connect to the device using serial + void Connect(); + // start a thread that listens for updates from the device and calls the callback with data + void BeginListener(const std::function& callback); + // returns if connected or not + bool IsConnected(); + // close the serial port + void Disconnect(); + + void QueueSend(const VRFFBData_t& data); + + private: + void ListenerThread(const std::function& callback); + bool ReceiveNextPacket(std::string& buff); + bool getPairedDeviceBtAddress(); + bool startupWindowsSocket(); + bool connectToDevice(); + bool sendMessageToDevice(); + bool m_isConnected; + std::atomic m_threadActive; + std::thread m_serialThread; + + std::unique_ptr m_encodingManager; + + VRBTSerialConfiguration_t m_btSerialConfiguration; + + BTH_ADDR m_deviceBtAddress; + SOCKADDR_BTH m_btSocketAddress; + SOCKET m_btClientSocket; + WCHAR* m_wcDeviceName; + + std::mutex m_writeMutex; + + std::string m_writeString = "\n"; }; \ No newline at end of file diff --git a/include/Communication/CommunicationManager.h b/include/Communication/CommunicationManager.h index bdb2d054..b291cc4e 100644 --- a/include/Communication/CommunicationManager.h +++ b/include/Communication/CommunicationManager.h @@ -1,14 +1,18 @@ #pragma once #include #include + #include "Encode/EncodingManager.h" class ICommunicationManager { -public: - virtual void Connect() = 0; - virtual void BeginListener(const std::function& callback) = 0; - virtual bool IsConnected() = 0; - virtual void Disconnect() = 0; -private: - std::unique_ptr m_encodingManager; + public: + virtual void Connect() = 0; + virtual void BeginListener(const std::function& callback) = 0; + virtual bool IsConnected() = 0; + virtual void Disconnect() = 0; + + virtual void QueueSend(const VRFFBData_t& data) = 0; + + private: + std::unique_ptr m_encodingManager; }; \ No newline at end of file diff --git a/include/Communication/SerialCommunicationManager.h b/include/Communication/SerialCommunicationManager.h index 81e73e0e..97e1ca69 100644 --- a/include/Communication/SerialCommunicationManager.h +++ b/include/Communication/SerialCommunicationManager.h @@ -1,43 +1,64 @@ #pragma once -#include "CommunicationManager.h" -#include "DeviceConfiguration.h" #include + +#include #include #include +#include #include -#include +#include "CommunicationManager.h" +#include "DeviceConfiguration.h" #define ARDUINO_WAIT_TIME 1000 class SerialCommunicationManager : public ICommunicationManager { -public: - SerialCommunicationManager(const VRSerialConfiguration_t& configuration, std::unique_ptr encodingManager) : m_serialConfiguration(configuration), m_encodingManager(std::move(encodingManager)), m_isConnected(false), m_hSerial(0), m_errors(0) {}; - //connect to the device using serial - void Connect(); - //start a thread that listens for updates from the device and calls the callback with data - void BeginListener(const std::function& callback); - //returns if connected or not - bool IsConnected(); - //close the serial port - void Disconnect(); -private: - void ListenerThread(const std::function& callback); - bool ReceiveNextPacket(std::string &buff); - bool PurgeBuffer(); - - bool m_isConnected; - //Serial comm handler - HANDLE m_hSerial; - //Connection information - COMSTAT m_status; - //Error tracking - DWORD m_errors; - std::atomic m_threadActive; - std::thread m_serialThread; - - VRSerialConfiguration_t m_serialConfiguration; - - std::unique_ptr m_encodingManager; + public: + SerialCommunicationManager(const VRSerialConfiguration_t& configuration, std::unique_ptr encodingManager) + : m_serialConfiguration(configuration), + m_encodingManager(std::move(encodingManager)), + m_isConnected(false), + m_hSerial(0), + m_errors(0) + { + //initially no force feedback + VRFFBData_t data(0, 0, 0, 0, 0); + + m_writeString = m_encodingManager->Encode(data); + }; + // connect to the device using serial + void Connect(); + // start a thread that listens for updates from the device and calls the callback with data + void BeginListener(const std::function& callback); + // returns if connected or not + bool IsConnected(); + // close the serial port + void Disconnect(); + + void QueueSend(const VRFFBData_t& data); + + private: + void ListenerThread(const std::function& callback); + bool ReceiveNextPacket(std::string& buff); + bool PurgeBuffer(); + bool Write(); + + bool m_isConnected; + // Serial comm handler + HANDLE m_hSerial; + // Connection information + COMSTAT m_status; + // Error tracking + DWORD m_errors; + std::atomic m_threadActive; + std::thread m_serialThread; + + VRSerialConfiguration_t m_serialConfiguration; + + std::unique_ptr m_encodingManager; + + std::mutex m_writeMutex; + + std::string m_writeString; }; \ No newline at end of file diff --git a/include/ControllerDiscovery.h b/include/ControllerDiscovery.h new file mode 100644 index 00000000..063286d6 --- /dev/null +++ b/include/ControllerDiscovery.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include "openvr_driver.h" +#include "Util/NamedPipe.h" + +struct ControllerDiscoveryPipeData_t { + short controllerId; +}; + +class ControllerDiscovery { + public: + ControllerDiscovery(vr::ETrackedControllerRole role, std::function callback); + + void Start(); + void Stop(); + + private: + vr::ETrackedControllerRole m_role; + std::unique_ptr m_pipe; + std::function m_callback; +}; \ No newline at end of file diff --git a/include/ControllerPose.h b/include/ControllerPose.h index 85e8e27e..1358ab43 100644 --- a/include/ControllerPose.h +++ b/include/ControllerPose.h @@ -1,25 +1,40 @@ #pragma once #include +#include #include "DeviceConfiguration.h" +#include "ControllerDiscovery.h" +#include "Calibration.h" class ControllerPose { -public: - ControllerPose(vr::ETrackedControllerRole shadowDeviceOfRole, - std::string thisDeviceManufacturer, - VRPoseConfiguration_t poseConfiguration); - vr::DriverPose_t UpdatePose(); -private: - //We may not initially know what the id of the device that we want to shadow is. This method finds devices that have a specific type specified and that are not this one - void DiscoverController(); + public: + ControllerPose(vr::ETrackedControllerRole shadowDeviceOfRole, std::string thisDeviceManufacturer, + VRPoseConfiguration_t poseConfiguration); - uint32_t m_shadowControllerId = vr::k_unTrackedDeviceIndexInvalid; + vr::DriverPose_t UpdatePose(); - VRPoseConfiguration_t m_poseConfiguration; + void StartCalibration(); - vr::ETrackedControllerRole m_shadowDeviceOfRole = vr::TrackedControllerRole_Invalid; + void FinishCalibration(); - std::string m_thisDeviceManufacturer; + void CancelCalibration(); - bool ControllerPose::IsOtherRole(int32_t test); + bool isCalibrating(); + private: + uint32_t m_shadowControllerId = vr::k_unTrackedDeviceIndexInvalid; + + VRPoseConfiguration_t m_poseConfiguration; + + vr::ETrackedControllerRole m_shadowDeviceOfRole = vr::TrackedControllerRole_Invalid; + + std::string m_thisDeviceManufacturer; + + vr::TrackedDevicePose_t GetControllerPose(); + + bool IsOtherRole(int32_t test); + + bool isRightHand(); + + std::unique_ptr m_controllerDiscoverer; + std::unique_ptr m_calibration; }; \ No newline at end of file diff --git a/include/DeviceConfiguration.h b/include/DeviceConfiguration.h index 4cc28dc5..1d84fb33 100644 --- a/include/DeviceConfiguration.h +++ b/include/DeviceConfiguration.h @@ -15,6 +15,7 @@ enum VRCommunicationProtocol { enum VREncodingProtocol { LEGACY = 0, + ALPHA = 1, }; enum VRDeviceDriver { @@ -35,16 +36,16 @@ struct VRBTSerialConfiguration_t { }; struct VRPoseConfiguration_t { - VRPoseConfiguration_t(vr::HmdVector3_t offsetVector, vr::HmdQuaternion_t angleOffsetQuaternion, float poseOffset, + VRPoseConfiguration_t(vr::HmdVector3_t offsetVector, vr::HmdQuaternion_t angleOffsetQuaternion, float poseTimeOffset, bool controllerOverrideEnabled, int controllerIdOverride) : offsetVector(offsetVector), angleOffsetQuaternion(angleOffsetQuaternion), - poseOffset(poseOffset), + poseTimeOffset(poseTimeOffset), controllerOverrideEnabled(controllerOverrideEnabled), controllerIdOverride(controllerIdOverride) {}; vr::HmdVector3_t offsetVector; vr::HmdQuaternion_t angleOffsetQuaternion; - float poseOffset; + float poseTimeOffset; int controllerIdOverride; bool controllerOverrideEnabled; }; diff --git a/include/DeviceDriver/KnuckleDriver.h b/include/DeviceDriver/KnuckleDriver.h index 475f6653..40ced7b3 100644 --- a/include/DeviceDriver/KnuckleDriver.h +++ b/include/DeviceDriver/KnuckleDriver.h @@ -1,50 +1,52 @@ #pragma once #pragma once #include + #include #include -#include "Communication/CommunicationManager.h" -#include "Encode/LegacyEncodingManager.h" -#include "DeviceDriver/DeviceDriver.h" - #include "Bones.h" - +#include "Communication/CommunicationManager.h" #include "ControllerPose.h" #include "DeviceConfiguration.h" +#include "DeviceDriver/DeviceDriver.h" +#include "Encode/LegacyEncodingManager.h" +#include "ForceFeedback.h" class KnuckleDeviceDriver : public IDeviceDriver { -public: - KnuckleDeviceDriver(VRDeviceConfiguration_t configuration, std::unique_ptr communicationManager, std::string serialNumber); + public: + KnuckleDeviceDriver(VRDeviceConfiguration_t configuration, std::unique_ptr communicationManager, std::string serialNumber); + + vr::EVRInitError Activate(uint32_t unObjectId); + void Deactivate(); - vr::EVRInitError Activate(uint32_t unObjectId); - void Deactivate(); + void EnterStandby(); + void* GetComponent(const char* pchComponentNameAndVersion); + void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize); + vr::DriverPose_t GetPose(); + void RunFrame(); - void EnterStandby(); - void* GetComponent(const char* pchComponentNameAndVersion); - void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize); - vr::DriverPose_t GetPose(); - void RunFrame(); + std::string GetSerialNumber(); + bool IsActive(); - std::string GetSerialNumber(); - bool IsActive(); -private: - void StartDevice(); - bool IsRightHand() const; + private: + void StartDevice(); + bool IsRightHand() const; - bool m_hasActivated; - uint32_t m_driverId; + bool m_hasActivated; + uint32_t m_driverId; - vr::VRInputComponentHandle_t m_skeletalComponentHandle{}; - vr::VRInputComponentHandle_t m_inputComponentHandles[23]{}; + vr::VRInputComponentHandle_t m_skeletalComponentHandle{}; + vr::VRInputComponentHandle_t m_inputComponentHandles[23]{}; - vr::VRInputComponentHandle_t m_haptic{}; + vr::VRInputComponentHandle_t m_haptic{}; - vr::VRBoneTransform_t m_handTransforms[NUM_BONES]; + vr::VRBoneTransform_t m_handTransforms[NUM_BONES]; - VRDeviceConfiguration_t m_configuration; - std::unique_ptr m_communicationManager; - std::string m_serialNumber; + VRDeviceConfiguration_t m_configuration; + std::unique_ptr m_communicationManager; + std::string m_serialNumber; - std::unique_ptr m_controllerPose; + std::unique_ptr m_controllerPose; + std::unique_ptr m_ffbProvider; }; diff --git a/include/DeviceDriver/LucidGloveDriver.h b/include/DeviceDriver/LucidGloveDriver.h index 73d74978..d9b4f6fa 100644 --- a/include/DeviceDriver/LucidGloveDriver.h +++ b/include/DeviceDriver/LucidGloveDriver.h @@ -46,7 +46,7 @@ class LucidGloveDeviceDriver : public IDeviceDriver { uint32_t m_driverId; vr::VRInputComponentHandle_t m_skeletalComponentHandle{}; - vr::VRInputComponentHandle_t m_inputComponentHandles[14]{}; + vr::VRInputComponentHandle_t m_inputComponentHandles[15]{}; vr::VRBoneTransform_t m_handTransforms[NUM_BONES]; diff --git a/include/DeviceProvider.h b/include/DeviceProvider.h index 5ee814e5..9c682ca2 100644 --- a/include/DeviceProvider.h +++ b/include/DeviceProvider.h @@ -4,19 +4,14 @@ #define _WINSOCKAPI_ #include + #include -#include "DeviceConfiguration.h" -#include "DriverLog.h" #include "Communication/CommunicationManager.h" -#include "Communication/SerialCommunicationManager.h" -#include "Communication/BTSerialCommunicationManager.h" - -#include "Encode/EncodingManager.h" -#include "Encode/LegacyEncodingManager.h" - +#include "DeviceConfiguration.h" #include "DeviceDriver/DeviceDriver.h" - +#include "DriverLog.h" +#include "Encode/EncodingManager.h" /** This class instantiates all the device drivers you have, meaning if you've @@ -27,50 +22,49 @@ Take a look at the comment blocks for all the methods in IServerTrackedDevicePro too. **/ class DeviceProvider : public vr::IServerTrackedDeviceProvider { -public: - - /** - Initiailze and add your drivers to OpenVR here. - **/ - vr::EVRInitError Init(vr::IVRDriverContext* pDriverContext); - - /** - Called right before your driver is unloaded. - **/ - void Cleanup(); - - /** - Returns version of the openVR interface this driver works with. - **/ - const char* const* GetInterfaceVersions(); - - /** - Called every frame. Update your drivers here. - **/ - void RunFrame(); - - /** - Return true if standby mode should be blocked. False otherwise. - **/ - bool ShouldBlockStandbyMode(); - - /** - Called when OpenVR goes into stand-by mode, so you can tell your devices to go into stand-by mode - **/ - void EnterStandby(); - - /** - Called when OpenVR leaves stand-by mode. - **/ - void LeaveStandby(); - -private: - std::unique_ptr m_leftHand; - std::unique_ptr m_rightHand; - /** - * returns the configuration set in VRSettings for the device role given - **/ - VRDeviceConfiguration_t GetDeviceConfiguration(vr::ETrackedControllerRole role); - - std::unique_ptr InstantiateDeviceDriver(VRDeviceConfiguration_t configuration); + public: + /** + Initiailze and add your drivers to OpenVR here. + **/ + vr::EVRInitError Init(vr::IVRDriverContext* pDriverContext); + + /** + Called right before your driver is unloaded. + **/ + void Cleanup(); + + /** + Returns version of the openVR interface this driver works with. + **/ + const char* const* GetInterfaceVersions(); + + /** + Called every frame. Update your drivers here. + **/ + void RunFrame(); + + /** + Return true if standby mode should be blocked. False otherwise. + **/ + bool ShouldBlockStandbyMode(); + + /** + Called when OpenVR goes into stand-by mode, so you can tell your devices to go into stand-by mode + **/ + void EnterStandby(); + + /** + Called when OpenVR leaves stand-by mode. + **/ + void LeaveStandby(); + + private: + std::unique_ptr m_leftHand; + std::unique_ptr m_rightHand; + /** + * returns the configuration set in VRSettings for the device role given + **/ + VRDeviceConfiguration_t GetDeviceConfiguration(vr::ETrackedControllerRole role); + + std::unique_ptr InstantiateDeviceDriver(VRDeviceConfiguration_t configuration); }; \ No newline at end of file diff --git a/include/Encode/AlphaEncodingManager.h b/include/Encode/AlphaEncodingManager.h new file mode 100644 index 00000000..df2ace56 --- /dev/null +++ b/include/Encode/AlphaEncodingManager.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Encode/EncodingManager.h" + +class AlphaEncodingManager : public IEncodingManager { +public: + AlphaEncodingManager(float maxAnalogValue) : m_maxAnalogValue(maxAnalogValue){}; + + //decode the given string into a VRCommData_t + VRCommData_t Decode(std::string input); + std::string Encode(const VRFFBData_t& input); + + private: + std::string getArgumentSubstring(std::string str, char del); + + float m_maxAnalogValue; + const char* alphabet = "ABCDEFGHIJKLMNO"; // expand as more letters are added to manager +}; \ No newline at end of file diff --git a/include/Encode/EncodingManager.h b/include/Encode/EncodingManager.h index 39c0fae3..ea6d6778 100644 --- a/include/Encode/EncodingManager.h +++ b/include/Encode/EncodingManager.h @@ -1,9 +1,10 @@ #pragma once #include #include +#include "ForceFeedback.h" struct VRCommData_t { - VRCommData_t(std::array flexion, std::array splay, float joyX, float joyY, bool joyButton, bool trgButton, bool aButton, bool bButton, bool grab, bool pinch) : + VRCommData_t(std::array flexion, std::array splay, float joyX, float joyY, bool joyButton, bool trgButton, bool aButton, bool bButton, bool grab, bool pinch, bool menu, bool calibrate) : flexion(flexion), splay(splay), joyX(joyX), @@ -13,7 +14,10 @@ struct VRCommData_t { aButton(aButton), bButton(bButton), grab(grab), - pinch(pinch) {}; + pinch(pinch), + menu(menu), + calibrate(calibrate){}; + std::array flexion; std::array splay; @@ -25,29 +29,33 @@ struct VRCommData_t { bool bButton; bool grab; bool pinch; + bool menu; + bool calibrate; }; enum VRCommDataInputPosition { - FIN_PINKY, - FIN_RING, - FIN_MIDDLE, - FIN_INDEX, - FIN_THUMB, - JOY_X, - JOY_Y, - JOY_BTN, - BTN_TRG, - BTN_A, - BTN_B, - GES_GRAB, - GES_PINCH, - MAX, + FIN_PINKY, + FIN_RING, + FIN_MIDDLE, + FIN_INDEX, + FIN_THUMB, + JOY_X, + JOY_Y, + JOY_BTN, + BTN_TRG, + BTN_A, + BTN_B, + GES_GRAB, + GES_PINCH, + MAX, }; class IEncodingManager { -public: - virtual VRCommData_t Decode(std::string input) = 0; - virtual ~IEncodingManager() {}; -private: - float m_maxAnalogValue; + public: + virtual VRCommData_t Decode(std::string input) = 0; + virtual std::string Encode(const VRFFBData_t& data) = 0; + virtual ~IEncodingManager(){}; + + private: + float m_maxAnalogValue; }; \ No newline at end of file diff --git a/include/Encode/LegacyEncodingManager.h b/include/Encode/LegacyEncodingManager.h index 6ea31de1..5f84e382 100644 --- a/include/Encode/LegacyEncodingManager.h +++ b/include/Encode/LegacyEncodingManager.h @@ -1,13 +1,16 @@ #pragma once #include +#include "ForceFeedback.h" class LegacyEncodingManager : public IEncodingManager { -public: - LegacyEncodingManager(float maxAnalogValue) : m_maxAnalogValue(maxAnalogValue){}; - - //decode the given string into a VRCommData_t - VRCommData_t Decode(std::string input); -private: - float m_maxAnalogValue; + public: + LegacyEncodingManager(float maxAnalogValue) : m_maxAnalogValue(maxAnalogValue){}; + + VRCommData_t Decode(std::string input); + + std::string Encode(const VRFFBData_t& input); + + private: + float m_maxAnalogValue; }; \ No newline at end of file diff --git a/include/ForceFeedback.h b/include/ForceFeedback.h new file mode 100644 index 00000000..5d49180c --- /dev/null +++ b/include/ForceFeedback.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "openvr_driver.h" +#include "Util/NamedPipe.h" + +struct VRFFBData_t { + VRFFBData_t(short thumbCurl, short indexCurl, short middleCurl, short ringCurl, short pinkyCurl) + : thumbCurl(thumbCurl), indexCurl(indexCurl), middleCurl(middleCurl), ringCurl(ringCurl), pinkyCurl(pinkyCurl){}; + + short thumbCurl; + short indexCurl; + short middleCurl; + short ringCurl; + short pinkyCurl; +}; + +class FFBListener { + public: + FFBListener(std::function callback, vr::ETrackedControllerRole role); + void Start(); + void Stop(); + + private: + std::function m_callback; + vr::ETrackedControllerRole m_role; + + std::unique_ptr m_pipe; +}; \ No newline at end of file diff --git a/include/Quaternion.h b/include/Quaternion.h index 03804ea6..2a7e0ecf 100644 --- a/include/Quaternion.h +++ b/include/Quaternion.h @@ -5,6 +5,8 @@ double DegToRad(int degrees); double RadToDeg(double rad); + + //get the quaternion for roation from a matrix vr::HmdQuaternion_t GetRotation(const vr::HmdMatrix34_t& matrix); vr::HmdVector3_t GetPosition(const vr::HmdMatrix34_t& matrix); @@ -17,3 +19,9 @@ vr::HmdQuaternion_t QuaternionFromAngle(const double& xx, const double& yy, cons vr::HmdQuaternion_t EulerToQuaternion(const double& x, const double& y, const double& z); vr::HmdMatrix33_t GetRotationMatrix(const vr::HmdMatrix34_t& matrix); vr::HmdVector3_t MultiplyMatrix(const vr::HmdMatrix33_t& matrix, const vr::HmdVector3_t& vector); +vr::HmdMatrix33_t QuaternionToMatrix(const vr::HmdQuaternion_t q); + +double QuatNorm(const vr::HmdQuaternion_t q); +vr::HmdQuaternion_t QuatConjugate(const vr::HmdQuaternion_t q); + +vr::HmdVector3_t QuaternionToEuler(const vr::HmdQuaternion_t q); \ No newline at end of file diff --git a/include/Util/NamedPipe.h b/include/Util/NamedPipe.h new file mode 100644 index 00000000..672ee8aa --- /dev/null +++ b/include/Util/NamedPipe.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include +#include + +typedef struct { + OVERLAPPED oOverlap; + HANDLE hPipeInst; + LPVOID chRequest; + DWORD cbRead; + DWORD cbToWrite; + std::function callback; +} PIPEINST, *LPPIPEINST; + +class NamedPipeUtil { + public: + NamedPipeUtil(std::string pipeName, size_t pipeSize); + ~NamedPipeUtil(); + + bool Start(const std::function& callback); + void Stop(); + + private: + void PipeListenerThread(const std::function& callback); + void ClosePipe(); + bool CreateAndConnectInstance(LPOVERLAPPED lpo, std::string& pipeName); + bool ConnectToNewClient(LPOVERLAPPED lpo); + + std::string m_pipeName; + size_t m_pipeSize; + + HANDLE m_hPipe; + + std::thread m_pipeThread; + + std::atomic m_listenerActive; + std::atomic m_clientConnected; + LPPIPEINST m_lpPipeInst; +}; \ No newline at end of file diff --git a/openglove/bin/win64/DLL GOES HERE.txt b/openglove/bin/win64/DLL GOES HERE.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/openglove/resources/input/openglove_profile.json b/openglove/resources/input/openglove_profile.json index caa3b78e..4aa2a615 100644 --- a/openglove/resources/input/openglove_profile.json +++ b/openglove/resources/input/openglove_profile.json @@ -40,11 +40,6 @@ "binding_image_point": [ 11, 60 ], "order": 2 }, - "/input/system/click": { - "type": "button", - "binding_image_point": [ 67, 81 ], - "order": 3 - }, "/input/A/click": { "type": "button", "binding_image_point": [ 67, 81 ], @@ -94,6 +89,13 @@ "type": "trigger", "visibility": "InputValueVisibility_AvailableButHidden", "binding_image_point": [ 56, 86 ] + }, + "/input/system" : { + "type" : "button", + "click" : true, + "touch" : true, + "binding_image_point" : [ 34,45 ], + "order" : 1 } } } \ No newline at end of file diff --git a/openglove/resources/settings/default.vrsettings b/openglove/resources/settings/default.vrsettings index 0efecd6e..eae066b3 100644 --- a/openglove/resources/settings/default.vrsettings +++ b/openglove/resources/settings/default.vrsettings @@ -4,9 +4,9 @@ "__title": "OpenGlove Configuration", "left_enabled": true, "right_enabled": true, - "communication_protocol": 0, //title:Communication Protocol - "device_driver": 1, //title: Device Driver - "encoding_protocol": 0 //title:Encoding Protocol + "communication_protocol": 0, //title:Communication Method + "device_driver": 1, //title:Device Driver Emulation + "encoding_protocol": 1 //title:Encoding Protocol }, "device_lucidgloves": { @@ -17,7 +17,7 @@ }, "device_knuckles": { - "__title": "Knuckle Driver", + "__title": "Knuckles Emulation", "__type": "device_driver:1", "left_serial_number": "LHR-E217CD00", "right_serial_number": "LHR-E217CD01" @@ -28,16 +28,16 @@ "right_x_offset_position": -0.1, "right_y_offset_position": -0.08, "right_z_offset_position": -0.03, - "right_x_offset_degrees": -40, - "right_y_offset_degrees": 0, - "right_z_offset_degrees": 0, + "right_x_offset_degrees": 0.0, + "right_y_offset_degrees": 0.0, + "right_z_offset_degrees": 0.0, "left_x_offset_position": 0.1, "left_y_offset_position": -0.08, "left_z_offset_position": -0.03, - "left_x_offset_degrees": -40, - "left_y_offset_degrees": 0, - "left_z_offset_degrees": 0, - "pose_offset": -0.01, + "left_x_offset_degrees": 0.0, + "left_y_offset_degrees": 0.0, + "left_z_offset_degrees": 0.0, + "pose_time_offset": -0.01, "controller_override": false, "controller_override_left": 3, "controller_override_right": 4 @@ -49,16 +49,23 @@ "left_port": "\\\\.\\COM4", "right_port": "\\\\.\\COM5" }, - "communication_btserial": { + "communication_btserial": + { "__type": "communication_protocol:1", "__title": "Bluetooth Serial", - "left_name": "lucidgloves-left", - "right_name": "lucidgloves-right" - }, + "left_name": "lucidgloves-left", + "right_name": "lucidgloves-right" + }, "encoding_legacy": { "__type": "encoding_protocol: 0", - "__title": "Encoding Protocol", + "__title": "Legacy Encoding", + "max_analog_value": 1023 + }, + "encoding_alpha": + { + "__type": "encoding_protocol: 1", + "__title": "Alpha Protocol", "max_analog_value": 1023 } -} \ No newline at end of file +} diff --git a/overlay/CMakeLists.txt b/overlay/CMakeLists.txt new file mode 100644 index 00000000..a7a9b738 --- /dev/null +++ b/overlay/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required (VERSION 3.8) + + +# Add source to this project's executable. +add_executable (openglove_overlay WIN32 "main.cpp" "main.h") + +target_include_directories("openglove_overlay" PUBLIC "${OPENVR_INCLUDE_DIR}") +target_link_libraries("openglove_overlay" PUBLIC "${OPENVR_LIB}") + +add_custom_command(TARGET openglove_overlay POST_BUILD +COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${PROJECT_SOURCE_DIR}/libraries/openvr/bin/win64/openvr_api.dll" + $) \ No newline at end of file diff --git a/overlay/main.cpp b/overlay/main.cpp new file mode 100644 index 00000000..9a62d066 --- /dev/null +++ b/overlay/main.cpp @@ -0,0 +1,157 @@ +#include "main.h" + +#include +#include +#include + +std::atomic appActive = true; + +const std::string ourManufacturer = "LucidVR"; + +std::string GetLastErrorAsString() { + DWORD errorMessageID = ::GetLastError(); + if (errorMessageID == 0) { + return std::string(); + } + + LPSTR messageBuffer = nullptr; + + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, + NULL); + + std::string message(messageBuffer, size); + + LocalFree(messageBuffer); + + return message; +} + +void GetAndSendControllerId(int id, vr::ETrackedControllerRole role) { + std::unique_ptr pipeHelper = std::make_unique(); + std::string pipeName; + + if (role == vr::ETrackedControllerRole::TrackedControllerRole_LeftHand) { + pipeName = "\\\\.\\pipe\\vrapplication\\discovery\\left"; + } else { + pipeName = "\\\\.\\pipe\\vrapplication\\discovery\\right"; + } + + ControllerPipeData data; + data.controllerId = id; + + pipeHelper->ConnectAndSendPipe(pipeName, data); +} + +void DiscoverController(vr::ETrackedControllerRole role) { + int lastFound = -1; + int curFound = -1; + + while (appActive) { + for (int32_t i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { + char thisManufacturer[1024]; + uint32_t err = vr::VRSystem()->GetStringTrackedDeviceProperty( + i, vr::ETrackedDeviceProperty::Prop_ManufacturerName_String, thisManufacturer, + sizeof(thisManufacturer)); + + std::string sThisManufacturer(thisManufacturer); + + if (ourManufacturer == sThisManufacturer) continue; + + short deviceRole = vr::VRSystem()->GetControllerRoleForTrackedDeviceIndex(i); + + int32_t controllerHint = vr::VRSystem()->GetInt32TrackedDeviceProperty( + i, vr::ETrackedDeviceProperty::Prop_ControllerRoleHint_Int32); + + if (controllerHint == role) { + curFound = i; + break; + } + + int controllerType = vr::VRSystem()->GetInt32TrackedDeviceProperty( + i, vr::ETrackedDeviceProperty::Prop_DeviceClass_Int32); + + if (controllerType == vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker || + controllerType == vr::ETrackedDeviceClass::TrackedDeviceClass_Controller) { + if (role == deviceRole) { + curFound = i; + } + } + + } + + if (curFound != lastFound) { + GetAndSendControllerId(curFound, role); + lastFound = curFound; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +} + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { + vr::EVRInitError error; + VR_Init(&error, vr::VRApplication_Background); + + if (error == vr::EVRInitError::VRInitError_None) { + std::thread leftControllerThread = std::thread( + &DiscoverController, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + + std::thread rightControllerThread = std::thread( + &DiscoverController, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + + while (appActive) { + vr::VREvent_t event; + while (vr::VRSystem() && vr::VRSystem()->PollNextEvent(&event, sizeof event)) { + switch (event.eventType) { + case vr::VREvent_Quit: + + appActive = false; + vr::VRSystem()->AcknowledgeQuit_Exiting(); + + leftControllerThread.join(); + rightControllerThread.join(); + vr::VR_Shutdown(); + return 0; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + } else { + vr::VR_Shutdown(); + } + return 0; +} + +PipeHelper::PipeHelper() {} + +bool PipeHelper::ConnectAndSendPipe(const std::string& pipeName, ControllerPipeData data) { + while (1) { + m_pipeHandle = CreateFile(pipeName.c_str(), // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file + + if (m_pipeHandle != INVALID_HANDLE_VALUE) break; + + if (GetLastError() != ERROR_PIPE_BUSY) { + return -1; + } + + if (!WaitNamedPipe(pipeName.c_str(), 1000)) { + return -1; + } + } + + DWORD dwWritten; + + WriteFile(m_pipeHandle, (LPCVOID)&data, sizeof(ControllerPipeData), &dwWritten, NULL); + + CloseHandle(m_pipeHandle); + + return true; +} \ No newline at end of file diff --git a/overlay/main.h b/overlay/main.h new file mode 100644 index 00000000..9392a44d --- /dev/null +++ b/overlay/main.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +struct ControllerPipeData { + short controllerId; +}; + +class PipeHelper { + public: + PipeHelper(); + + bool ConnectAndSendPipe(const std::string& pipeName, ControllerPipeData data); + void Send(); + + private: + HANDLE m_pipeHandle; +}; \ No newline at end of file diff --git a/src/Bones.cpp b/src/Bones.cpp index 3de54d92..0fb5cafd 100644 --- a/src/Bones.cpp +++ b/src/Bones.cpp @@ -4,224 +4,215 @@ // these poses come from Valve's Index Controllers so share the same root bone to wrist // geometry assumptions. vr::VRBoneTransform_t rightOpenPose[NUM_BONES] = { -{ { 0.000000f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ { 0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, 0.920279f, -0.379296f} }, -{ { 0.012083f, 0.028070f, 0.025050f, 1.000000f}, { 0.567418f, -0.464112f, 0.623374f, -0.272106f} }, -{ {-0.040406f, -0.000000f, 0.000000f, 1.000000f}, { 0.994838f, 0.082939f, 0.019454f, 0.055130f} }, -{ {-0.032517f, -0.000000f, -0.000000f, 1.000000f}, { 0.974793f, -0.003213f, 0.021867f, -0.222015f} }, -{ {-0.030464f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ {-0.000632f, 0.026866f, 0.015002f, 1.000000f}, { 0.421979f, -0.644251f, 0.422133f, 0.478202f} }, -{ {-0.074204f, 0.005002f, -0.000234f, 1.000000f}, { 0.995332f, 0.007007f, -0.039124f, 0.087949f} }, -{ {-0.043930f, 0.000000f, 0.000000f, 1.000000f}, { 0.997891f, 0.045808f, 0.002142f, -0.045943f} }, -{ {-0.028695f, -0.000000f, -0.000000f, 1.000000f}, { 0.999649f, 0.001850f, -0.022782f, -0.013409f} }, -{ {-0.022821f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, 0.000000f, -0.000000f} }, -{ {-0.002177f, 0.007120f, 0.016319f, 1.000000f}, { 0.541276f, -0.546723f, 0.460749f, 0.442520f} }, -{ {-0.070953f, -0.000779f, -0.000997f, 1.000000f}, { 0.980294f, -0.167261f, -0.078959f, 0.069368f} }, -{ {-0.043108f, -0.000000f, -0.000000f, 1.000000f}, { 0.997947f, 0.018493f, 0.013192f, 0.059886f} }, -{ {-0.033266f, -0.000000f, -0.000000f, 1.000000f}, { 0.997394f, -0.003328f, -0.028225f, -0.066315f} }, -{ {-0.025892f, 0.000000f, -0.000000f, 1.000000f}, { 0.999195f, -0.000000f, 0.000000f, 0.040126f} }, -{ {-0.000513f, -0.006545f, 0.016348f, 1.000000f}, { 0.550143f, -0.516692f, 0.429888f, 0.495548f} }, -{ {-0.065876f, -0.001786f, -0.000693f, 1.000000f}, { 0.990420f, -0.058696f, -0.101820f, 0.072495f} }, -{ {-0.040697f, -0.000000f, -0.000000f, 1.000000f}, { 0.999545f, -0.002240f, 0.000004f, 0.030081f} }, -{ {-0.028747f, 0.000000f, 0.000000f, 1.000000f}, { 0.999102f, -0.000721f, -0.012693f, 0.040420f} }, -{ {-0.022430f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ { 0.002478f, -0.018981f, 0.015214f, 1.000000f}, { 0.523940f, -0.526918f, 0.326740f, 0.584025f} }, -{ {-0.062878f, -0.002844f, -0.000332f, 1.000000f}, { 0.986609f, -0.059615f, -0.135163f, 0.069132f} }, -{ {-0.030220f, -0.000000f, -0.000000f, 1.000000f}, { 0.994317f, 0.001896f, -0.000132f, 0.106446f} }, -{ {-0.018187f, -0.000000f, -0.000000f, 1.000000f}, { 0.995931f, -0.002010f, -0.052079f, -0.073526f} }, -{ {-0.018018f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ { 0.006059f, 0.056285f, 0.060064f, 1.000000f}, { 0.737238f, 0.202745f, -0.594267f, -0.249441f} }, -{ { 0.040416f, -0.043018f, 0.019345f, 1.000000f}, {-0.290331f, 0.623527f, 0.663809f, 0.293734f} }, -{ { 0.039354f, -0.075674f, 0.047048f, 1.000000f}, {-0.187047f, 0.678062f, 0.659285f, 0.265683f} }, -{ { 0.038340f, -0.090987f, 0.082579f, 1.000000f}, {-0.183037f, 0.736793f, 0.634757f, 0.143936f} }, -{ { 0.031806f, -0.087214f, 0.121015f, 1.000000f}, {-0.003659f, 0.758407f, 0.639342f, 0.126678f} }, + {{0.000000f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, 0.920279f, -0.379296f}}, + {{0.012083f, 0.028070f, 0.025050f, 1.000000f}, {0.567418f, -0.464112f, 0.623374f, -0.272106f}}, + {{-0.040406f, -0.000000f, 0.000000f, 1.000000f}, {0.994838f, 0.082939f, 0.019454f, 0.055130f}}, + {{-0.032517f, -0.000000f, -0.000000f, 1.000000f}, {0.974793f, -0.003213f, 0.021867f, -0.222015f}}, + {{-0.030464f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{-0.000632f, 0.026866f, 0.015002f, 1.000000f}, {0.421979f, -0.644251f, 0.422133f, 0.478202f}}, + {{-0.074204f, 0.005002f, -0.000234f, 1.000000f}, {0.995332f, 0.007007f, -0.039124f, 0.087949f}}, + {{-0.043930f, 0.000000f, 0.000000f, 1.000000f}, {0.997891f, 0.045808f, 0.002142f, -0.045943f}}, + {{-0.028695f, -0.000000f, -0.000000f, 1.000000f}, {0.999649f, 0.001850f, -0.022782f, -0.013409f}}, + {{-0.022821f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, 0.000000f, -0.000000f}}, + {{-0.002177f, 0.007120f, 0.016319f, 1.000000f}, {0.541276f, -0.546723f, 0.460749f, 0.442520f}}, + {{-0.070953f, -0.000779f, -0.000997f, 1.000000f}, {0.980294f, -0.167261f, -0.078959f, 0.069368f}}, + {{-0.043108f, -0.000000f, -0.000000f, 1.000000f}, {0.997947f, 0.018493f, 0.013192f, 0.059886f}}, + {{-0.033266f, -0.000000f, -0.000000f, 1.000000f}, {0.997394f, -0.003328f, -0.028225f, -0.066315f}}, + {{-0.025892f, 0.000000f, -0.000000f, 1.000000f}, {0.999195f, -0.000000f, 0.000000f, 0.040126f}}, + {{-0.000513f, -0.006545f, 0.016348f, 1.000000f}, {0.550143f, -0.516692f, 0.429888f, 0.495548f}}, + {{-0.065876f, -0.001786f, -0.000693f, 1.000000f}, {0.990420f, -0.058696f, -0.101820f, 0.072495f}}, + {{-0.040697f, -0.000000f, -0.000000f, 1.000000f}, {0.999545f, -0.002240f, 0.000004f, 0.030081f}}, + {{-0.028747f, 0.000000f, 0.000000f, 1.000000f}, {0.999102f, -0.000721f, -0.012693f, 0.040420f}}, + {{-0.022430f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{0.002478f, -0.018981f, 0.015214f, 1.000000f}, {0.523940f, -0.526918f, 0.326740f, 0.584025f}}, + {{-0.062878f, -0.002844f, -0.000332f, 1.000000f}, {0.986609f, -0.059615f, -0.135163f, 0.069132f}}, + {{-0.030220f, -0.000000f, -0.000000f, 1.000000f}, {0.994317f, 0.001896f, -0.000132f, 0.106446f}}, + {{-0.018187f, -0.000000f, -0.000000f, 1.000000f}, {0.995931f, -0.002010f, -0.052079f, -0.073526f}}, + {{-0.018018f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{0.006059f, 0.056285f, 0.060064f, 1.000000f}, {0.737238f, 0.202745f, -0.594267f, -0.249441f}}, + {{0.040416f, -0.043018f, 0.019345f, 1.000000f}, {-0.290331f, 0.623527f, 0.663809f, 0.293734f}}, + {{0.039354f, -0.075674f, 0.047048f, 1.000000f}, {-0.187047f, 0.678062f, 0.659285f, 0.265683f}}, + {{0.038340f, -0.090987f, 0.082579f, 1.000000f}, {-0.183037f, 0.736793f, 0.634757f, 0.143936f}}, + {{0.031806f, -0.087214f, 0.121015f, 1.000000f}, {-0.003659f, 0.758407f, 0.639342f, 0.126678f}}, }; -vr::VRBoneTransform_t rightFistPose[NUM_BONES] = -{ -{ { 0.000000f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ { 0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, 0.920279f, -0.379296f} }, -{ { 0.016305f, 0.027529f, 0.017800f, 1.000000f}, { 0.483332f, -0.225703f, 0.836342f, -0.126413f} }, -{ {-0.040406f, -0.000000f, 0.000000f, 1.000000f}, { 0.894335f, -0.013302f, -0.082902f, 0.439448f} }, -{ {-0.032517f, -0.000000f, -0.000000f, 1.000000f}, { 0.842428f, 0.000655f, 0.001244f, 0.538807f} }, -{ {-0.030464f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ {-0.003802f, 0.021514f, 0.012803f, 1.000000f}, { 0.395174f, -0.617314f, 0.449185f, 0.510874f} }, -{ {-0.074204f, 0.005002f, -0.000234f, 1.000000f}, { 0.737291f, -0.032006f, -0.115013f, 0.664944f} }, -{ {-0.043287f, 0.000000f, 0.000000f, 1.000000f}, { 0.611381f, 0.003287f, 0.003823f, 0.791321f} }, -{ {-0.028275f, -0.000000f, -0.000000f, 1.000000f}, { 0.745388f, -0.000684f, -0.000945f, 0.666629f} }, -{ {-0.022821f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, 0.000000f, -0.000000f} }, -{ {-0.005787f, 0.006806f, 0.016534f, 1.000000f}, { 0.522315f, -0.514203f, 0.483700f, 0.478348f} }, -{ {-0.070953f, -0.000779f, -0.000997f, 1.000000f}, { 0.723653f, -0.097901f, 0.048546f, 0.681458f} }, -{ {-0.043108f, -0.000000f, -0.000000f, 1.000000f}, { 0.637464f, -0.002366f, -0.002831f, 0.770472f} }, -{ {-0.033266f, -0.000000f, -0.000000f, 1.000000f}, { 0.658008f, 0.002610f, 0.003196f, 0.753000f} }, -{ {-0.025892f, 0.000000f, -0.000000f, 1.000000f}, { 0.999195f, -0.000000f, 0.000000f, 0.040126f} }, -{ {-0.004123f, -0.006858f, 0.016563f, 1.000000f}, { 0.523374f, -0.489609f, 0.463997f, 0.520644f} }, -{ {-0.065876f, -0.001786f, -0.000693f, 1.000000f}, { 0.759970f, -0.055609f, 0.011571f, 0.647471f} }, -{ {-0.040331f, -0.000000f, -0.000000f, 1.000000f}, { 0.664315f, 0.001595f, 0.001967f, 0.747449f} }, -{ {-0.028489f, 0.000000f, 0.000000f, 1.000000f}, { 0.626957f, -0.002784f, -0.003234f, 0.779042f} }, -{ {-0.022430f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ {-0.001131f, -0.019295f, 0.015429f, 1.000000f}, { 0.477833f, -0.479766f, 0.379935f, 0.630198f} }, -{ {-0.062878f, -0.002844f, -0.000332f, 1.000000f}, { 0.827001f, 0.034282f, 0.003440f, 0.561144f} }, -{ {-0.029874f, -0.000000f, -0.000000f, 1.000000f}, { 0.702185f, -0.006716f, -0.009289f, 0.711903f} }, -{ {-0.017979f, -0.000000f, -0.000000f, 1.000000f}, { 0.676853f, 0.007956f, 0.009917f, 0.736009f} }, -{ {-0.018018f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ {-0.019716f, 0.002802f, 0.093937f, 1.000000f}, { 0.377286f, -0.540831f, -0.150446f, 0.736562f} }, -{ {-0.000171f, 0.016473f, 0.096515f, 1.000000f}, {-0.006456f, 0.022747f, 0.932927f, 0.359287f} }, -{ {-0.000448f, 0.001536f, 0.116543f, 1.000000f}, {-0.039357f, 0.105143f, 0.928833f, 0.353079f} }, -{ {-0.003949f, -0.014869f, 0.130608f, 1.000000f}, {-0.055071f, 0.068695f, 0.944016f, 0.317933f} }, -{ {-0.003263f, -0.034685f, 0.139926f, 1.000000f}, { 0.019690f, -0.100741f, 0.957331f, 0.270149f} }, +vr::VRBoneTransform_t rightFistPose[NUM_BONES] = { + {{0.000000f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, 0.920279f, -0.379296f}}, + {{0.016305f, 0.027529f, 0.017800f, 1.000000f}, {0.483332f, -0.225703f, 0.836342f, -0.126413f}}, + {{-0.040406f, -0.000000f, 0.000000f, 1.000000f}, {0.894335f, -0.013302f, -0.082902f, 0.439448f}}, + {{-0.032517f, -0.000000f, -0.000000f, 1.000000f}, {0.842428f, 0.000655f, 0.001244f, 0.538807f}}, + {{-0.030464f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{-0.003802f, 0.021514f, 0.012803f, 1.000000f}, {0.395174f, -0.617314f, 0.449185f, 0.510874f}}, + {{-0.074204f, 0.005002f, -0.000234f, 1.000000f}, {0.737291f, -0.032006f, -0.115013f, 0.664944f}}, + {{-0.043287f, 0.000000f, 0.000000f, 1.000000f}, {0.611381f, 0.003287f, 0.003823f, 0.791321f}}, + {{-0.028275f, -0.000000f, -0.000000f, 1.000000f}, {0.745388f, -0.000684f, -0.000945f, 0.666629f}}, + {{-0.022821f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, 0.000000f, -0.000000f}}, + {{-0.005787f, 0.006806f, 0.016534f, 1.000000f}, {0.522315f, -0.514203f, 0.483700f, 0.478348f}}, + {{-0.070953f, -0.000779f, -0.000997f, 1.000000f}, {0.723653f, -0.097901f, 0.048546f, 0.681458f}}, + {{-0.043108f, -0.000000f, -0.000000f, 1.000000f}, {0.637464f, -0.002366f, -0.002831f, 0.770472f}}, + {{-0.033266f, -0.000000f, -0.000000f, 1.000000f}, {0.658008f, 0.002610f, 0.003196f, 0.753000f}}, + {{-0.025892f, 0.000000f, -0.000000f, 1.000000f}, {0.999195f, -0.000000f, 0.000000f, 0.040126f}}, + {{-0.004123f, -0.006858f, 0.016563f, 1.000000f}, {0.523374f, -0.489609f, 0.463997f, 0.520644f}}, + {{-0.065876f, -0.001786f, -0.000693f, 1.000000f}, {0.759970f, -0.055609f, 0.011571f, 0.647471f}}, + {{-0.040331f, -0.000000f, -0.000000f, 1.000000f}, {0.664315f, 0.001595f, 0.001967f, 0.747449f}}, + {{-0.028489f, 0.000000f, 0.000000f, 1.000000f}, {0.626957f, -0.002784f, -0.003234f, 0.779042f}}, + {{-0.022430f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{-0.001131f, -0.019295f, 0.015429f, 1.000000f}, {0.477833f, -0.479766f, 0.379935f, 0.630198f}}, + {{-0.062878f, -0.002844f, -0.000332f, 1.000000f}, {0.827001f, 0.034282f, 0.003440f, 0.561144f}}, + {{-0.029874f, -0.000000f, -0.000000f, 1.000000f}, {0.702185f, -0.006716f, -0.009289f, 0.711903f}}, + {{-0.017979f, -0.000000f, -0.000000f, 1.000000f}, {0.676853f, 0.007956f, 0.009917f, 0.736009f}}, + {{-0.018018f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{-0.019716f, 0.002802f, 0.093937f, 1.000000f}, {0.377286f, -0.540831f, -0.150446f, 0.736562f}}, + {{-0.000171f, 0.016473f, 0.096515f, 1.000000f}, {-0.006456f, 0.022747f, 0.932927f, 0.359287f}}, + {{-0.000448f, 0.001536f, 0.116543f, 1.000000f}, {-0.039357f, 0.105143f, 0.928833f, 0.353079f}}, + {{-0.003949f, -0.014869f, 0.130608f, 1.000000f}, {-0.055071f, 0.068695f, 0.944016f, 0.317933f}}, + {{-0.003263f, -0.034685f, 0.139926f, 1.000000f}, {0.019690f, -0.100741f, 0.957331f, 0.270149f}}, }; vr::VRBoneTransform_t leftOpenPose[NUM_BONES] = { -{ { 0.000000f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ {-0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, -0.920279f, 0.379296f} }, -{ {-0.012083f, 0.028070f, 0.025050f, 1.000000f}, { 0.464112f, 0.567418f, 0.272106f, 0.623374f} }, -{ { 0.040406f, 0.000000f, -0.000000f, 1.000000f}, { 0.994838f, 0.082939f, 0.019454f, 0.055130f} }, -{ { 0.032517f, 0.000000f, 0.000000f, 1.000000f}, { 0.974793f, -0.003213f, 0.021867f, -0.222015f} }, -{ { 0.030464f, -0.000000f, -0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ { 0.000632f, 0.026866f, 0.015002f, 1.000000f}, { 0.644251f, 0.421979f, -0.478202f, 0.422133f} }, -{ { 0.074204f, -0.005002f, 0.000234f, 1.000000f}, { 0.995332f, 0.007007f, -0.039124f, 0.087949f} }, -{ { 0.043930f, -0.000000f, -0.000000f, 1.000000f}, { 0.997891f, 0.045808f, 0.002142f, -0.045943f} }, -{ { 0.028695f, 0.000000f, 0.000000f, 1.000000f}, { 0.999649f, 0.001850f, -0.022782f, -0.013409f} }, -{ { 0.022821f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, -0.000000f, 0.000000f, -0.000000f} }, -{ { 0.002177f, 0.007120f, 0.016319f, 1.000000f}, { 0.546723f, 0.541276f, -0.442520f, 0.460749f} }, -{ { 0.070953f, 0.000779f, 0.000997f, 1.000000f}, { 0.980294f, -0.167261f, -0.078959f, 0.069368f} }, -{ { 0.043108f, 0.000000f, 0.000000f, 1.000000f}, { 0.997947f, 0.018493f, 0.013192f, 0.059886f} }, -{ { 0.033266f, 0.000000f, 0.000000f, 1.000000f}, { 0.997394f, -0.003328f, -0.028225f, -0.066315f} }, -{ { 0.025892f, -0.000000f, 0.000000f, 1.000000f}, { 0.999195f, -0.000000f, 0.000000f, 0.040126f} }, -{ { 0.000513f, -0.006545f, 0.016348f, 1.000000f}, { 0.516692f, 0.550143f, -0.495548f, 0.429888f} }, -{ { 0.065876f, 0.001786f, 0.000693f, 1.000000f}, { 0.990420f, -0.058696f, -0.101820f, 0.072495f} }, -{ { 0.040697f, 0.000000f, 0.000000f, 1.000000f}, { 0.999545f, -0.002240f, 0.000004f, 0.030081f} }, -{ { 0.028747f, -0.000000f, -0.000000f, 1.000000f}, { 0.999102f, -0.000721f, -0.012693f, 0.040420f} }, -{ { 0.022430f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ {-0.002478f, -0.018981f, 0.015214f, 1.000000f}, { 0.526918f, 0.523940f, -0.584025f, 0.326740f} }, -{ { 0.062878f, 0.002844f, 0.000332f, 1.000000f}, { 0.986609f, -0.059615f, -0.135163f, 0.069132f} }, -{ { 0.030220f, 0.000000f, 0.000000f, 1.000000f}, { 0.994317f, 0.001896f, -0.000132f, 0.106446f} }, -{ { 0.018187f, 0.000000f, 0.000000f, 1.000000f}, { 0.995931f, -0.002010f, -0.052079f, -0.073526f} }, -{ { 0.018018f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ {-0.006059f, 0.056285f, 0.060064f, 1.000000f}, { 0.737238f, 0.202745f, 0.594267f, 0.249441f} }, -{ {-0.040416f, -0.043018f, 0.019345f, 1.000000f}, {-0.290331f, 0.623527f, -0.663809f, -0.293734f} }, -{ {-0.039354f, -0.075674f, 0.047048f, 1.000000f}, {-0.187047f, 0.678062f, -0.659285f, -0.265683f} }, -{ {-0.038340f, -0.090987f, 0.082579f, 1.000000f}, {-0.183037f, 0.736793f, -0.634757f, -0.143936f} }, -{ {-0.031806f, -0.087214f, 0.121015f, 1.000000f}, {-0.003659f, 0.758407f, -0.639342f, -0.126678f} }, + {{0.000000f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{-0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, -0.920279f, 0.379296f}}, + {{-0.012083f, 0.028070f, 0.025050f, 1.000000f}, {0.464112f, 0.567418f, 0.272106f, 0.623374f}}, + {{0.040406f, 0.000000f, -0.000000f, 1.000000f}, {0.994838f, 0.082939f, 0.019454f, 0.055130f}}, + {{0.032517f, 0.000000f, 0.000000f, 1.000000f}, {0.974793f, -0.003213f, 0.021867f, -0.222015f}}, + {{0.030464f, -0.000000f, -0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{0.000632f, 0.026866f, 0.015002f, 1.000000f}, {0.644251f, 0.421979f, -0.478202f, 0.422133f}}, + {{0.074204f, -0.005002f, 0.000234f, 1.000000f}, {0.995332f, 0.007007f, -0.039124f, 0.087949f}}, + {{0.043930f, -0.000000f, -0.000000f, 1.000000f}, {0.997891f, 0.045808f, 0.002142f, -0.045943f}}, + {{0.028695f, 0.000000f, 0.000000f, 1.000000f}, {0.999649f, 0.001850f, -0.022782f, -0.013409f}}, + {{0.022821f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, -0.000000f, 0.000000f, -0.000000f}}, + {{0.002177f, 0.007120f, 0.016319f, 1.000000f}, {0.546723f, 0.541276f, -0.442520f, 0.460749f}}, + {{0.070953f, 0.000779f, 0.000997f, 1.000000f}, {0.980294f, -0.167261f, -0.078959f, 0.069368f}}, + {{0.043108f, 0.000000f, 0.000000f, 1.000000f}, {0.997947f, 0.018493f, 0.013192f, 0.059886f}}, + {{0.033266f, 0.000000f, 0.000000f, 1.000000f}, {0.997394f, -0.003328f, -0.028225f, -0.066315f}}, + {{0.025892f, -0.000000f, 0.000000f, 1.000000f}, {0.999195f, -0.000000f, 0.000000f, 0.040126f}}, + {{0.000513f, -0.006545f, 0.016348f, 1.000000f}, {0.516692f, 0.550143f, -0.495548f, 0.429888f}}, + {{0.065876f, 0.001786f, 0.000693f, 1.000000f}, {0.990420f, -0.058696f, -0.101820f, 0.072495f}}, + {{0.040697f, 0.000000f, 0.000000f, 1.000000f}, {0.999545f, -0.002240f, 0.000004f, 0.030081f}}, + {{0.028747f, -0.000000f, -0.000000f, 1.000000f}, {0.999102f, -0.000721f, -0.012693f, 0.040420f}}, + {{0.022430f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{-0.002478f, -0.018981f, 0.015214f, 1.000000f}, {0.526918f, 0.523940f, -0.584025f, 0.326740f}}, + {{0.062878f, 0.002844f, 0.000332f, 1.000000f}, {0.986609f, -0.059615f, -0.135163f, 0.069132f}}, + {{0.030220f, 0.000000f, 0.000000f, 1.000000f}, {0.994317f, 0.001896f, -0.000132f, 0.106446f}}, + {{0.018187f, 0.000000f, 0.000000f, 1.000000f}, {0.995931f, -0.002010f, -0.052079f, -0.073526f}}, + {{0.018018f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{-0.006059f, 0.056285f, 0.060064f, 1.000000f}, {0.737238f, 0.202745f, 0.594267f, 0.249441f}}, + {{-0.040416f, -0.043018f, 0.019345f, 1.000000f}, {-0.290331f, 0.623527f, -0.663809f, -0.293734f}}, + {{-0.039354f, -0.075674f, 0.047048f, 1.000000f}, {-0.187047f, 0.678062f, -0.659285f, -0.265683f}}, + {{-0.038340f, -0.090987f, 0.082579f, 1.000000f}, {-0.183037f, 0.736793f, -0.634757f, -0.143936f}}, + {{-0.031806f, -0.087214f, 0.121015f, 1.000000f}, {-0.003659f, 0.758407f, -0.639342f, -0.126678f}}, }; vr::VRBoneTransform_t leftFistPose[NUM_BONES] = { -{ { 0.000000f, 0.000000f, 0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ {-0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, -0.920279f, 0.379296f} }, -{ {-0.016305f, 0.027529f, 0.017800f, 1.000000f}, { 0.225703f, 0.483332f, 0.126413f, 0.836342f} }, -{ { 0.040406f, 0.000000f, -0.000000f, 1.000000f}, { 0.894335f, -0.013302f, -0.082902f, 0.439448f} }, -{ { 0.032517f, 0.000000f, 0.000000f, 1.000000f}, { 0.842428f, 0.000655f, 0.001244f, 0.538807f} }, -{ { 0.030464f, -0.000000f, -0.000000f, 1.000000f}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f} }, -{ { 0.003802f, 0.021514f, 0.012803f, 1.000000f}, { 0.617314f, 0.395175f, -0.510874f, 0.449185f} }, -{ { 0.074204f, -0.005002f, 0.000234f, 1.000000f}, { 0.737291f, -0.032006f, -0.115013f, 0.664944f} }, -{ { 0.043287f, -0.000000f, -0.000000f, 1.000000f}, { 0.611381f, 0.003287f, 0.003823f, 0.791321f} }, -{ { 0.028275f, 0.000000f, 0.000000f, 1.000000f}, { 0.745388f, -0.000684f, -0.000945f, 0.666629f} }, -{ { 0.022821f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, -0.000000f, 0.000000f, -0.000000f} }, -{ { 0.005787f, 0.006806f, 0.016534f, 1.000000f}, { 0.514203f, 0.522315f, -0.478348f, 0.483700f} }, -{ { 0.070953f, 0.000779f, 0.000997f, 1.000000f}, { 0.723653f, -0.097901f, 0.048546f, 0.681458f} }, -{ { 0.043108f, 0.000000f, 0.000000f, 1.000000f}, { 0.637464f, -0.002366f, -0.002831f, 0.770472f} }, -{ { 0.033266f, 0.000000f, 0.000000f, 1.000000f}, { 0.658008f, 0.002610f, 0.003196f, 0.753000f} }, -{ { 0.025892f, -0.000000f, 0.000000f, 1.000000f}, { 0.999195f, -0.000000f, 0.000000f, 0.040126f} }, -{ { 0.004123f, -0.006858f, 0.016563f, 1.000000f}, { 0.489609f, 0.523374f, -0.520644f, 0.463997f} }, -{ { 0.065876f, 0.001786f, 0.000693f, 1.000000f}, { 0.759970f, -0.055609f, 0.011571f, 0.647471f} }, -{ { 0.040331f, 0.000000f, 0.000000f, 1.000000f}, { 0.664315f, 0.001595f, 0.001967f, 0.747449f} }, -{ { 0.028489f, -0.000000f, -0.000000f, 1.000000f}, { 0.626957f, -0.002784f, -0.003234f, 0.779042f} }, -{ { 0.022430f, -0.000000f, 0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ { 0.001131f, -0.019295f, 0.015429f, 1.000000f}, { 0.479766f, 0.477833f, -0.630198f, 0.379934f} }, -{ { 0.062878f, 0.002844f, 0.000332f, 1.000000f}, { 0.827001f, 0.034282f, 0.003440f, 0.561144f} }, -{ { 0.029874f, 0.000000f, 0.000000f, 1.000000f}, { 0.702185f, -0.006716f, -0.009289f, 0.711903f} }, -{ { 0.017979f, 0.000000f, 0.000000f, 1.000000f}, { 0.676853f, 0.007956f, 0.009917f, 0.736009f} }, -{ { 0.018018f, 0.000000f, -0.000000f, 1.000000f}, { 1.000000f, 0.000000f, 0.000000f, 0.000000f} }, -{ { 0.019716f, 0.002802f, 0.093937f, 1.000000f}, { 0.377286f, -0.540831f, 0.150446f, -0.736562f} }, -{ { 0.000171f, 0.016473f, 0.096515f, 1.000000f}, {-0.006456f, 0.022747f, -0.932927f, -0.359287f} }, -{ { 0.000448f, 0.001536f, 0.116543f, 1.000000f}, {-0.039357f, 0.105143f, -0.928833f, -0.353079f} }, -{ { 0.003949f, -0.014869f, 0.130608f, 1.000000f}, {-0.055071f, 0.068695f, -0.944016f, -0.317933f} }, -{ { 0.003263f, -0.034685f, 0.139926f, 1.000000f}, { 0.019690f, -0.100741f, -0.957331f, -0.270149f} }, + {{0.000000f, 0.000000f, 0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{-0.034038f, 0.036503f, 0.164722f, 1.000000f}, {-0.055147f, -0.078608f, -0.920279f, 0.379296f}}, + {{-0.016305f, 0.027529f, 0.017800f, 1.000000f}, {0.225703f, 0.483332f, 0.126413f, 0.836342f}}, + {{0.040406f, 0.000000f, -0.000000f, 1.000000f}, {0.894335f, -0.013302f, -0.082902f, 0.439448f}}, + {{0.032517f, 0.000000f, 0.000000f, 1.000000f}, {0.842428f, 0.000655f, 0.001244f, 0.538807f}}, + {{0.030464f, -0.000000f, -0.000000f, 1.000000f}, {1.000000f, -0.000000f, -0.000000f, 0.000000f}}, + {{0.003802f, 0.021514f, 0.012803f, 1.000000f}, {0.617314f, 0.395175f, -0.510874f, 0.449185f}}, + {{0.074204f, -0.005002f, 0.000234f, 1.000000f}, {0.737291f, -0.032006f, -0.115013f, 0.664944f}}, + {{0.043287f, -0.000000f, -0.000000f, 1.000000f}, {0.611381f, 0.003287f, 0.003823f, 0.791321f}}, + {{0.028275f, 0.000000f, 0.000000f, 1.000000f}, {0.745388f, -0.000684f, -0.000945f, 0.666629f}}, + {{0.022821f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, -0.000000f, 0.000000f, -0.000000f}}, + {{0.005787f, 0.006806f, 0.016534f, 1.000000f}, {0.514203f, 0.522315f, -0.478348f, 0.483700f}}, + {{0.070953f, 0.000779f, 0.000997f, 1.000000f}, {0.723653f, -0.097901f, 0.048546f, 0.681458f}}, + {{0.043108f, 0.000000f, 0.000000f, 1.000000f}, {0.637464f, -0.002366f, -0.002831f, 0.770472f}}, + {{0.033266f, 0.000000f, 0.000000f, 1.000000f}, {0.658008f, 0.002610f, 0.003196f, 0.753000f}}, + {{0.025892f, -0.000000f, 0.000000f, 1.000000f}, {0.999195f, -0.000000f, 0.000000f, 0.040126f}}, + {{0.004123f, -0.006858f, 0.016563f, 1.000000f}, {0.489609f, 0.523374f, -0.520644f, 0.463997f}}, + {{0.065876f, 0.001786f, 0.000693f, 1.000000f}, {0.759970f, -0.055609f, 0.011571f, 0.647471f}}, + {{0.040331f, 0.000000f, 0.000000f, 1.000000f}, {0.664315f, 0.001595f, 0.001967f, 0.747449f}}, + {{0.028489f, -0.000000f, -0.000000f, 1.000000f}, {0.626957f, -0.002784f, -0.003234f, 0.779042f}}, + {{0.022430f, -0.000000f, 0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{0.001131f, -0.019295f, 0.015429f, 1.000000f}, {0.479766f, 0.477833f, -0.630198f, 0.379934f}}, + {{0.062878f, 0.002844f, 0.000332f, 1.000000f}, {0.827001f, 0.034282f, 0.003440f, 0.561144f}}, + {{0.029874f, 0.000000f, 0.000000f, 1.000000f}, {0.702185f, -0.006716f, -0.009289f, 0.711903f}}, + {{0.017979f, 0.000000f, 0.000000f, 1.000000f}, {0.676853f, 0.007956f, 0.009917f, 0.736009f}}, + {{0.018018f, 0.000000f, -0.000000f, 1.000000f}, {1.000000f, 0.000000f, 0.000000f, 0.000000f}}, + {{0.019716f, 0.002802f, 0.093937f, 1.000000f}, {0.377286f, -0.540831f, 0.150446f, -0.736562f}}, + {{0.000171f, 0.016473f, 0.096515f, 1.000000f}, {-0.006456f, 0.022747f, -0.932927f, -0.359287f}}, + {{0.000448f, 0.001536f, 0.116543f, 1.000000f}, {-0.039357f, 0.105143f, -0.928833f, -0.353079f}}, + {{0.003949f, -0.014869f, 0.130608f, 1.000000f}, {-0.055071f, 0.068695f, -0.944016f, -0.317933f}}, + {{0.003263f, -0.034685f, 0.139926f, 1.000000f}, {0.019690f, -0.100741f, -0.957331f, -0.270149f}}, }; vr::HmdQuaternionf_t CalculateOrientation(const float transform, const int boneIndex, const vr::VRBoneTransform_t* openPose, const vr::VRBoneTransform_t* fistPose) { + const vr::HmdQuaternionf_t openPoseOrientation = openPose[boneIndex].orientation; + const vr::HmdQuaternionf_t fistPoseOrientation = fistPose[boneIndex].orientation; - const vr::HmdQuaternionf_t openPoseOrientation = openPose[boneIndex].orientation; - const vr::HmdQuaternionf_t fistPoseOrientation = fistPose[boneIndex].orientation; + vr::HmdQuaternionf_t result; + result.w = Lerp(openPoseOrientation.w, fistPoseOrientation.w, transform); + result.x = Lerp(openPoseOrientation.x, fistPoseOrientation.x, transform); + result.y = Lerp(openPoseOrientation.y, fistPoseOrientation.y, transform); + result.z = Lerp(openPoseOrientation.z, fistPoseOrientation.z, transform); - vr::HmdQuaternionf_t result; - result.w = Lerp(openPoseOrientation.w, fistPoseOrientation.w, transform); - result.x = Lerp(openPoseOrientation.x, fistPoseOrientation.x, transform); - result.y = Lerp(openPoseOrientation.y, fistPoseOrientation.y, transform); - result.z = Lerp(openPoseOrientation.z, fistPoseOrientation.z, transform); - - return result; + return result; } vr::HmdVector4_t CalculatePosition(const float transform, const int boneIndex, const vr::VRBoneTransform_t* openPose, const vr::VRBoneTransform_t* fistPose) { + const vr::HmdVector4_t openPosePosition = openPose[boneIndex].position; + const vr::HmdVector4_t fistPosePosition = fistPose[boneIndex].position; - const vr::HmdVector4_t openPosePosition = openPose[boneIndex].position; - const vr::HmdVector4_t fistPosePosition = fistPose[boneIndex].position; - - vr::HmdVector4_t result; - result.v[0] = Lerp(openPosePosition.v[0], fistPosePosition.v[0], transform); - result.v[1] = Lerp(openPosePosition.v[1], fistPosePosition.v[1], transform); - result.v[2] = Lerp(openPosePosition.v[2], fistPosePosition.v[2], transform); - result.v[3] = Lerp(openPosePosition.v[3], fistPosePosition.v[3], transform); + vr::HmdVector4_t result; + result.v[0] = Lerp(openPosePosition.v[0], fistPosePosition.v[0], transform); + result.v[1] = Lerp(openPosePosition.v[1], fistPosePosition.v[1], transform); + result.v[2] = Lerp(openPosePosition.v[2], fistPosePosition.v[2], transform); + result.v[3] = Lerp(openPosePosition.v[3], fistPosePosition.v[3], transform); - return result; + return result; } -//Transform should be between 0-1 +// Transform should be between 0-1 void ComputeBoneFlexion(vr::VRBoneTransform_t* bone_transform, float transform, int index, const bool isRightHand) { + vr::VRBoneTransform_t* fist_pose = isRightHand ? rightFistPose : leftFistPose; + vr::VRBoneTransform_t* open_pose = isRightHand ? rightOpenPose : leftOpenPose; - vr::VRBoneTransform_t* fist_pose = isRightHand ? rightFistPose : leftFistPose; - vr::VRBoneTransform_t* open_pose = isRightHand ? rightOpenPose : leftOpenPose; - - - bone_transform->orientation = CalculateOrientation(transform, index, open_pose, fist_pose); - bone_transform->position = CalculatePosition(transform, index, open_pose, fist_pose); + bone_transform->orientation = CalculateOrientation(transform, index, open_pose, fist_pose); + bone_transform->position = CalculatePosition(transform, index, open_pose, fist_pose); } - -float Lerp(const float a, const float b, const float f) { - return a + f * (b - a); -} +float Lerp(const float a, const float b, const float f) { return a + f * (b - a); } int FingerFromBone(vr::BoneIndex_t bone) { - switch (bone) { - case eBone_Thumb0: - case eBone_Thumb1: - case eBone_Thumb2: - case eBone_Thumb3: - case eBone_Aux_Thumb: - return 0; - case eBone_IndexFinger0: - case eBone_IndexFinger1: - case eBone_IndexFinger2: - case eBone_IndexFinger3: - case eBone_IndexFinger4: - case eBone_Aux_IndexFinger: - return 1; - case eBone_MiddleFinger0: - case eBone_MiddleFinger1: - case eBone_MiddleFinger2: - case eBone_MiddleFinger3: - case eBone_MiddleFinger4: - case eBone_Aux_MiddleFinger: - return 2; - case eBone_RingFinger0: - case eBone_RingFinger1: - case eBone_RingFinger2: - case eBone_RingFinger3: - case eBone_RingFinger4: - case eBone_Aux_RingFinger: - return 3; - case eBone_PinkyFinger0: - case eBone_PinkyFinger1: - case eBone_PinkyFinger2: - case eBone_PinkyFinger3: - case eBone_PinkyFinger4: - case eBone_Aux_PinkyFinger: - return 4; - - default: - return -1; - } - + switch (bone) { + case eBone_Thumb0: + case eBone_Thumb1: + case eBone_Thumb2: + case eBone_Thumb3: + case eBone_Aux_Thumb: + return 0; + case eBone_IndexFinger0: + case eBone_IndexFinger1: + case eBone_IndexFinger2: + case eBone_IndexFinger3: + case eBone_IndexFinger4: + case eBone_Aux_IndexFinger: + return 1; + case eBone_MiddleFinger0: + case eBone_MiddleFinger1: + case eBone_MiddleFinger2: + case eBone_MiddleFinger3: + case eBone_MiddleFinger4: + case eBone_Aux_MiddleFinger: + return 2; + case eBone_RingFinger0: + case eBone_RingFinger1: + case eBone_RingFinger2: + case eBone_RingFinger3: + case eBone_RingFinger4: + case eBone_Aux_RingFinger: + return 3; + case eBone_PinkyFinger0: + case eBone_PinkyFinger1: + case eBone_PinkyFinger2: + case eBone_PinkyFinger3: + case eBone_PinkyFinger4: + case eBone_Aux_PinkyFinger: + return 4; + + default: + return -1; + } } \ No newline at end of file diff --git a/src/Calibration.cpp b/src/Calibration.cpp new file mode 100644 index 00000000..1502f0eb --- /dev/null +++ b/src/Calibration.cpp @@ -0,0 +1,74 @@ +#include "Calibration.h" + +#include +#include + +#include "DriverLog.h" +#include "Quaternion.h" + +Calibration::Calibration() { + m_isCalibrating = false; +} + +void Calibration::StartCalibration(vr::DriverPose_t maintainPose) { + maintainPose.vecVelocity[0] = 0; + maintainPose.vecVelocity[1] = 0; + maintainPose.vecVelocity[2] = 0; + maintainPose.vecAngularVelocity[0] = 0; + maintainPose.vecAngularVelocity[1] = 0; + maintainPose.vecAngularVelocity[2] = 0; + m_maintainPose = maintainPose; + m_isCalibrating = true; +} + +VRPoseConfiguration_t Calibration::FinishCalibration(vr::TrackedDevicePose_t controllerPose, VRPoseConfiguration_t poseConfiguration, bool isRightHand) { + + m_isCalibrating = false; + // get the matrix that represents the position of the controller that we are shadowing + vr::HmdMatrix34_t controllerMatrix = controllerPose.mDeviceToAbsoluteTracking; + + vr::HmdQuaternion_t controllerQuat = GetRotation(controllerMatrix); + vr::HmdQuaternion_t handQuat = m_maintainPose.qRotation; + + + //qC * qT = qH -> qC*qC^-1 * qT = qH * qC^-1 -> qT = qH * qC^-1 + vr::HmdQuaternion_t transformQuat = MultiplyQuaternion(QuatConjugate(controllerQuat), handQuat); + //m_poseConfiguration.angleOffsetQuaternion = transformQuat; + poseConfiguration.angleOffsetQuaternion.w = transformQuat.w; + poseConfiguration.angleOffsetQuaternion.x = transformQuat.x; + poseConfiguration.angleOffsetQuaternion.y = transformQuat.y; + poseConfiguration.angleOffsetQuaternion.z = transformQuat.z; + + vr::HmdVector3_t differenceVector = { m_maintainPose.vecPosition[0] - controllerMatrix.m[0][3], + m_maintainPose.vecPosition[1] - controllerMatrix.m[1][3], + m_maintainPose.vecPosition[2] - controllerMatrix.m[2][3] }; + + vr::HmdQuaternion_t transformInverse = QuatConjugate(controllerQuat); + vr::HmdMatrix33_t transformMatrix = QuaternionToMatrix(transformInverse); + vr::HmdVector3_t transformVector = MultiplyMatrix(transformMatrix, differenceVector); + + poseConfiguration.offsetVector = transformVector; + + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_x_offset_position" : "left_x_offset_position", transformVector.v[0]); + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_y_offset_position" : "left_y_offset_position", transformVector.v[1]); + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_z_offset_position" : "left_z_offset_position", transformVector.v[2]); + + vr::HmdVector3_t eulerOffset = QuaternionToEuler(transformQuat); + + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_x_offset_degrees" : "left_x_offset_degrees", eulerOffset.v[0]); + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_y_offset_degrees" : "left_y_offset_degrees", eulerOffset.v[1]); + vr::VRSettings()->SetFloat(c_poseSettingsSection, isRightHand ? "right_z_offset_degrees" : "left_z_offset_degrees", eulerOffset.v[2]); + + return poseConfiguration; +} + +void Calibration::CancelCalibration() { m_isCalibrating = false; } + +bool Calibration::isCalibrating() { + return m_isCalibrating; +} + +vr::DriverPose_t Calibration::GetMaintainPose() { + return m_maintainPose; +} + diff --git a/src/Communication/BTSerialCommunicationManager.cpp b/src/Communication/BTSerialCommunicationManager.cpp index 4b6a5d44..5c90dfeb 100644 --- a/src/Communication/BTSerialCommunicationManager.cpp +++ b/src/Communication/BTSerialCommunicationManager.cpp @@ -1,222 +1,196 @@ #include -//Adapted from Finally Functional's SerialBT implementation +// Adapted from Finally Functional's SerialBT implementation -BTSerialCommunicationManager::BTSerialCommunicationManager(const VRBTSerialConfiguration_t& configuration, std::unique_ptr encodingManager) - : m_btSerialConfiguration(configuration), - m_encodingManager(std::move(encodingManager)), - m_isConnected(false) -{ - //convert the bluetooth device name from settings into wide - //const char* name = configuration.name.c_str(); - //size_t newsize = strlen(name) + 1; - //m_wcDeviceName = new WCHAR[newsize]; - //size_t convertedChars = 0; - //mbstowcs_s(&convertedChars, m_wcDeviceName, newsize, name, _TRUNCATE); - - //std::wstring thiswstring = std::wstring(configuration.name.begin(), configuration.name.end()); - - //m_wcDeviceName = (WCHAR*)(thiswstring.c_str()); - - -}; +BTSerialCommunicationManager::BTSerialCommunicationManager(const VRBTSerialConfiguration_t& configuration, std::unique_ptr encodingManager) + : m_btSerialConfiguration(configuration), m_encodingManager(std::move(encodingManager)), m_isConnected(false){}; void BTSerialCommunicationManager::Connect() { - DriverLog("Trying to connect to bluetooth"); - - //We're not yet connected - m_isConnected = false; - - //Try to connect - if (!getPairedEsp32BtAddress()) //find an ESP32 paired with this machine - { - DriverLog("Error getting Bluetooth address"); - return; - } - if (!startupWindowsSocket()) //initialize windows sockets - { - DriverLog("Error Initializing windows sockets"); - return; - } - if (!connectToEsp32()) //initialize BT windows socket for connecting to ESP32 - { - DriverLog("Error connecting to Bluetooth device"); - return; - } - else { - //If everything went fine we're connected - m_isConnected = true; - DriverLog("Connected to bluetooth!"); - } + DriverLog("Trying to connect to bluetooth"); + + // We're not yet connected + m_isConnected = false; + + // Try to connect + if (!getPairedDeviceBtAddress()) // find an device paired with this machine + { + DriverLog("Error getting Bluetooth address"); + return; + } + if (!startupWindowsSocket()) // initialize windows sockets + { + DriverLog("Error Initializing windows sockets"); + return; + } + if (!connectToDevice()) // initialize BT windows socket for connecting to device + { + DriverLog("Error connecting to Bluetooth device"); + return; + } else { + // If everything went fine we're connected + m_isConnected = true; + DriverLog("Connected to bluetooth!"); + } } void BTSerialCommunicationManager::BeginListener(const std::function& callback) { - DriverLog("Begun listener"); - m_threadActive = true; - m_serialThread = std::thread(&BTSerialCommunicationManager::ListenerThread, this, callback); + DriverLog("Begun listener"); + m_threadActive = true; + m_serialThread = std::thread(&BTSerialCommunicationManager::ListenerThread, this, callback); } void BTSerialCommunicationManager::ListenerThread(const std::function& callback) { - //DebugDriverLog("In listener thread"); - std::this_thread::sleep_for(std::chrono::milliseconds(ARDUINO_WAIT_TIME)); - - while (m_threadActive) { - std::string receivedString; - bool readSuccessful = ReceiveNextPacket(receivedString); - - if (readSuccessful) { - try { - VRCommData_t commData = m_encodingManager->Decode(receivedString); - callback(commData); - } - catch (const std::invalid_argument& ia) { - DriverLog("Received error from encoding manager. Skipping..."); - } - } - else { - DriverLog("Detected that arduino has disconnected! Stopping listener..."); - //We should probably do more logic for trying to reconnect to the arduino - //For now, it should be obvious to people that the arduinos have disconnected - m_threadActive = false; - } - - } + // DebugDriverLog("In listener thread"); + std::this_thread::sleep_for(std::chrono::milliseconds(ARDUINO_WAIT_TIME)); + + while (m_threadActive) { + std::string receivedString; + bool readSuccessful = ReceiveNextPacket(receivedString); + if (readSuccessful) { + try { + VRCommData_t commData = m_encodingManager->Decode(receivedString); + callback(commData); + sendMessageToDevice(); + } catch (const std::invalid_argument& ia) { + DriverLog("Received error from encoding manager. Skipping..."); + } + } else { + DriverLog("Detected that arduino has disconnected! Stopping listener..."); + // We should probably do more logic for trying to reconnect to the arduino + // For now, it should be obvious to people that the arduinos have disconnected + m_threadActive = false; + } + } } bool BTSerialCommunicationManager::ReceiveNextPacket(std::string& buff) { - char nextChar[1]; - do { - int recievedMessageLength = 1; - int recieveResult = recv(m_btClientSocket, nextChar, recievedMessageLength, 0); //if your socket is blocking, this will block until a - if (recieveResult < 0) //a message is recieved. If not, it will return right - { //away - continue; - } - buff += nextChar[0]; - } while (nextChar[0] != '\n' || buff.length() < 1); - - //("Packet Recieved! Length: %i, %s", buff.length(), buff.c_str()); - - return true; + char nextChar[1]; + do { + int recievedMessageLength = 1; + int recieveResult = recv(m_btClientSocket, nextChar, recievedMessageLength, 0); // if your socket is blocking, this will block until a + if (recieveResult < 0) // a message is recieved. If not, it will return right + { // away + continue; + } + buff += nextChar[0]; + } while (nextChar[0] != '\n' || buff.length() < 1); + + //("Packet Recieved! Length: %i, %s", buff.length(), buff.c_str()); + + return true; } -void BTSerialCommunicationManager::Disconnect() { - if (m_isConnected) { - if (m_threadActive) { - m_threadActive = false; - m_serialThread.join(); - } - m_isConnected = false; - //CloseHandle(m_hSerial); - - - //Disconnect - if (shutdown(m_btClientSocket, 2) == SOCKET_ERROR) { - DriverLog("Could not disconnect socket from ESP32. Error %ld", WSAGetLastError()); - } - else - DriverLog("Disconnected from socket successfully."); - } +void BTSerialCommunicationManager::QueueSend(const VRFFBData_t& data) { + std::lock_guard lock(m_writeMutex); + + m_writeString = m_encodingManager->Encode(data); } -//May want to get a heartbeat here instead? -bool BTSerialCommunicationManager::IsConnected() { - return m_isConnected; + +void BTSerialCommunicationManager::Disconnect() { + if (m_isConnected) { + if (m_threadActive) { + m_threadActive = false; + m_serialThread.join(); + } + m_isConnected = false; + // CloseHandle(m_hSerial); + + // Disconnect + if (shutdown(m_btClientSocket, 2) == SOCKET_ERROR) { + DriverLog("Could not disconnect socket from bluetooth device. Error %ld", WSAGetLastError()); + } else + DriverLog("Disconnected from socket successfully."); + } } +// May want to get a heartbeat here instead? +bool BTSerialCommunicationManager::IsConnected() { return m_isConnected; } /// -/// Gets the bluetooth devices paired with this machine and -/// finds an ESP32. If it finds one, its BT address is stored. +/// Gets the bluetooth devices paired with this machine and +/// finds a device. If it finds one, its BT address is stored. /// -bool BTSerialCommunicationManager::getPairedEsp32BtAddress() { - BLUETOOTH_DEVICE_SEARCH_PARAMS btDeviceSearchParameters = - { - sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), //size of object - 1, //return authenticated devices - 0, //return remembered devices - 1, //return unknown devices - 1, //return connected devices - 1, //issue inquery - 2, //timeout multipler. Multiply this value by 1.28 seconds to get timeout. - NULL //radio handler - }; - BLUETOOTH_DEVICE_INFO btDeviceInfo = { sizeof(BLUETOOTH_DEVICE_INFO),0 }; //default - HBLUETOOTH_DEVICE_FIND btDevice = NULL; - btDevice = BluetoothFindFirstDevice(&btDeviceSearchParameters, &btDeviceInfo); //returns first BT device connected to this machine - if (btDevice == NULL) { - DriverLog("Could not find any bluetooth devices."); - return false; - } - do { - //wprintf(L"Checking %s.\r\n", btDeviceInfo.szName); - - std::wstring thiswstring = std::wstring(m_btSerialConfiguration.name.begin(), m_btSerialConfiguration.name.end()); - - m_wcDeviceName = (WCHAR*)(thiswstring.c_str()); - if (wcscmp(btDeviceInfo.szName, m_wcDeviceName) == 0) {// - DriverLog("ESP32 found!\r\n"); - if (btDeviceInfo.fAuthenticated) //I found that if fAuthenticated is true it means the device is paired. - { - DriverLog("ESP32 is authenticated.\r\n"); - m_esp32BtAddress = btDeviceInfo.Address.ullLong; - return true; - } - else { - DriverLog("This ESP32 is not authenticated. Please pair with it first.\r\n"); - } - } - } while (BluetoothFindNextDevice(btDevice, &btDeviceInfo)); //loop through remaining BT devices connected to this machine - - DriverLog("Could not find a paired ESP32 with name %s", m_btSerialConfiguration.name.c_str()); - return false; +bool BTSerialCommunicationManager::getPairedDeviceBtAddress() { + BLUETOOTH_DEVICE_SEARCH_PARAMS btDeviceSearchParameters = { + sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), // size of object + 1, // return authenticated devices + 0, // return remembered devices + 1, // return unknown devices + 1, // return connected devices + 1, // issue inquery + 2, // timeout multipler. Multiply this value by 1.28 seconds to get timeout. + NULL // radio handler + }; + BLUETOOTH_DEVICE_INFO btDeviceInfo = {sizeof(BLUETOOTH_DEVICE_INFO), 0}; // default + HBLUETOOTH_DEVICE_FIND btDevice = NULL; + btDevice = BluetoothFindFirstDevice(&btDeviceSearchParameters, &btDeviceInfo); // returns first BT device connected to this machine + if (btDevice == NULL) { + DriverLog("Could not find any bluetooth devices."); + return false; + } + do { + std::wstring thiswstring = std::wstring(m_btSerialConfiguration.name.begin(), m_btSerialConfiguration.name.end()); + + m_wcDeviceName = (WCHAR*)(thiswstring.c_str()); + if (wcscmp(btDeviceInfo.szName, m_wcDeviceName) == 0) { // + DriverLog("Bluetooth Device found!\r\n"); + if (btDeviceInfo.fAuthenticated) // I found that if fAuthenticated is true it means the device is paired. + { + DriverLog("Bluetooth Device is authenticated.\r\n"); + m_deviceBtAddress = btDeviceInfo.Address.ullLong; + return true; + } else { + DriverLog("This Bluetooth Device is not authenticated. Please pair with it first.\r\n"); + } + } + } while (BluetoothFindNextDevice(btDevice, &btDeviceInfo)); // loop through remaining BT devices connected to this machine + + DriverLog("Could not find a paired Bluetooth Device with name %s", m_btSerialConfiguration.name.c_str()); + return false; } -/// -/// Windows sockets need an initialization method called before they are used. -/// bool BTSerialCommunicationManager::startupWindowsSocket() { - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 2); - int wsaStartupError = WSAStartup(wVersionRequested, &wsaData); //call this before using BT windows socket. - if (wsaStartupError != 0) { - DebugDriverLog("WSAStartup failed with error: %d", wsaStartupError); - return false; - } - return true; + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + int wsaStartupError = WSAStartup(wVersionRequested, &wsaData); // call this before using BT windows socket. + if (wsaStartupError != 0) { + DebugDriverLog("WSAStartup failed with error: %d", wsaStartupError); + return false; + } + return true; } -/// -/// Sets up bluetooth socket to communicate with ESP32. -/// -bool BTSerialCommunicationManager::connectToEsp32() { - m_btClientSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); //initialize BT windows socket - memset(&m_btSocketAddress, 0, sizeof(m_btSocketAddress)); - m_btSocketAddress.addressFamily = AF_BTH; - m_btSocketAddress.serviceClassId = RFCOMM_PROTOCOL_UUID; - m_btSocketAddress.port = 0; //port needs to be 0 if the remote device is a client. See references. - m_btSocketAddress.btAddr = m_esp32BtAddress; //this is the BT address of the remote device. - if (connect(m_btClientSocket, (SOCKADDR*)&m_btSocketAddress, sizeof(m_btSocketAddress)) != 0) //connect to the BT device. - { - DriverLog("Could not connect socket to ESP32. Error %ld", WSAGetLastError()); - return false; - } - unsigned long nonBlockingMode = 1; - if (ioctlsocket(m_btClientSocket, FIONBIO, (unsigned long*)&nonBlockingMode) != 0) //set the socket to be non-blocking, meaning - { //it will return right away when sending/recieving - DriverLog("Could not set socket to be non-blocking."); - return false; - } - return true; +bool BTSerialCommunicationManager::connectToDevice() { + m_btClientSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); // initialize BT windows socket + memset(&m_btSocketAddress, 0, sizeof(m_btSocketAddress)); + m_btSocketAddress.addressFamily = AF_BTH; + m_btSocketAddress.serviceClassId = RFCOMM_PROTOCOL_UUID; + m_btSocketAddress.port = 0; // port needs to be 0 if the remote device is a client. See references. + m_btSocketAddress.btAddr = m_deviceBtAddress; // this is the BT address of the remote device. + if (connect(m_btClientSocket, (SOCKADDR*)&m_btSocketAddress, sizeof(m_btSocketAddress)) != 0) // connect to the BT device. + { + DriverLog("Could not connect socket to Bluetooth Device. Error %ld", WSAGetLastError()); + return false; + } + unsigned long nonBlockingMode = 1; + if (ioctlsocket(m_btClientSocket, FIONBIO, (unsigned long*)&nonBlockingMode) != 0) // set the socket to be non-blocking, meaning + { // it will return right away when sending/recieving + DriverLog("Could not set socket to be non-blocking."); + return false; + } + return true; } -bool BTSerialCommunicationManager::sendMessageToEsp32() { - const char* message = "Message from Windows\r\n"; - int sendResult = send(m_btClientSocket, message, (int)strlen(message), 0); //send your message to the BT device - if (sendResult == SOCKET_ERROR) { - DriverLog("Sending to ESP32 failed. Error code %d", WSAGetLastError()); - closesocket(m_btClientSocket); - WSACleanup(); - return false; - } - return true; +bool BTSerialCommunicationManager::sendMessageToDevice() { + std::lock_guard lock(m_writeMutex); + const char* message = m_writeString.c_str(); + //DebugDriverLog("Sending %s to Bluetooth Device.", m_writeString.c_str()); + int sendResult = send(m_btClientSocket, message, (int)strlen(message), 0); // send your message to the BT device + if (sendResult == SOCKET_ERROR) { + DriverLog("Sending to Bluetooth Device failed. Error code %d", WSAGetLastError()); + closesocket(m_btClientSocket); + WSACleanup(); + return false; + } + return true; } \ No newline at end of file diff --git a/src/Communication/SerialCommunicationManager.cpp b/src/Communication/SerialCommunicationManager.cpp index 88dd5bf2..780911f3 100644 --- a/src/Communication/SerialCommunicationManager.cpp +++ b/src/Communication/SerialCommunicationManager.cpp @@ -1,146 +1,151 @@ #include #include + #include "DriverLog.h" void SerialCommunicationManager::Connect() { - //We're not yet connected - m_isConnected = false; - - //Try to connect to the given port throuh CreateFile - m_hSerial = CreateFile(m_serialConfiguration.port.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (this->m_hSerial == INVALID_HANDLE_VALUE) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) { - - DebugDriverLog("Serial error: Handle was not attached. Reason: not available."); - } - else { - DebugDriverLog("Serial error:Received error connecting to port"); - } - } - else { - //If connected we try to set the comm parameters - DCB dcbSerialParams = { 0 }; - - //Try to get the current - if (!GetCommState(m_hSerial, &dcbSerialParams)) { - //If impossible, show an error - DebugDriverLog("Serial error: failed to get current serial parameters!"); - } - else { - //Define serial connection parameters for the arduino board - dcbSerialParams.BaudRate = CBR_115200; - dcbSerialParams.ByteSize = 8; - dcbSerialParams.StopBits = ONESTOPBIT; - dcbSerialParams.Parity = NOPARITY; - - //reset upon establishing a connection - dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; - - //set the parameters and check for their proper application - if (!SetCommState(m_hSerial, &dcbSerialParams)) { - DebugDriverLog("ALERT: Could not set Serial Port parameters"); - } - else { - //If everything went fine we're connected - m_isConnected = true; - //Flush any remaining characters in the buffers - PurgeComm(m_hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR); - } - } - } + // We're not yet connected + m_isConnected = false; + + // Try to connect to the given port throuh CreateFile + m_hSerial = CreateFile(m_serialConfiguration.port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (this->m_hSerial == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + DebugDriverLog("Serial error: Handle was not attached. Reason: not available."); + } else { + DebugDriverLog("Serial error:Received error connecting to port"); + } + } else { + // If connected we try to set the comm parameters + DCB dcbSerialParams = {0}; + + // Try to get the current + if (!GetCommState(m_hSerial, &dcbSerialParams)) { + // If impossible, show an error + DebugDriverLog("Serial error: failed to get current serial parameters!"); + } else { + // Define serial connection parameters for the arduino board + dcbSerialParams.BaudRate = CBR_115200; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.Parity = NOPARITY; + + // reset upon establishing a connection + dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; + + // set the parameters and check for their proper application + if (!SetCommState(m_hSerial, &dcbSerialParams)) { + DebugDriverLog("ALERT: Could not set Serial Port parameters"); + } else { + // If everything went fine we're connected + m_isConnected = true; + // Flush any remaining characters in the buffers + PurgeComm(m_hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR); + } + } + } } void SerialCommunicationManager::BeginListener(const std::function& callback) { - //DebugDriverLog("Begun listener"); - m_threadActive = true; - m_serialThread = std::thread(&SerialCommunicationManager::ListenerThread, this, callback); + // DebugDriverLog("Begun listener"); + m_threadActive = true; + m_serialThread = std::thread(&SerialCommunicationManager::ListenerThread, this, callback); } void SerialCommunicationManager::ListenerThread(const std::function& callback) { - //DebugDriverLog("In listener thread"); - std::this_thread::sleep_for(std::chrono::milliseconds(ARDUINO_WAIT_TIME)); - PurgeBuffer(); - - while (m_threadActive) { - std::string receivedString; - bool readSuccessful = ReceiveNextPacket(receivedString); - - - if (readSuccessful) { - try { - VRCommData_t commData = m_encodingManager->Decode(receivedString); - callback(commData); - } - catch (const std::invalid_argument& ia) { - DriverLog("Received error from encoding manager. Skipping..."); - } - } - else { - DebugDriverLog("Detected that arduino has disconnected! Stopping listener..."); - //We should probably do more logic for trying to reconnect to the arduino - //For now, it should be obvious to people that the arduinos have disconnected - m_threadActive = false; - } - - } + // DebugDriverLog("In listener thread"); + std::this_thread::sleep_for(std::chrono::milliseconds(ARDUINO_WAIT_TIME)); + PurgeBuffer(); + + while (m_threadActive) { + std::string receivedString; + bool readSuccessful = ReceiveNextPacket(receivedString); + + if (readSuccessful) { + try { + VRCommData_t commData = m_encodingManager->Decode(receivedString); + callback(commData); + + Write(); + } catch (const std::invalid_argument& ia) { + DriverLog("Received error from encoding manager. Skipping..."); + } + } else { + DebugDriverLog("Detected that arduino has disconnected! Stopping listener..."); + // We should probably do more logic for trying to reconnect to the arduino + // For now, it should be obvious to people that the arduinos have disconnected + m_threadActive = false; + } + } } bool SerialCommunicationManager::ReceiveNextPacket(std::string& buff) { - DWORD dwCommEvent; - DWORD dwRead = 0; - - if (!SetCommMask(m_hSerial, EV_RXCHAR)) { - DebugDriverLog("Error setting comm mask"); - return false; - } - - char nextChar; - int bytesRead = 0; - if (WaitCommEvent(m_hSerial, &dwCommEvent, NULL)) { - do { - if (ReadFile(m_hSerial, &nextChar, 1, &dwRead, NULL)) { - buff += nextChar; - bytesRead++; - } - else { - DebugDriverLog("Read file error"); - return false; - } - } while (nextChar != '\n'); - } - else { - DebugDriverLog("Error in comm event"); - return false; - } - - return true; + DWORD dwCommEvent; + DWORD dwRead = 0; + + if (!SetCommMask(m_hSerial, EV_RXCHAR)) { + DebugDriverLog("Error setting comm mask"); + return false; + } + + char nextChar; + int bytesRead = 0; + if (WaitCommEvent(m_hSerial, &dwCommEvent, NULL)) { + do { + if (ReadFile(m_hSerial, &nextChar, 1, &dwRead, NULL)) { + buff += nextChar; + bytesRead++; + } else { + DebugDriverLog("Read file error"); + return false; + } + } while (nextChar != '\n'); + } else { + DebugDriverLog("Error in comm event"); + return false; + } + + return true; } -bool SerialCommunicationManager::PurgeBuffer() { - return PurgeComm(m_hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR); + +void SerialCommunicationManager::QueueSend(const VRFFBData_t& data) { + std::lock_guard lock(m_writeMutex); + + m_writeString = m_encodingManager->Encode(data); } -void SerialCommunicationManager::Disconnect() { - if (m_isConnected) { - if (m_threadActive) { - m_threadActive = false; - m_serialThread.join(); - } - m_isConnected = false; - CloseHandle(m_hSerial); +bool SerialCommunicationManager::Write() { + std::lock_guard lock(m_writeMutex); + + const char* buf = m_writeString.c_str(); + + DWORD bytesSend; + if (!WriteFile(this->m_hSerial, (void*)buf, strlen(buf), &bytesSend, 0)) { + ClearCommError(this->m_hSerial, &this->m_errors, &this->m_status); - //Disconnect - } + DebugDriverLog("Error connecting writing to Serial Port."); + return false; + } + + return true; +} + +bool SerialCommunicationManager::PurgeBuffer() { return PurgeComm(m_hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR); } + +void SerialCommunicationManager::Disconnect() { + if (m_isConnected) { + if (m_threadActive) { + m_threadActive = false; + m_serialThread.join(); + } + m_isConnected = false; + CloseHandle(m_hSerial); + + // Disconnect + } } -//May want to get a heartbeat here instead? -bool SerialCommunicationManager::IsConnected() { - return m_isConnected; -} \ No newline at end of file +// May want to get a heartbeat here instead? +bool SerialCommunicationManager::IsConnected() { return m_isConnected; } \ No newline at end of file diff --git a/src/ControllerDiscovery.cpp b/src/ControllerDiscovery.cpp new file mode 100644 index 00000000..0a7d7ae5 --- /dev/null +++ b/src/ControllerDiscovery.cpp @@ -0,0 +1,21 @@ +#include "ControllerDiscovery.h" + +#include "DriverLog.h" + +ControllerDiscovery::ControllerDiscovery(vr::ETrackedControllerRole role, std::function callback) + : m_role(role), m_callback(std::move(callback)) { + + std::string pipeName = "\\\\.\\pipe\\vrapplication\\discovery\\" + std::string(role == vr::ETrackedControllerRole::TrackedControllerRole_RightHand ? "right" : "left"); + + m_pipe = std::make_unique(pipeName, sizeof(ControllerDiscoveryPipeData_t)); +}; + +void ControllerDiscovery::Start() { + m_pipe->Start([&](LPVOID data) { + ControllerDiscoveryPipeData_t *controllerPipeData = (ControllerDiscoveryPipeData_t *)data; + + m_callback(*controllerPipeData); + }); +}; + +void ControllerDiscovery::Stop() { m_pipe->Stop(); }; \ No newline at end of file diff --git a/src/ControllerPose.cpp b/src/ControllerPose.cpp index 5693a5c1..f39b1fdc 100644 --- a/src/ControllerPose.cpp +++ b/src/ControllerPose.cpp @@ -1,152 +1,121 @@ #include "ControllerPose.h" -#include #include +#include + #include "DriverLog.h" #include "Quaternion.h" - ControllerPose::ControllerPose(vr::ETrackedControllerRole shadowDeviceOfRole, std::string thisDeviceManufacturer, - VRPoseConfiguration_t poseConfiguration) : - m_shadowDeviceOfRole(shadowDeviceOfRole), - m_thisDeviceManufacturer(std::move(thisDeviceManufacturer)), - m_poseConfiguration(poseConfiguration) {} + VRPoseConfiguration_t poseConfiguration) + : m_shadowDeviceOfRole(shadowDeviceOfRole), + m_thisDeviceManufacturer(std::move(thisDeviceManufacturer)), + m_poseConfiguration(poseConfiguration) { + + if (m_poseConfiguration.controllerOverrideEnabled) { + m_shadowControllerId = m_poseConfiguration.controllerIdOverride; + } else { + m_controllerDiscoverer = std::make_unique(shadowDeviceOfRole, [&](ControllerDiscoveryPipeData_t data) { + m_shadowControllerId = data.controllerId; + DriverLog("Received controller id from overlay: %i", data.controllerId); + }); + + m_controllerDiscoverer->Start(); + } + m_calibration = std::make_unique(); +} -vr::DriverPose_t ControllerPose::UpdatePose() { - vr::DriverPose_t newPose = {0}; - newPose.qWorldFromDriverRotation.w = 1; - newPose.qDriverFromHeadRotation.w = 1; +vr::TrackedDevicePose_t ControllerPose::GetControllerPose() { + vr::TrackedDevicePose_t trackedDevicePoses[vr::k_unMaxTrackedDeviceCount]; + vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0, trackedDevicePoses, + vr::k_unMaxTrackedDeviceCount); + return trackedDevicePoses[m_shadowControllerId]; +} - if (m_shadowControllerId != vr::k_unTrackedDeviceIndexInvalid) { - vr::TrackedDevicePose_t trackedDevicePoses[vr::k_unMaxTrackedDeviceCount]; - vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0, trackedDevicePoses, vr::k_unMaxTrackedDeviceCount); +vr::DriverPose_t ControllerPose::UpdatePose() { + if (m_calibration->isCalibrating()) + return m_calibration->GetMaintainPose(); - if (trackedDevicePoses[m_shadowControllerId].bPoseIsValid) { + vr::DriverPose_t newPose = {0}; + newPose.qWorldFromDriverRotation.w = 1; + newPose.qDriverFromHeadRotation.w = 1; - //get the matrix that represents the position of the controller that we are shadowing - vr::HmdMatrix34_t controllerMatrix = trackedDevicePoses[m_shadowControllerId].mDeviceToAbsoluteTracking; + if (m_shadowControllerId != vr::k_unTrackedDeviceIndexInvalid) { + vr::TrackedDevicePose_t controllerPose = GetControllerPose(); - //get only the rotation (3x3 matrix), as the 3x4 matrix also includes position - vr::HmdMatrix33_t controllerRotationMatrix = GetRotationMatrix(controllerMatrix); + if (controllerPose.bPoseIsValid) { + // get the matrix that represents the position of the controller that we are shadowing + vr::HmdMatrix34_t controllerMatrix = controllerPose.mDeviceToAbsoluteTracking; - //multiply the rotation matrix by the offset vector set that is the offset of the controller relative to the hand - vr::HmdVector3_t vectorOffset = MultiplyMatrix(controllerRotationMatrix, m_poseConfiguration.offsetVector); + // get only the rotation (3x3 matrix), as the 3x4 matrix also includes position + vr::HmdMatrix33_t controllerRotationMatrix = GetRotationMatrix(controllerMatrix); - //combine these positions to get the resultant position - vr::HmdVector3_t newControllerPosition = CombinePosition(controllerMatrix, vectorOffset); + // multiply the rotation matrix by the offset vector set that is the offset of the controller + // relative to the hand + vr::HmdVector3_t vectorOffset = + MultiplyMatrix(controllerRotationMatrix, m_poseConfiguration.offsetVector); - newPose.vecPosition[0] = newControllerPosition.v[0]; - newPose.vecPosition[1] = newControllerPosition.v[1]; - newPose.vecPosition[2] = newControllerPosition.v[2]; + // combine these positions to get the resultant position + vr::HmdVector3_t newControllerPosition = CombinePosition(controllerMatrix, vectorOffset); - //Multiply rotation quaternions together, as the controller may be rotated relative to the hand - newPose.qRotation = MultiplyQuaternion(GetRotation(controllerMatrix), m_poseConfiguration.angleOffsetQuaternion); + newPose.vecPosition[0] = newControllerPosition.v[0]; + newPose.vecPosition[1] = newControllerPosition.v[1]; + newPose.vecPosition[2] = newControllerPosition.v[2]; - //Copy other values from the controller that we want for this device - newPose.vecAngularVelocity[0] = trackedDevicePoses[m_shadowControllerId].vAngularVelocity.v[0]; - newPose.vecAngularVelocity[1] = trackedDevicePoses[m_shadowControllerId].vAngularVelocity.v[1]; - newPose.vecAngularVelocity[2] = trackedDevicePoses[m_shadowControllerId].vAngularVelocity.v[2]; + // Multiply rotation quaternions together, as the controller may be rotated relative to the + // hand + newPose.qRotation = MultiplyQuaternion(GetRotation(controllerMatrix), + m_poseConfiguration.angleOffsetQuaternion); - newPose.vecVelocity[0] = trackedDevicePoses[m_shadowControllerId].vVelocity.v[0]; - newPose.vecVelocity[1] = trackedDevicePoses[m_shadowControllerId].vVelocity.v[1]; - newPose.vecVelocity[2] = trackedDevicePoses[m_shadowControllerId].vVelocity.v[2]; + // Copy other values from the controller that we want for this device + newPose.vecAngularVelocity[0] = controllerPose.vAngularVelocity.v[0]; + newPose.vecAngularVelocity[1] = controllerPose.vAngularVelocity.v[1]; + newPose.vecAngularVelocity[2] = controllerPose.vAngularVelocity.v[2]; - newPose.poseIsValid = true; - newPose.deviceIsConnected = true; + newPose.vecVelocity[0] = controllerPose.vVelocity.v[0]; + newPose.vecVelocity[1] = controllerPose.vVelocity.v[1]; + newPose.vecVelocity[2] = controllerPose.vVelocity.v[2]; - newPose.result = vr::TrackingResult_Running_OK; + newPose.poseIsValid = true; + newPose.deviceIsConnected = true; - newPose.poseTimeOffset = m_poseConfiguration.poseOffset; - } else { - newPose.poseIsValid = false; - newPose.deviceIsConnected = true; - newPose.result = vr::TrackingResult_Uninitialized; - } + newPose.result = vr::TrackingResult_Running_OK; + newPose.poseTimeOffset = m_poseConfiguration.poseTimeOffset; } else { - newPose.result = vr::TrackingResult_Uninitialized; - newPose.deviceIsConnected = false; - DiscoverController(); + newPose.poseIsValid = false; + newPose.deviceIsConnected = true; + newPose.result = vr::TrackingResult_Uninitialized; } - return newPose; + } else { + newPose.result = vr::TrackingResult_Uninitialized; + newPose.deviceIsConnected = false; + } + + return newPose; } -//Checks if a role is the opposite hand from the current hand -bool ControllerPose::IsOtherRole(int32_t test) { - if (m_shadowDeviceOfRole == vr::ETrackedControllerRole::TrackedControllerRole_RightHand) - return test == vr::ETrackedControllerRole::TrackedControllerRole_LeftHand; - else - return test == vr::ETrackedControllerRole::TrackedControllerRole_RightHand; +void ControllerPose::StartCalibration() { + m_calibration->StartCalibration(UpdatePose()); } -void ControllerPose::DiscoverController() { - //if there's an override, default to that - if (m_poseConfiguration.controllerOverrideEnabled) { - DriverLog("Controller ID override set to id: %i", m_poseConfiguration.controllerIdOverride); - m_shadowControllerId = m_poseConfiguration.controllerIdOverride; +void ControllerPose::FinishCalibration() { + if (m_shadowControllerId == vr::k_unTrackedDeviceIndexInvalid) { + DebugDriverLog("Index invalid"); + CancelCalibration(); return; } + m_poseConfiguration = m_calibration->FinishCalibration(GetControllerPose(), m_poseConfiguration, isRightHand()); +} - std::priority_queue> backupDevices; - bool otherTaken = false; - - //omit id 0, as this is always the headset pose - for (int i = 1; i < vr::k_unMaxTrackedDeviceCount; i++) { - vr::ETrackedPropertyError err; - vr::PropertyContainerHandle_t container = vr::VRProperties()->TrackedDeviceToPropertyContainer(i); - std::string foundDeviceManufacturer = vr::VRProperties()->GetStringProperty(container, - vr::Prop_ManufacturerName_String, - &err); - const int32_t foundDeviceClass = vr::VRProperties()->GetInt32Property(container, vr::ETrackedDeviceProperty::Prop_DeviceClass_Int32, &err); - - int32_t foundControllerRole = vr::VRProperties()->GetInt32Property(container, - vr::ETrackedDeviceProperty::Prop_ControllerRoleHint_Int32, - &err); - - //make sure we're not trying to find our own controllers - if (m_thisDeviceManufacturer == foundDeviceManufacturer) continue; - - //We have a device which identifies itself as the correct role, and that device is not this one. - if (foundControllerRole == m_shadowDeviceOfRole) { - DriverLog("Discovered a %s handed controller! Id: %i, Manufacturer: %s", - m_shadowDeviceOfRole == vr::ETrackedControllerRole::TrackedControllerRole_RightHand ? "right" : "left", - i, foundDeviceManufacturer.c_str()); - m_shadowControllerId = i; - return; - } - //If this belongs to the other hand, then skip it - else if (IsOtherRole(foundControllerRole)) { - otherTaken = true; - continue; - } - //otherwise fill backup - switch (foundDeviceClass) { - case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller: - backupDevices.push(std::make_pair(2, i)); //this is a generic controller like a vive wand - break; - case vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker: - backupDevices.push(std::make_pair(1, i)); //this is a generic tracker - break; - /*default: - backupDevices.push(std::make_pair(0, i)); //currently not considering other devices - */ - } - } - - //If we haven't already picked something, use the backup - if (!backupDevices.empty()) { - m_shadowControllerId = backupDevices.top().second; - DriverLog("Selected a controller/tracker from backup. Id: %i, Priority: %i", m_shadowControllerId, backupDevices.top().first); - vr::ETrackedPropertyError err; - vr::PropertyContainerHandle_t container = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_shadowControllerId); - /* - * There really needs to be a better way to ensure we're attached to the right generic controller, - * but unfortunately we have not found a practical solution for this yet which works with vive wands. :( - */ - vr::VRProperties()->SetInt32Property(container, vr::ETrackedDeviceProperty::Prop_ControllerRoleHint_Int32, m_shadowDeviceOfRole); - } - //else - //DebugDriverLog("No suitable devices to attach to!");// +void ControllerPose::CancelCalibration() { m_calibration->CancelCalibration(); } + +bool ControllerPose::isCalibrating() { + return m_calibration->isCalibrating(); +} + +bool ControllerPose::isRightHand() { + return m_shadowDeviceOfRole == vr::TrackedControllerRole_RightHand; } \ No newline at end of file diff --git a/src/DeviceDriver/KnuckleDriver.cpp b/src/DeviceDriver/KnuckleDriver.cpp index d8a6e38e..f3cda1bf 100644 --- a/src/DeviceDriver/KnuckleDriver.cpp +++ b/src/DeviceDriver/KnuckleDriver.cpp @@ -4,255 +4,277 @@ #include "DriverLog.h" -namespace knuckleDevice { - const char* c_deviceManufacturer = "FluidControlObject"; -} +static const char* c_deviceManufacturer = "LucidVR"; enum ComponentIndex : int { - SYSTEM_CLICK, - SYSTEM_TOUCH, - TRIGGER_CLICK, - TRIGGER_VALUE, - TRACKPAD_X, - TRACKPAD_Y, - TRACKPAD_TOUCH, - TRACKPAD_FORCE, - GRIP_TOUCH, - GRIP_FORCE, - GRIP_VALUE, - THUMBSTICK_CLICK, - THUMBSTICK_TOUCH, - THUMBSTICK_X, - THUMBSTICK_Y, - A_CLICK, - A_TOUCH, - B_CLICK, - B_TOUCH, - FINGER_INDEX, - FINGER_MIDDLE, - FINGER_RING, - FINGER_PINKY + SYSTEM_CLICK, + SYSTEM_TOUCH, + TRIGGER_CLICK, + TRIGGER_VALUE, + TRACKPAD_X, + TRACKPAD_Y, + TRACKPAD_TOUCH, + TRACKPAD_FORCE, + GRIP_TOUCH, + GRIP_FORCE, + GRIP_VALUE, + THUMBSTICK_CLICK, + THUMBSTICK_TOUCH, + THUMBSTICK_X, + THUMBSTICK_Y, + A_CLICK, + A_TOUCH, + B_CLICK, + B_TOUCH, + FINGER_INDEX, + FINGER_MIDDLE, + FINGER_RING, + FINGER_PINKY }; KnuckleDeviceDriver::KnuckleDeviceDriver(VRDeviceConfiguration_t configuration, std::unique_ptr communicationManager, std::string serialNumber) - : m_configuration(configuration), - m_communicationManager(std::move(communicationManager)), - m_serialNumber(std::move(serialNumber)), - m_driverId(-1), - m_hasActivated(false) { - - //copy a default bone transform to our hand transform for use in finger positioning later - std::copy( - std::begin(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), - std::end(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), - std::begin(m_handTransforms) - ); - + : m_configuration(configuration), + m_communicationManager(std::move(communicationManager)), + m_serialNumber(std::move(serialNumber)), + m_driverId(-1), + m_hasActivated(false) { + // copy a default bone transform to our hand transform for use in finger positioning later + std::copy(std::begin(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), + std::end(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), std::begin(m_handTransforms)); } +bool KnuckleDeviceDriver::IsRightHand() const { return m_configuration.role == vr::TrackedControllerRole_RightHand; } -bool KnuckleDeviceDriver::IsRightHand() const { - return m_configuration.role == vr::TrackedControllerRole_RightHand; -} +std::string KnuckleDeviceDriver::GetSerialNumber() { return m_serialNumber; } + +bool KnuckleDeviceDriver::IsActive() { return m_hasActivated; } -std::string KnuckleDeviceDriver::GetSerialNumber() { - return m_serialNumber; -} -bool KnuckleDeviceDriver::IsActive() { - return m_hasActivated; -} vr::EVRInitError KnuckleDeviceDriver::Activate(uint32_t unObjectId) { - const bool isRightHand = IsRightHand(); - m_driverId = unObjectId; //unique ID for your driver - m_controllerPose = std::make_unique(m_configuration.role, std::string(knuckleDevice::c_deviceManufacturer), m_configuration.poseConfiguration); - - vr::PropertyContainerHandle_t props = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_driverId); //this gets a container object where you store all the information about your driver - - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); - vr::VRProperties()->SetStringProperty(props, vr::Prop_TrackingSystemName_String, "lighthouse"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, GetSerialNumber().c_str()); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_WillDriftInYaw_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsWireless_Bool, true); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, false); - vr::VRProperties()->SetFloatProperty(props, vr::Prop_DeviceBatteryPercentage_Float, 1.f); // Always charged - - vr::HmdMatrix34_t l_matrix = { -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f }; - vr::VRProperties()->SetProperty(props, vr::Prop_StatusDisplayTransform_Matrix34, &l_matrix, sizeof(vr::HmdMatrix34_t), vr::k_unHmdMatrix34PropertyTag); - - vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_UpdateAvailable_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_ManualUpdate_Bool, false); - vr::VRProperties()->SetStringProperty(props, vr::Prop_Firmware_ManualUpdateURL_String, "https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware"); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceCanPowerOff_Bool, false); - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, (int32_t)vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_ForceUpdateRequired_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_Identifiable_Bool, true); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_RemindUpdate_Bool, false); - vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis0Type_Int32, vr::k_eControllerAxis_TrackPad); - vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis1Type_Int32, vr::k_eControllerAxis_Trigger); - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, IsRightHand() ? vr::TrackedControllerRole_RightHand : vr::TrackedControllerRole_LeftHand); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasDisplayComponent_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasCameraComponent_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasDriverDirectModeComponent_Bool, false); - vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasVirtualDisplayComponent_Bool, false); - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, IsRightHand() ? "Knuckles Right" : "Knuckles Left"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, IsRightHand() ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, knuckleDevice::c_deviceManufacturer); - vr::VRProperties()->SetStringProperty(props, vr::Prop_TrackingFirmwareVersion_String, "1562916277 watchman@ValveBuilder02 2019-07-12 FPGA 538(2.26/10/2) BL 0 VRC 1562916277 Radio 1562882729"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_HardwareRevision_String, "product 17 rev 14.1.9 lot 2019/4/20 0"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ConnectedWirelessDongle_String, "C2F75F5986-DIY"); - vr::VRProperties()->SetUint64Property(props, vr::Prop_HardwareRevision_Uint64, 286130441U); - vr::VRProperties()->SetUint64Property(props, vr::Prop_FirmwareVersion_Uint64, 1562916277U); - vr::VRProperties()->SetUint64Property(props, vr::Prop_FPGAVersion_Uint64, 538U); - vr::VRProperties()->SetUint64Property(props, vr::Prop_VRCVersion_Uint64, 1562916277U); - vr::VRProperties()->SetUint64Property(props, vr::Prop_RadioVersion_Uint64, 1562882729U); - vr::VRProperties()->SetUint64Property(props, vr::Prop_DongleVersion_Uint64, 1558748372U); - vr::VRProperties()->SetStringProperty(props, vr::Prop_Firmware_ProgrammingTarget_String, IsRightHand() ? "LHR-E217CD01" : "LHR-E217CD00"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ResourceRoot_String, "indexcontroller"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_RegisteredDeviceType_String, IsRightHand() ? "valve/index_controllerLHR-E217CD01" : "valve/index_controllerLHR-E217CD00"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{indexcontroller}/input/index_controller_profile.json"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, IsRightHand() ? "{openglove}/icons/right_controller_status_off.png" : "{openglove}/icons/left_controller_status_off.png"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, IsRightHand() ? "{openglove}/icons/right_controller_status_searching.gif" : "{openglove}/icons/left_controller_status_searching.gif"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, IsRightHand() ? "{openglove}/icons/right_controller_status_searching_alert.gif" : "{openglove}/icons/left_controller_status_searching_alert.gif"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, IsRightHand() ? "{openglove}/icons/right_controller_status_ready.png" : "{openglove}/icons/left_controller_status_ready.png"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, IsRightHand() ? "{openglove}/icons/right_controller_status_ready_alert.png" : "{openglove}/icons/left_controller_status_ready_alert.png"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, IsRightHand() ? "{openglove}/icons/right_controller_status_error.png" : "{openglove}/icons/left_controller_status_error.png"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, IsRightHand() ? "{openglove}/icons/right_controller_status_off.png" : "{openglove}/icons/left_controller_status_off.png"); - vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, IsRightHand() ? "{openglove}/icons/right_controller_status_ready_low.png" : "{openglove}/icons/left_controller_status_ready_low.png"); - - vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis2Type_Int32, vr::k_eControllerAxis_Trigger); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "knuckles"); - - - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/system/click", &m_inputComponentHandles[ComponentIndex::SYSTEM_CLICK]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/system/touch", &m_inputComponentHandles[ComponentIndex::SYSTEM_TOUCH]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trigger/click", &m_inputComponentHandles[ComponentIndex::TRIGGER_CLICK]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/trigger/value", &m_inputComponentHandles[ComponentIndex::TRIGGER_VALUE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/x", &m_inputComponentHandles[ComponentIndex::TRACKPAD_X], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/y", &m_inputComponentHandles[ComponentIndex::TRACKPAD_Y], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &m_inputComponentHandles[ComponentIndex::TRACKPAD_TOUCH]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/force", &m_inputComponentHandles[ComponentIndex::TRACKPAD_FORCE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/grip/touch", &m_inputComponentHandles[ComponentIndex::GRIP_TOUCH]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/grip/force", &m_inputComponentHandles[ComponentIndex::GRIP_FORCE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/grip/value", &m_inputComponentHandles[ComponentIndex::GRIP_VALUE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_CLICK]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_TOUCH]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/thumbstick/x", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_X], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/thumbstick/y", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_Y], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/a/click", &m_inputComponentHandles[ComponentIndex::A_CLICK]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/a/touch", &m_inputComponentHandles[ComponentIndex::A_TOUCH]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/b/click", &m_inputComponentHandles[ComponentIndex::B_CLICK]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/b/touch", &m_inputComponentHandles[ComponentIndex::B_TOUCH]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/index", &m_inputComponentHandles[ComponentIndex::FINGER_INDEX], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/middle", &m_inputComponentHandles[ComponentIndex::FINGER_MIDDLE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/ring", &m_inputComponentHandles[ComponentIndex::FINGER_RING], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/pinky", &m_inputComponentHandles[ComponentIndex::FINGER_PINKY], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateHapticComponent(props, "/output/haptic", &m_haptic); - - vr::EVRInputError error = vr::VRDriverInput()->CreateSkeletonComponent(props, - isRightHand ? "/input/skeleton/right" : "/input/skeleton/left", - isRightHand ? "/skeleton/hand/right" : "/skeleton/hand/left", - "/pose/raw", - vr::VRSkeletalTracking_Partial, - isRightHand ? rightOpenPose : leftOpenPose, - NUM_BONES, - &m_skeletalComponentHandle); - - if (error != vr::VRInputError_None) { - DebugDriverLog("CreateSkeletonComponent failed. Error: %s\n", error); - } - - StartDevice(); - - m_hasActivated = true; - - return vr::VRInitError_None; + const bool isRightHand = IsRightHand(); + m_driverId = unObjectId; // unique ID for your driver + m_controllerPose = std::make_unique(m_configuration.role, std::string(c_deviceManufacturer), m_configuration.poseConfiguration); + + vr::PropertyContainerHandle_t props = + vr::VRProperties()->TrackedDeviceToPropertyContainer(m_driverId); // this gets a container object where you store all the information about your driver + + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); + vr::VRProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, GetSerialNumber().c_str()); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_WillDriftInYaw_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsWireless_Bool, true); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceIsCharging_Bool, false); + vr::VRProperties()->SetFloatProperty(props, vr::Prop_DeviceBatteryPercentage_Float, 1.f); // Always charged + + vr::HmdMatrix34_t l_matrix = {-1.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f}; + vr::VRProperties()->SetProperty(props, vr::Prop_StatusDisplayTransform_Matrix34, &l_matrix, sizeof(vr::HmdMatrix34_t), vr::k_unHmdMatrix34PropertyTag); + + vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_UpdateAvailable_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_ManualUpdate_Bool, false); + vr::VRProperties()->SetStringProperty(props, vr::Prop_Firmware_ManualUpdateURL_String, "https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware"); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceProvidesBatteryStatus_Bool, true); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_DeviceCanPowerOff_Bool, false); + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, (int32_t)vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_ForceUpdateRequired_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_Identifiable_Bool, true); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_Firmware_RemindUpdate_Bool, false); + vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis0Type_Int32, vr::k_eControllerAxis_TrackPad); + vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis1Type_Int32, vr::k_eControllerAxis_Trigger); + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, + IsRightHand() ? vr::TrackedControllerRole_RightHand : vr::TrackedControllerRole_LeftHand); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasDisplayComponent_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasCameraComponent_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasDriverDirectModeComponent_Bool, false); + vr::VRProperties()->SetBoolProperty(props, vr::Prop_HasVirtualDisplayComponent_Bool, false); + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, IsRightHand() ? "Knuckles Right" : "Knuckles Left"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, + IsRightHand() ? "{indexcontroller}valve_controller_knu_1_0_right" : "{indexcontroller}valve_controller_knu_1_0_left"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, c_deviceManufacturer); + vr::VRProperties()->SetStringProperty(props, vr::Prop_TrackingFirmwareVersion_String, + "1562916277 watchman@ValveBuilder02 2019-07-12 FPGA 538(2.26/10/2) BL 0 VRC 1562916277 Radio 1562882729"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_HardwareRevision_String, "product 17 rev 14.1.9 lot 2019/4/20 0"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ConnectedWirelessDongle_String, "C2F75F5986-DIY"); + vr::VRProperties()->SetUint64Property(props, vr::Prop_HardwareRevision_Uint64, 286130441U); + vr::VRProperties()->SetUint64Property(props, vr::Prop_FirmwareVersion_Uint64, 1562916277U); + vr::VRProperties()->SetUint64Property(props, vr::Prop_FPGAVersion_Uint64, 538U); + vr::VRProperties()->SetUint64Property(props, vr::Prop_VRCVersion_Uint64, 1562916277U); + vr::VRProperties()->SetUint64Property(props, vr::Prop_RadioVersion_Uint64, 1562882729U); + vr::VRProperties()->SetUint64Property(props, vr::Prop_DongleVersion_Uint64, 1558748372U); + vr::VRProperties()->SetStringProperty(props, vr::Prop_Firmware_ProgrammingTarget_String, IsRightHand() ? "LHR-E217CD01" : "LHR-E217CD00"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ResourceRoot_String, "indexcontroller"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_RegisteredDeviceType_String, + IsRightHand() ? "valve/index_controllerLHR-E217CD01" : "valve/index_controllerLHR-E217CD00"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{indexcontroller}/input/index_controller_profile.json"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_off.png" : "{openglove}/icons/left_controller_status_off.png"); + vr::VRProperties()->SetStringProperty( + props, vr::Prop_NamedIconPathDeviceSearching_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_searching.gif" : "{openglove}/icons/left_controller_status_searching.gif"); + vr::VRProperties()->SetStringProperty( + props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_searching_alert.gif" : "{openglove}/icons/left_controller_status_searching_alert.gif"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_ready.png" : "{openglove}/icons/left_controller_status_ready.png"); + vr::VRProperties()->SetStringProperty( + props, vr::Prop_NamedIconPathDeviceReadyAlert_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_ready_alert.png" : "{openglove}/icons/left_controller_status_ready_alert.png"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_error.png" : "{openglove}/icons/left_controller_status_error.png"); + vr::VRProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_off.png" : "{openglove}/icons/left_controller_status_off.png"); + vr::VRProperties()->SetStringProperty( + props, vr::Prop_NamedIconPathDeviceAlertLow_String, + IsRightHand() ? "{openglove}/icons/right_controller_status_ready_low.png" : "{openglove}/icons/left_controller_status_ready_low.png"); + + vr::VRProperties()->SetInt32Property(props, vr::Prop_Axis2Type_Int32, vr::k_eControllerAxis_Trigger); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "knuckles"); + + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/system/click", &m_inputComponentHandles[ComponentIndex::SYSTEM_CLICK]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/system/touch", &m_inputComponentHandles[ComponentIndex::SYSTEM_TOUCH]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trigger/click", &m_inputComponentHandles[ComponentIndex::TRIGGER_CLICK]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/trigger/value", &m_inputComponentHandles[ComponentIndex::TRIGGER_VALUE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/x", &m_inputComponentHandles[ComponentIndex::TRACKPAD_X], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/y", &m_inputComponentHandles[ComponentIndex::TRACKPAD_Y], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &m_inputComponentHandles[ComponentIndex::TRACKPAD_TOUCH]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/trackpad/force", &m_inputComponentHandles[ComponentIndex::TRACKPAD_FORCE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/grip/touch", &m_inputComponentHandles[ComponentIndex::GRIP_TOUCH]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/grip/force", &m_inputComponentHandles[ComponentIndex::GRIP_FORCE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/grip/value", &m_inputComponentHandles[ComponentIndex::GRIP_VALUE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/thumbstick/click", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_CLICK]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/thumbstick/touch", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_TOUCH]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/thumbstick/x", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_X], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/thumbstick/y", &m_inputComponentHandles[ComponentIndex::THUMBSTICK_Y], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/a/click", &m_inputComponentHandles[ComponentIndex::A_CLICK]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/a/touch", &m_inputComponentHandles[ComponentIndex::A_TOUCH]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/b/click", &m_inputComponentHandles[ComponentIndex::B_CLICK]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/b/touch", &m_inputComponentHandles[ComponentIndex::B_TOUCH]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/index", &m_inputComponentHandles[ComponentIndex::FINGER_INDEX], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/middle", &m_inputComponentHandles[ComponentIndex::FINGER_MIDDLE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/ring", &m_inputComponentHandles[ComponentIndex::FINGER_RING], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/pinky", &m_inputComponentHandles[ComponentIndex::FINGER_PINKY], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateHapticComponent(props, "/output/haptic", &m_haptic); + + vr::EVRInputError error = vr::VRDriverInput()->CreateSkeletonComponent( + props, isRightHand ? "/input/skeleton/right" : "/input/skeleton/left", isRightHand ? "/skeleton/hand/right" : "/skeleton/hand/left", "/pose/raw", + vr::VRSkeletalTracking_Partial, isRightHand ? rightOpenPose : leftOpenPose, NUM_BONES, &m_skeletalComponentHandle); + + if (error != vr::VRInputError_None) { + DebugDriverLog("CreateSkeletonComponent failed. Error: %s\n", error); + } + + StartDevice(); + + m_hasActivated = true; + + return vr::VRInitError_None; } -//This could do with a rename, its a bit vague as to what it does +// This could do with a rename, its a bit vague as to what it does void KnuckleDeviceDriver::StartDevice() { - m_communicationManager->Connect(); - if (m_communicationManager->IsConnected()) { - m_communicationManager->BeginListener([&](VRCommData_t datas) { - try { - //Compute each finger transform - for (int i = 0; i < NUM_BONES; i++) { - int fingerNum = FingerFromBone(i); - if (fingerNum != -1) { - ComputeBoneFlexion(&m_handTransforms[i], datas.flexion[fingerNum], i, IsRightHand()); - } - } - vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithoutController, m_handTransforms, NUM_BONES); - vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithController, m_handTransforms, NUM_BONES); - - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_X], datas.joyX, 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_Y], datas.joyY, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_CLICK], datas.joyButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_TOUCH], datas.joyButton, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::TRIGGER_CLICK], datas.trgButton, 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::TRIGGER_VALUE], datas.flexion[1], 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::A_CLICK], datas.aButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::A_TOUCH], datas.aButton, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::B_CLICK], datas.bButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::B_TOUCH], datas.bButton, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::GRIP_FORCE], datas.grab, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::GRIP_TOUCH], datas.grab, 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::GRIP_VALUE], datas.grab, 0); - - //We don't have a thumb on the index - //vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMB], datas.flexion[0], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_INDEX], datas.flexion[1], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_MIDDLE], datas.flexion[2], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_RING], datas.flexion[3], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_PINKY], datas.flexion[4], 0); - } - catch (const std::exception& e) { - DebugDriverLog("Exception caught while parsing comm data"); - } - }); - - } - else { - DebugDriverLog("Device did not connect successfully"); - //Perhaps retry - } + m_ffbProvider = std::make_unique( + [&](VRFFBData_t data) { + // Queue the force feedback data for sending. + m_communicationManager->QueueSend(data); + }, + m_configuration.role); + + m_ffbProvider->Start(); + m_communicationManager->Connect(); + if (m_communicationManager->IsConnected()) { + m_communicationManager->BeginListener([&](VRCommData_t datas) { + try { + // Compute each finger transform + for (int i = 0; i < NUM_BONES; i++) { + int fingerNum = FingerFromBone(i); + if (fingerNum != -1) { + ComputeBoneFlexion(&m_handTransforms[i], datas.flexion[fingerNum], i, IsRightHand()); + } + } + vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithoutController, m_handTransforms, NUM_BONES); + vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithController, m_handTransforms, NUM_BONES); + + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_X], datas.joyX, 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_Y], datas.joyY, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_CLICK], datas.joyButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::THUMBSTICK_TOUCH], datas.joyButton, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::TRIGGER_CLICK], datas.trgButton, 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::TRIGGER_VALUE], datas.flexion[1], 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::A_CLICK], datas.aButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::A_TOUCH], datas.aButton, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::B_CLICK], datas.bButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::B_TOUCH], datas.bButton, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::GRIP_FORCE], datas.grab, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::GRIP_TOUCH], datas.grab, 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::GRIP_VALUE], datas.grab, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::SYSTEM_CLICK], datas.menu, 0); + + + // We don't have a thumb on the index + // vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::THUMB], datas.flexion[0], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_INDEX], datas.flexion[1], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_MIDDLE], datas.flexion[2], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_RING], datas.flexion[3], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::FINGER_PINKY], datas.flexion[4], 0); + + if (datas.calibrate) { + if (!m_controllerPose->isCalibrating()) m_controllerPose->StartCalibration(); + } else { + if (m_controllerPose->isCalibrating()) m_controllerPose->FinishCalibration(); + } + } catch (const std::exception& e) { + DebugDriverLog("Exception caught while parsing comm data"); + } + }); + + } else { + DebugDriverLog("Device did not connect successfully"); + } } vr::DriverPose_t KnuckleDeviceDriver::GetPose() { - if (m_hasActivated) return m_controllerPose->UpdatePose(); + if (m_hasActivated) return m_controllerPose->UpdatePose(); - vr::DriverPose_t pose = { 0 }; - return pose; + vr::DriverPose_t pose = {0}; + return pose; } void KnuckleDeviceDriver::RunFrame() { - if (m_hasActivated) { - vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_driverId, m_controllerPose->UpdatePose(), sizeof(vr::DriverPose_t)); - } + if (m_hasActivated) { + vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_driverId, m_controllerPose->UpdatePose(), sizeof(vr::DriverPose_t)); + } } - void KnuckleDeviceDriver::Deactivate() { - if (m_hasActivated) { - m_communicationManager->Disconnect(); - m_driverId = vr::k_unTrackedDeviceIndexInvalid; - } + if (m_hasActivated) { + m_communicationManager->Disconnect(); + m_ffbProvider->Stop(); + m_driverId = vr::k_unTrackedDeviceIndexInvalid; + } } -void* KnuckleDeviceDriver::GetComponent(const char* pchComponentNameAndVersion) { - return nullptr; -} +void* KnuckleDeviceDriver::GetComponent(const char* pchComponentNameAndVersion) { return nullptr; } void KnuckleDeviceDriver::EnterStandby() {} void KnuckleDeviceDriver::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) { - if (unResponseBufferSize >= 1) { - pchResponseBuffer[0] = 0; - } + if (unResponseBufferSize >= 1) { + pchResponseBuffer[0] = 0; + } } \ No newline at end of file diff --git a/src/DeviceDriver/LucidGloveDriver.cpp b/src/DeviceDriver/LucidGloveDriver.cpp index 776cff95..75aa050b 100644 --- a/src/DeviceDriver/LucidGloveDriver.cpp +++ b/src/DeviceDriver/LucidGloveDriver.cpp @@ -2,195 +2,188 @@ #include "DriverLog.h" -namespace lucidGlove { - const char* c_deviceManufacturer = "Lucas_VRTech&Danwillm"; - const char* c_deviceControllerType = "lucidgloves"; - const char* c_deviceModelNumber = "lucidgloves1"; - const char* c_basePosePath = "/pose/raw"; - const char* c_inputProfilePath = "{lucidgloves}/input/openglove_profile.json"; - const char* c_renderModelPath = "{lucidgloves}/rendermodels/lucidgloves"; - -} +static const char* c_deviceManufacturer = "LucidVR"; +static const char* c_deviceControllerType = "lucidgloves"; +static const char* c_deviceModelNumber = "lucidgloves1"; +static const char* c_basePosePath = "/pose/raw"; +static const char* c_inputProfilePath = "{openglove}/input/openglove_profile.json"; enum ComponentIndex : int { - COMP_JOY_X = 0, - COMP_JOY_Y = 1, - COMP_JOY_BTN = 2, - COMP_BTN_TRG = 3, - COMP_BTN_A = 4, - COMP_BTN_B = 5, - COMP_GES_GRAB = 6, - COMP_GES_PINCH = 7, - COMP_HAPTIC = 8, - COMP_TRG_THUMB = 9, - COMP_TRG_INDEX = 10, - COMP_TRG_MIDDLE = 11, - COMP_TRG_RING = 12, - COMP_TRG_PINKY = 13 + COMP_JOY_X = 0, + COMP_JOY_Y = 1, + COMP_JOY_BTN = 2, + COMP_BTN_TRG = 3, + COMP_BTN_A = 4, + COMP_BTN_B = 5, + COMP_GES_GRAB = 6, + COMP_GES_PINCH = 7, + COMP_HAPTIC = 8, + COMP_TRG_THUMB = 9, + COMP_TRG_INDEX = 10, + COMP_TRG_MIDDLE = 11, + COMP_TRG_RING = 12, + COMP_TRG_PINKY = 13, + COMP_BTN_MENU = 14, }; -LucidGloveDeviceDriver::LucidGloveDeviceDriver(VRDeviceConfiguration_t configuration, std::unique_ptr communicationManager, std::string serialNumber) - : m_configuration(configuration), - m_communicationManager(std::move(communicationManager)), - m_serialNumber(serialNumber), - m_driverId(-1), - m_hasActivated(false) { - - //copy a default bone transform to our hand transform for use in finger positioning later - std::copy( - std::begin(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), - std::end(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), - std::begin(m_handTransforms) - ); +LucidGloveDeviceDriver::LucidGloveDeviceDriver(VRDeviceConfiguration_t configuration, std::unique_ptr communicationManager, + std::string serialNumber) + : m_configuration(configuration), m_communicationManager(std::move(communicationManager)), m_serialNumber(serialNumber), m_driverId(-1), m_hasActivated(false) { + // copy a default bone transform to our hand transform for use in finger positioning later + std::copy(std::begin(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), + std::end(m_configuration.role == vr::TrackedControllerRole_RightHand ? rightOpenPose : leftOpenPose), std::begin(m_handTransforms)); } -bool LucidGloveDeviceDriver::IsRightHand() const { - return m_configuration.role == vr::TrackedControllerRole_RightHand; -} +bool LucidGloveDeviceDriver::IsRightHand() const { return m_configuration.role == vr::TrackedControllerRole_RightHand; } + +std::string LucidGloveDeviceDriver::GetSerialNumber() { return m_serialNumber; } + +bool LucidGloveDeviceDriver::IsActive() { return m_hasActivated; } -std::string LucidGloveDeviceDriver::GetSerialNumber() { - return m_serialNumber; -} -bool LucidGloveDeviceDriver::IsActive() { - return m_hasActivated; -} vr::EVRInitError LucidGloveDeviceDriver::Activate(uint32_t unObjectId) { - const bool isRightHand = IsRightHand(); + const bool isRightHand = IsRightHand(); + + m_driverId = unObjectId; // unique ID for your driver + m_controllerPose = std::make_unique(m_configuration.role, std::string(c_deviceManufacturer), m_configuration.poseConfiguration); - m_driverId = unObjectId; //unique ID for your driver - m_controllerPose = std::make_unique(m_configuration.role, std::string(lucidGlove::c_deviceManufacturer), m_configuration.poseConfiguration); + vr::PropertyContainerHandle_t props = + vr::VRProperties()->TrackedDeviceToPropertyContainer(m_driverId); // this gets a container object where you store all the information about your driver - vr::PropertyContainerHandle_t props = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_driverId); //this gets a container object where you store all the information about your driver + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); + vr::VRProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, c_inputProfilePath); // tell OpenVR where to get your driver's Input Profile + vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, m_configuration.role); // tells OpenVR what kind of device this is + vr::VRProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, GetSerialNumber().c_str()); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, c_deviceModelNumber); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, c_deviceManufacturer); + vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, (int32_t)vr::TrackedDeviceClass_Controller); + vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, c_deviceControllerType); - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerHandSelectionPriority_Int32, (int32_t)2147483647); - vr::VRProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, lucidGlove::c_inputProfilePath); //tell OpenVR where to get your driver's Input Profile - vr::VRProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, m_configuration.role); //tells OpenVR what kind of device this is - vr::VRProperties()->SetStringProperty(props, vr::Prop_SerialNumber_String, GetSerialNumber().c_str()); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, lucidGlove::c_deviceModelNumber); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ManufacturerName_String, lucidGlove::c_deviceManufacturer); - vr::VRProperties()->SetInt32Property(props, vr::Prop_DeviceClass_Int32, (int32_t)vr::TrackedDeviceClass_Controller); - vr::VRProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, lucidGlove::c_deviceControllerType); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/joystick/x", &m_inputComponentHandles[ComponentIndex::COMP_JOY_X], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/joystick/y", &m_inputComponentHandles[ComponentIndex::COMP_JOY_Y], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/joystick/click", &m_inputComponentHandles[ComponentIndex::COMP_JOY_BTN]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/joystick/x", &m_inputComponentHandles[ComponentIndex::COMP_JOY_X], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/joystick/y", &m_inputComponentHandles[ComponentIndex::COMP_JOY_Y], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/joystick/click", &m_inputComponentHandles[ComponentIndex::COMP_JOY_BTN]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trigger/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_TRG]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/trigger/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_TRG]); + vr::VRDriverInput()->CreateBooleanComponent(props, isRightHand ? "/input/A/click" : "/input/system/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_A]); - vr::VRDriverInput()->CreateBooleanComponent(props, isRightHand ? "/input/A/click" : "/input/system/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_A]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/B/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_B]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/B/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_B]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/grab/click", &m_inputComponentHandles[ComponentIndex::COMP_GES_GRAB]); + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/pinch/click", &m_inputComponentHandles[ComponentIndex::COMP_GES_PINCH]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/grab/click", &m_inputComponentHandles[ComponentIndex::COMP_GES_GRAB]); - vr::VRDriverInput()->CreateBooleanComponent(props, "/input/pinch/click", &m_inputComponentHandles[ComponentIndex::COMP_GES_PINCH]); + vr::VRDriverInput()->CreateHapticComponent(props, "output/haptic", &m_inputComponentHandles[ComponentIndex::COMP_HAPTIC]); - vr::VRDriverInput()->CreateHapticComponent(props, "output/haptic", &m_inputComponentHandles[ComponentIndex::COMP_HAPTIC]); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/thumb", &m_inputComponentHandles[ComponentIndex::COMP_TRG_THUMB], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/index", &m_inputComponentHandles[ComponentIndex::COMP_TRG_INDEX], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/middle", &m_inputComponentHandles[ComponentIndex::COMP_TRG_MIDDLE], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/ring", &m_inputComponentHandles[ComponentIndex::COMP_TRG_RING], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/pinky", &m_inputComponentHandles[ComponentIndex::COMP_TRG_PINKY], vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + + vr::VRDriverInput()->CreateBooleanComponent(props, "/input/system/click", &m_inputComponentHandles[ComponentIndex::COMP_BTN_MENU]); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/thumb", &m_inputComponentHandles[ComponentIndex::COMP_TRG_THUMB], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/index", &m_inputComponentHandles[ComponentIndex::COMP_TRG_INDEX], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/middle", &m_inputComponentHandles[ComponentIndex::COMP_TRG_MIDDLE], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/ring", &m_inputComponentHandles[ComponentIndex::COMP_TRG_RING], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - vr::VRDriverInput()->CreateScalarComponent(props, "/input/finger/pinky", &m_inputComponentHandles[ComponentIndex::COMP_TRG_PINKY], vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided); - // Create the skeletal component and save the handle for later use + // Create the skeletal component and save the handle for later use// - vr::EVRInputError error = vr::VRDriverInput()->CreateSkeletonComponent(props, - isRightHand ? "/input/skeleton/right" : "/input/skeleton/left", - isRightHand ? "/skeleton/hand/right" : "/skeleton/hand/left", - "/pose/raw", - vr::VRSkeletalTracking_Partial, - isRightHand ? rightOpenPose : leftOpenPose, - NUM_BONES, - &m_skeletalComponentHandle); + vr::EVRInputError error = vr::VRDriverInput()->CreateSkeletonComponent( + props, isRightHand ? "/input/skeleton/right" : "/input/skeleton/left", isRightHand ? "/skeleton/hand/right" : "/skeleton/hand/left", "/pose/raw", + vr::VRSkeletalTracking_Partial, isRightHand ? rightOpenPose : leftOpenPose, NUM_BONES, &m_skeletalComponentHandle); - if (error != vr::VRInputError_None) { - // Handle failure case - DebugDriverLog("CreateSkeletonComponent failed. Error: %s\n", error); - } + if (error != vr::VRInputError_None) { + // Handle failure case + DebugDriverLog("CreateSkeletonComponent failed. Error: %s\n", error); + } - StartDevice(); + StartDevice(); - m_hasActivated = true; + m_hasActivated = true; - return vr::VRInitError_None; + return vr::VRInitError_None; } -//This could do with a rename, its a bit vague as to what it does +// This could do with a rename, its a bit vague as to what it does void LucidGloveDeviceDriver::StartDevice() { - m_communicationManager->Connect(); - //DebugDriverLog("Getting ready to connect:"); - if (m_communicationManager->IsConnected()) { - //DebugDriverLog("Connected successfully"); - m_communicationManager->BeginListener([&](VRCommData_t datas) { - try { - //Compute each finger transform - for (int i = 0; i < NUM_BONES; i++) { - int fingerNum = FingerFromBone(i); - if (fingerNum != -1) { - ComputeBoneFlexion(&m_handTransforms[i], datas.flexion[fingerNum], i, IsRightHand()); - } - } - vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithoutController, m_handTransforms, NUM_BONES); - vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithController, m_handTransforms, NUM_BONES); - - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_X], datas.joyX, 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_Y], datas.joyY, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_BTN], datas.joyButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_TRG], datas.trgButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_A], datas.aButton, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_B], datas.bButton, 0); - - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_GES_GRAB], datas.grab, 0); - vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_GES_PINCH], datas.pinch, 0); - - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_THUMB], datas.flexion[0], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_INDEX], datas.flexion[1], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_MIDDLE], datas.flexion[2], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_RING], datas.flexion[3], 0); - vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_PINKY], datas.flexion[4], 0); - } - catch (const std::exception& e) { - DebugDriverLog("Exception caught while parsing comm data"); - } - }); - - } - else { - DebugDriverLog("Device did not connect successfully"); - //Perhaps retry - } + m_communicationManager->Connect(); + // DebugDriverLog("Getting ready to connect:"); + if (m_communicationManager->IsConnected()) { + // DebugDriverLog("Connected successfully"); + m_communicationManager->BeginListener([&](VRCommData_t datas) { + try { + // Compute each finger transform + for (int i = 0; i < NUM_BONES; i++) { + int fingerNum = FingerFromBone(i); + if (fingerNum != -1) { + ComputeBoneFlexion(&m_handTransforms[i], datas.flexion[fingerNum], i, IsRightHand()); + } + } + vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithoutController, m_handTransforms, NUM_BONES); + vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletalComponentHandle, vr::VRSkeletalMotionRange_WithController, m_handTransforms, NUM_BONES); + + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_X], datas.joyX, 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_Y], datas.joyY, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_JOY_BTN], datas.joyButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_TRG], datas.trgButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_A], datas.aButton, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_B], datas.bButton, 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_GES_GRAB], datas.grab, 0); + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_GES_PINCH], datas.pinch, 0); + + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_THUMB], datas.flexion[0], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_INDEX], datas.flexion[1], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_MIDDLE], datas.flexion[2], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_RING], datas.flexion[3], 0); + vr::VRDriverInput()->UpdateScalarComponent(m_inputComponentHandles[ComponentIndex::COMP_TRG_PINKY], datas.flexion[4], 0); + + vr::VRDriverInput()->UpdateBooleanComponent(m_inputComponentHandles[ComponentIndex::COMP_BTN_MENU], datas.menu, 0); + if (datas.calibrate) { + if (!m_controllerPose->isCalibrating()) m_controllerPose->StartCalibration(); + } else { + if (m_controllerPose->isCalibrating()) m_controllerPose->FinishCalibration(); + } + } catch (const std::exception& e) { + DebugDriverLog("Exception caught while parsing comm data"); + } + }); + + } else { + DebugDriverLog("Device did not connect successfully"); + } } vr::DriverPose_t LucidGloveDeviceDriver::GetPose() { - if (m_hasActivated) return m_controllerPose->UpdatePose(); + if (m_hasActivated) return m_controllerPose->UpdatePose(); - vr::DriverPose_t pose = { 0 }; - return pose; + vr::DriverPose_t pose = {0}; + return pose; } void LucidGloveDeviceDriver::RunFrame() { - if (m_hasActivated) { - vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_driverId, m_controllerPose->UpdatePose(), sizeof(vr::DriverPose_t)); - } + if (m_hasActivated) { + vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_driverId, m_controllerPose->UpdatePose(), sizeof(vr::DriverPose_t)); + } } - void LucidGloveDeviceDriver::Deactivate() { - if (m_hasActivated) { - m_communicationManager->Disconnect(); - m_driverId = vr::k_unTrackedDeviceIndexInvalid; - } + if (m_hasActivated) { + m_communicationManager->Disconnect(); + m_driverId = vr::k_unTrackedDeviceIndexInvalid; + } } -void* LucidGloveDeviceDriver::GetComponent(const char* pchComponentNameAndVersion) { - return nullptr; -} +void* LucidGloveDeviceDriver::GetComponent(const char* pchComponentNameAndVersion) { return nullptr; } void LucidGloveDeviceDriver::EnterStandby() {} void LucidGloveDeviceDriver::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) { - if (unResponseBufferSize >= 1) { - pchResponseBuffer[0] = 0; - } + if (unResponseBufferSize >= 1) { + pchResponseBuffer[0] = 0; + } } \ No newline at end of file diff --git a/src/DeviceProvider.cpp b/src/DeviceProvider.cpp index c1df91c0..6d740bff 100644 --- a/src/DeviceProvider.cpp +++ b/src/DeviceProvider.cpp @@ -1,142 +1,225 @@ #include -#include "DriverLog.h" +#include -#include "DeviceDriver/LucidGloveDriver.h" -#include "DeviceDriver/KnuckleDriver.h" +#include +#include "Communication/BTSerialCommunicationManager.h" #include "Communication/SerialCommunicationManager.h" - +#include "DeviceDriver/KnuckleDriver.h" +#include "DeviceDriver/LucidGloveDriver.h" +#include "DriverLog.h" +#include "Encode/AlphaEncodingManager.h" +#include "Encode/LegacyEncodingManager.h" #include "Quaternion.h" -vr::EVRInitError DeviceProvider::Init(vr::IVRDriverContext* pDriverContext) { - vr::EVRInitError initError = InitServerDriverContext(pDriverContext); - if (initError != vr::EVRInitError::VRInitError_None) return initError; - - VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); - InitDriverLog(vr::VRDriverLog()); - - VRDeviceConfiguration_t leftConfiguration = GetDeviceConfiguration(vr::TrackedControllerRole_LeftHand); - VRDeviceConfiguration_t rightConfiguration = GetDeviceConfiguration(vr::TrackedControllerRole_RightHand); - - if (leftConfiguration.enabled) { - m_leftHand = InstantiateDeviceDriver(leftConfiguration); - vr::VRServerDriverHost()->TrackedDeviceAdded(m_leftHand->GetSerialNumber().c_str(), vr::TrackedDeviceClass_Controller, m_leftHand.get()); - } - if (rightConfiguration.enabled) { - m_rightHand = InstantiateDeviceDriver(rightConfiguration); - vr::VRServerDriverHost()->TrackedDeviceAdded(m_rightHand->GetSerialNumber().c_str(), vr::TrackedDeviceClass_Controller, m_rightHand.get()); - } - - return vr::VRInitError_None; +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +std::string GetDriverPath() { + char path[MAX_PATH]; + HMODULE hm = NULL; + + if (GetModuleHandleEx( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)&__ImageBase, &hm) == 0) { + int ret = GetLastError(); + fprintf(stderr, "GetModuleHandle failed, error = %d\n", ret); + // Return or however you want to handle an error. + } + if (GetModuleFileName(hm, path, sizeof(path)) == 0) { + int ret = GetLastError(); + fprintf(stderr, "GetModuleFileName failed, error = %d\n", ret); + // Return or however you want to handle an error. + } + + std::string::size_type pos = std::string(path).find_last_of("\\/"); + return std::string(path).substr(0, pos); } -std::unique_ptr DeviceProvider::InstantiateDeviceDriver(VRDeviceConfiguration_t configuration) { - - std::unique_ptr communicationManager; - std::unique_ptr encodingManager; - - bool isRightHand = configuration.role == vr::TrackedControllerRole_RightHand; - - switch (configuration.encodingProtocol) { - default: - DriverLog("No encoding protocol set. Using legacy."); - case VREncodingProtocol::LEGACY: - const int maxAnalogValue = vr::VRSettings()->GetInt32("encoding_legacy", "max_analog_value"); - encodingManager = std::make_unique(maxAnalogValue); - break; - } - - - - switch (configuration.communicationProtocol) { - case VRCommunicationProtocol::BTSERIAL: - { - DriverLog("Communication set to BTSerial"); - char name[248]; - vr::VRSettings()->GetString("communication_btserial", isRightHand ? "right_name" : "left_name", name, sizeof(name)); - VRBTSerialConfiguration_t btSerialSettings(name); - communicationManager = std::make_unique(btSerialSettings, std::move(encodingManager)); - break; - } - default: - DriverLog("No communication protocol set. Using serial."); - case VRCommunicationProtocol::SERIAL: - char port[16]; - vr::VRSettings()->GetString("communication_serial", isRightHand ? "right_port" : "left_port", port, sizeof(port)); - VRSerialConfiguration_t serialSettings(port); - - communicationManager = std::make_unique(serialSettings, std::move(encodingManager)); - break; - } - - switch (configuration.deviceDriver) { - case VRDeviceDriver::EMULATED_KNUCKLES: - { - char serialNumber[32]; - vr::VRSettings()->GetString("device_knuckles", isRightHand ? "right_serial_number" : "left_serial_number", serialNumber, sizeof(serialNumber)); - - return std::make_unique(configuration, std::move(communicationManager), serialNumber); - } - - default: - DriverLog("No device driver selected. Using lucidgloves."); - case VRDeviceDriver::LUCIDGLOVES: - { - char serialNumber[32]; - vr::VRSettings()->GetString("device_lucidgloves", isRightHand ? "right_serial_number" : "left_serial_number", serialNumber, sizeof(serialNumber)); - - return std::make_unique(configuration, std::move(communicationManager), serialNumber); - } - } +bool CreateBackgroundProcess() { + STARTUPINFOA si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); -} -VRDeviceConfiguration_t DeviceProvider::GetDeviceConfiguration(vr::ETrackedControllerRole role) { - const bool isRightHand = role == vr::TrackedControllerRole_RightHand; - - const bool isEnabled = vr::VRSettings()->GetBool(c_driverSettingsSection, isRightHand ? "right_enabled" : "left_enabled"); - - const auto communicationProtocol = (VRCommunicationProtocol)vr::VRSettings()->GetInt32(c_driverSettingsSection, "communication_protocol"); - const auto encodingProtocol = (VREncodingProtocol)vr::VRSettings()->GetInt32(c_driverSettingsSection, "encoding_protocol"); - const auto deviceDriver = (VRDeviceDriver)vr::VRSettings()->GetInt32(c_driverSettingsSection, "device_driver"); + const std::string driverPath = GetDriverPath(); + DriverLog("Path to DLL: %s", driverPath.c_str()); - const float poseOffset = vr::VRSettings()->GetFloat(c_poseSettingsSection, "pose_offset"); + std::string path = driverPath + "\\openglove_overlay.exe"; - const float offsetXPos = vr::VRSettings()->GetFloat(c_poseSettingsSection, isRightHand ? "right_x_offset_position" : "left_x_offset_position"); - const float offsetYPos = vr::VRSettings()->GetFloat(c_poseSettingsSection, isRightHand ? "right_y_offset_position" : "left_y_offset_position"); - const float offsetZPos = vr::VRSettings()->GetFloat(c_poseSettingsSection, isRightHand ? "right_z_offset_position" : "left_z_offset_position"); + bool success = true; + if (!CreateProcess(path.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) success = false; - const int offsetXRot = vr::VRSettings()->GetInt32(c_poseSettingsSection, isRightHand ? "right_x_offset_degrees" : "left_x_offset_degrees"); - const int offsetYRot = vr::VRSettings()->GetInt32(c_poseSettingsSection, isRightHand ? "right_y_offset_degrees" : "left_y_offset_degrees"); - const int offsetZRot = vr::VRSettings()->GetInt32(c_poseSettingsSection, isRightHand ? "right_z_offset_degrees" : "left_z_offset_degrees"); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); - const bool controllerOverrideEnabled = vr::VRSettings()->GetBool(c_poseSettingsSection, "controller_override"); - const int controllerIdOverride = controllerOverrideEnabled ? vr::VRSettings()->GetInt32(c_poseSettingsSection, isRightHand ? "controller_override_right" : "controller_override_left"):-1; - - const vr::HmdVector3_t offsetVector = {offsetXPos, offsetYPos, offsetZPos}; - - //Convert the rotation to a quaternion - const vr::HmdQuaternion_t angleOffsetQuaternion = EulerToQuaternion(DegToRad(offsetXRot), DegToRad(offsetYRot), DegToRad(offsetZRot)); + return success; +} - return VRDeviceConfiguration_t(role, isEnabled, VRPoseConfiguration_t(offsetVector, angleOffsetQuaternion, poseOffset, controllerOverrideEnabled, controllerIdOverride), encodingProtocol, communicationProtocol, deviceDriver); +vr::EVRInitError DeviceProvider::Init(vr::IVRDriverContext* pDriverContext) { + vr::EVRInitError initError = InitServerDriverContext(pDriverContext); + if (initError != vr::EVRInitError::VRInitError_None) return initError; + + VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); + InitDriverLog(vr::VRDriverLog()); + DebugDriverLog("OpenGlove is running in DEBUG mode"); + + if (!CreateBackgroundProcess()) { + DriverLog("Could not create background process"); + return vr::VRInitError_Init_FileNotFound; + } + + VRDeviceConfiguration_t leftConfiguration = + GetDeviceConfiguration(vr::TrackedControllerRole_LeftHand); + VRDeviceConfiguration_t rightConfiguration = + GetDeviceConfiguration(vr::TrackedControllerRole_RightHand); + + if (leftConfiguration.enabled) { + m_leftHand = InstantiateDeviceDriver(leftConfiguration); + vr::VRServerDriverHost()->TrackedDeviceAdded( + m_leftHand->GetSerialNumber().c_str(), vr::TrackedDeviceClass_Controller, m_leftHand.get()); + } + if (rightConfiguration.enabled) { + m_rightHand = InstantiateDeviceDriver(rightConfiguration); + vr::VRServerDriverHost()->TrackedDeviceAdded(m_rightHand->GetSerialNumber().c_str(), + vr::TrackedDeviceClass_Controller, + m_rightHand.get()); + } + + return vr::VRInitError_None; +} +std::unique_ptr DeviceProvider::InstantiateDeviceDriver( + VRDeviceConfiguration_t configuration) { + std::unique_ptr communicationManager; + std::unique_ptr encodingManager; + + bool isRightHand = configuration.role == vr::TrackedControllerRole_RightHand; + switch (configuration.encodingProtocol) { + default: + DriverLog("No encoding protocol set. Using legacy."); + case VREncodingProtocol::LEGACY: { + const int maxAnalogValue = vr::VRSettings()->GetInt32("encoding_legacy", "max_analog_value"); + encodingManager = std::make_unique(maxAnalogValue); + break; + } + case VREncodingProtocol::ALPHA: { + const int maxAnalogValue = + vr::VRSettings()->GetInt32("encoding_alpha", "max_analog_value"); // + encodingManager = std::make_unique(maxAnalogValue); + break; + } + } + + switch (configuration.communicationProtocol) { + case VRCommunicationProtocol::BTSERIAL: { + DriverLog("Communication set to BTSerial"); + char name[248]; + vr::VRSettings()->GetString("communication_btserial", + isRightHand ? "right_name" : "left_name", name, sizeof(name)); + VRBTSerialConfiguration_t btSerialSettings(name); + communicationManager = std::make_unique( + btSerialSettings, std::move(encodingManager)); + break; + } + default: + DriverLog("No communication protocol set. Using serial."); + case VRCommunicationProtocol::SERIAL: + char port[16]; + vr::VRSettings()->GetString("communication_serial", isRightHand ? "right_port" : "left_port", + port, sizeof(port)); + VRSerialConfiguration_t serialSettings(port); + + communicationManager = + std::make_unique(serialSettings, std::move(encodingManager)); + break; + } + + switch (configuration.deviceDriver) { + case VRDeviceDriver::EMULATED_KNUCKLES: { + char serialNumber[32]; + vr::VRSettings()->GetString("device_knuckles", + isRightHand ? "right_serial_number" : "left_serial_number", + serialNumber, sizeof(serialNumber)); + + return std::make_unique(configuration, std::move(communicationManager), + serialNumber); + } + + default: + DriverLog("No device driver selected. Using lucidgloves."); + case VRDeviceDriver::LUCIDGLOVES: { + char serialNumber[32]; + vr::VRSettings()->GetString("device_lucidgloves", + isRightHand ? "right_serial_number" : "left_serial_number", + serialNumber, sizeof(serialNumber)); + + return std::make_unique( + configuration, std::move(communicationManager), serialNumber); + } + } +} +VRDeviceConfiguration_t DeviceProvider::GetDeviceConfiguration(vr::ETrackedControllerRole role) { + const bool isRightHand = role == vr::TrackedControllerRole_RightHand; + + const bool isEnabled = vr::VRSettings()->GetBool(c_driverSettingsSection, + isRightHand ? "right_enabled" : "left_enabled"); + + const auto communicationProtocol = (VRCommunicationProtocol)vr::VRSettings()->GetInt32( + c_driverSettingsSection, "communication_protocol"); + const auto encodingProtocol = + (VREncodingProtocol)vr::VRSettings()->GetInt32(c_driverSettingsSection, "encoding_protocol"); + const auto deviceDriver = + (VRDeviceDriver)vr::VRSettings()->GetInt32(c_driverSettingsSection, "device_driver"); + + const float poseTimeOffset = vr::VRSettings()->GetFloat(c_poseSettingsSection, "pose_time_offset"); + + const float offsetXPos = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_x_offset_position" : "left_x_offset_position"); + const float offsetYPos = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_y_offset_position" : "left_y_offset_position"); + const float offsetZPos = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_z_offset_position" : "left_z_offset_position"); + + const float offsetXRot = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_x_offset_degrees" : "left_x_offset_degrees"); + const float offsetYRot = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_y_offset_degrees" : "left_y_offset_degrees"); + const float offsetZRot = vr::VRSettings()->GetFloat( + c_poseSettingsSection, isRightHand ? "right_z_offset_degrees" : "left_z_offset_degrees"); + + const bool controllerOverrideEnabled = + vr::VRSettings()->GetBool(c_poseSettingsSection, "controller_override"); + const int controllerIdOverride = + controllerOverrideEnabled + ? vr::VRSettings()->GetInt32(c_poseSettingsSection, isRightHand + ? "controller_override_right" + : "controller_override_left") + : -1; + + const vr::HmdVector3_t offsetVector = {offsetXPos, offsetYPos, offsetZPos}; + + // Convert the rotation to a quaternion + const vr::HmdQuaternion_t angleOffsetQuaternion = + EulerToQuaternion(DegToRad(offsetXRot), DegToRad(offsetYRot), DegToRad(offsetZRot)); + + return VRDeviceConfiguration_t( + role, isEnabled, + VRPoseConfiguration_t(offsetVector, angleOffsetQuaternion, poseTimeOffset, + controllerOverrideEnabled, controllerIdOverride), + encodingProtocol, communicationProtocol, deviceDriver); } void DeviceProvider::Cleanup() {} -const char* const* DeviceProvider::GetInterfaceVersions() { - return vr::k_InterfaceVersions; -} +const char* const* DeviceProvider::GetInterfaceVersions() { return vr::k_InterfaceVersions; } void DeviceProvider::RunFrame() { - if (m_leftHand && m_leftHand->IsActive()) - m_leftHand->RunFrame(); - if (m_rightHand && m_rightHand->IsActive()) - m_rightHand->RunFrame(); + if (m_leftHand && m_leftHand->IsActive()) m_leftHand->RunFrame(); + if (m_rightHand && m_rightHand->IsActive()) m_rightHand->RunFrame(); } -bool DeviceProvider::ShouldBlockStandbyMode() { - return false; -} +bool DeviceProvider::ShouldBlockStandbyMode() { return false; } void DeviceProvider::EnterStandby() {} diff --git a/src/Encode/AlphaEncodingManager.cpp b/src/Encode/AlphaEncodingManager.cpp new file mode 100644 index 00000000..fa6c81f7 --- /dev/null +++ b/src/Encode/AlphaEncodingManager.cpp @@ -0,0 +1,105 @@ +#include + +#include +#include +#include "DriverLog.h" + + +/* Alpha encoding uses the wasted data in the delimiter from legacy to allow for optional arguments and redundancy over smaller packets +*Alpha Encoding Manager Arguments: +* A - Pinky Finger Position +* B - Ring Finger Position +* C - Middle Finger Position +* D - Index Finger Position +* E - Thumb Finger Position +* F - Joystick X +* G - Joystick Y +* H - Joystick click +* I - Trigger button +* J - A button +* K - B button +* L - Grab button +* M - Pinch button +* - Calibration Reset button +* +*/ + +std::string AlphaEncodingManager::getArgumentSubstring(std::string str, char del) { + int start = str.find(del); + if (start == std::string::npos) + return NULL; + int end = str.find_first_of(alphabet, start + 1); //characters may not necessarily be in order, so end at any letter + return str.substr(start, end - start); +} + +bool argValid(std::string str, char del) { return str.find(del) != std::string::npos; } + +VRCommData_t AlphaEncodingManager::Decode(std::string input) { + + std::array flexion; + std::array splay; + + for (int i = 0; i < 5; i++) { //splay tracking not yet supported + flexion[i] = -1; // 0.5; + splay[i] = 0.5; + } + + if (argValid(input, 'A')) + flexion[0] = + stof(getArgumentSubstring(input, 'A').substr(1, std::string::npos)) / m_maxAnalogValue; + if (argValid(input, 'B')) + flexion[1] = + stof(getArgumentSubstring(input, 'B').substr(1, std::string::npos)) / m_maxAnalogValue; + if (argValid(input, 'C')) + flexion[2] = + stof(getArgumentSubstring(input, 'C').substr(1, std::string::npos)) / m_maxAnalogValue; + if (argValid(input, 'D')) + flexion[3] = + stof(getArgumentSubstring(input, 'D').substr(1, std::string::npos)) / m_maxAnalogValue; + if (argValid(input, 'E')) + flexion[4] = + stof(getArgumentSubstring(input, 'E').substr(1, std::string::npos)) / m_maxAnalogValue; + + float joyX = 0; + float joyY = 0; + + if (argValid(input, 'F')) + joyX = 2 * stof(getArgumentSubstring(input, 'F').substr(1, std::string::npos)) / m_maxAnalogValue - 1; + if (argValid(input, 'G')) + joyY = 2 * stof(getArgumentSubstring(input, 'G').substr(1, std::string::npos)) / m_maxAnalogValue - 1; + + VRCommData_t commData( + flexion, + splay, + joyX, + joyY, + argValid(input, 'H'), //joystick click + argValid(input, 'I'), //trigger + argValid(input, 'J'), //A button + argValid(input, 'K'), //B button + argValid(input, 'L'), //grab + argValid(input, 'M'), //pinch + argValid(input, 'N'), //menu + argValid(input, 'O') //calibration + ); + + return commData; +} + +template +std::string string_format(const std::string& format, Args... args) { + int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + DriverLog("Error decoding string"); + return ""; + } + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + std::snprintf(buf.get(), size, format.c_str(), args...); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside +} + +std::string AlphaEncodingManager::Encode(const VRFFBData_t& data) { + std::string result = string_format("A%dB%dC%dD%dE%d\n", data.thumbCurl, data.indexCurl, data.middleCurl, data.ringCurl, data.pinkyCurl); + return result; +}; \ No newline at end of file diff --git a/src/Encode/LegacyEncodingManager.cpp b/src/Encode/LegacyEncodingManager.cpp index 4783bf76..17264b00 100644 --- a/src/Encode/LegacyEncodingManager.cpp +++ b/src/Encode/LegacyEncodingManager.cpp @@ -2,31 +2,32 @@ #include #include + #include "DriverLog.h" VRCommData_t LegacyEncodingManager::Decode(std::string input) { - std::string buf; - std::stringstream ss(input); + std::string buf; + std::stringstream ss(input); - std::vector tokens(VRCommDataInputPosition::MAX); - std::fill(tokens.begin(), tokens.begin() + VRCommDataInputPosition::MAX, 0.0f); + std::vector tokens(VRCommDataInputPosition::MAX); + std::fill(tokens.begin(), tokens.begin() + VRCommDataInputPosition::MAX, 0.0f); - short i = 0; - while (getline(ss, buf, '&')) { - tokens[i] = std::stof(buf); - i++; - } + short i = 0; + while (getline(ss, buf, '&')) { + tokens[i] = std::stof(buf); + i++; + } - std::array flexion; - std::array splay; + std::array flexion; + std::array splay; - for (int i = 0; i < 5; i++) { - flexion[i] = tokens[i] / m_maxAnalogValue; - splay[i] = 0.5; - } + for (int i = 0; i < 5; i++) { + flexion[i] = tokens[i] / m_maxAnalogValue; + splay[i] = 0.5; + } - const float joyX = (2 * tokens[VRCommDataInputPosition::JOY_X] / m_maxAnalogValue) - 1; - const float joyY = (2 * tokens[VRCommDataInputPosition::JOY_Y] / m_maxAnalogValue) - 1; + const float joyX = (2 * tokens[VRCommDataInputPosition::JOY_X] / m_maxAnalogValue) - 1; + const float joyY = (2 * tokens[VRCommDataInputPosition::JOY_Y] / m_maxAnalogValue) - 1; VRCommData_t commData( flexion, @@ -38,8 +39,28 @@ VRCommData_t LegacyEncodingManager::Decode(std::string input) { tokens[VRCommDataInputPosition::BTN_A] == 1, tokens[VRCommDataInputPosition::BTN_B] == 1, tokens[VRCommDataInputPosition::GES_GRAB] == 1, - tokens[VRCommDataInputPosition::GES_PINCH] == 1 + tokens[VRCommDataInputPosition::GES_PINCH] == 1, + false, + false ); - return commData; -} \ No newline at end of file + return commData; +} + +template +std::string string_format(const std::string& format, Args... args) { + int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + DriverLog("Error decoding string"); + return ""; + } + auto size = static_cast(size_s); + auto buf = std::make_unique(size); + std::snprintf(buf.get(), size, format.c_str(), args...); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside +} + +std::string LegacyEncodingManager::Encode(const VRFFBData_t& data) { + std::string result = string_format("%d&%d&%d&%d&%d\n", data.thumbCurl, data.indexCurl, data.middleCurl, data.ringCurl, data.pinkyCurl); + return result; +}; \ No newline at end of file diff --git a/src/ForceFeedback.cpp b/src/ForceFeedback.cpp new file mode 100644 index 00000000..34ff151a --- /dev/null +++ b/src/ForceFeedback.cpp @@ -0,0 +1,21 @@ +#include "ForceFeedback.h" + +#include + +#include "DriverLog.h" + +FFBListener::FFBListener(std::function callback, vr::ETrackedControllerRole role) : m_callback(callback), m_role(role) { + std::string pipeName = "\\\\.\\pipe\\vrapplication\\ffb\\curl\\"; + pipeName.append((role == vr::ETrackedControllerRole::TrackedControllerRole_RightHand) ? "right" : "left"); + m_pipe = std::make_unique(pipeName, sizeof(VRFFBData_t)); +}; + +void FFBListener::Start() { + m_pipe->Start([&](LPVOID data) { + VRFFBData_t *ffbData = (VRFFBData_t *)data; + + m_callback(*ffbData); + }); +} + +void FFBListener::Stop() { m_pipe->Stop(); }; \ No newline at end of file diff --git a/src/Quaternion.cpp b/src/Quaternion.cpp index 0d26feb9..90afcf8f 100644 --- a/src/Quaternion.cpp +++ b/src/Quaternion.cpp @@ -83,31 +83,26 @@ vr::HmdQuaternion_t QuaternionFromAngle(const double& xx, const double& yy, cons return quat; } -//Adapted from libGDX -vr::HmdQuaternion_t EulerToQuaternion(const double& yaw, const double& pitch, const double& roll) { - vr::HmdQuaternion_t result; +vr::HmdMatrix33_t QuaternionToMatrix(const vr::HmdQuaternion_t q) { - double hr = roll * 0.5f; - double shr = sin(hr); - double chr = cos(hr); - double hp = pitch * 0.5f; - double shp = sin(hp); - double chp = cos(hp); - double hy = yaw * 0.5f; - double shy = sin(hy); - double chy = cos(hy); - double chy_shp = chy * shp; - double shy_chp = shy * chp; - double chy_chp = chy * chp; - double shy_shp = shy * shp; - - result.x = (chy_shp * chr) + (shy_chp * shr); - result.y = (shy_chp * chr) - (chy_shp * shr); - result.z = (chy_chp * shr) - (shy_shp * chr); - result.w = (chy_chp * chr) + (shy_shp * shr); + vr::HmdMatrix33_t result = { { + {1 - 2*q.y*q.y - 2*q.z*q.z, 2*q.x*q.y - 2*q.z*q.w, 2*q.x*q.z + 2*q.y*q.w}, + {2*q.x*q.y + 2*q.z*q.w, 1 - 2*q.x*q.x - 2 * q.z*q.z, 2*q.y*q.z - 2*q.x*q.w}, + {2*q.x*q.z - 2*q.y*q.w, 2*q.y*q.z + 2*q.x*q.w, 1 - 2*q.x*q.x - 2*q.y*q.y} + } }; + return result; } +double QuatNorm(const vr::HmdQuaternion_t q) { + return sqrt(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z); +} + +vr::HmdQuaternion_t QuatConjugate(const vr::HmdQuaternion_t q) { + vr::HmdQuaternion_t quat = { q.w,-q.x,-q.y,-q.z }; + return quat; +} + vr::HmdQuaternion_t MultiplyQuaternion(const vr::HmdQuaternion_t& q, const vr::HmdQuaternion_t& r) { vr::HmdQuaternion_t result; @@ -116,5 +111,45 @@ vr::HmdQuaternion_t MultiplyQuaternion(const vr::HmdQuaternion_t& q, const vr::H result.y = (r.w * q.y + r.x * q.z + r.y * q.w - r.z * q.x); result.z = (r.w * q.z - r.x * q.y + r.y * q.x + r.z * q.w); + return result; +} + +vr::HmdQuaternion_t EulerToQuaternion(const double& yaw, const double& pitch, const double& roll) { + // Abbreviations for the various angular functions + double cy = cos(yaw * 0.5); + double sy = sin(yaw * 0.5); + double cp = cos(pitch * 0.5); + double sp = sin(pitch * 0.5); + double cr = cos(roll * 0.5); + double sr = sin(roll * 0.5); + + vr::HmdQuaternion_t q; + q.w = cr * cp * cy + sr * sp * sy; + q.x = sr * cp * cy - cr * sp * sy; + q.y = cr * sp * cy + sr * cp * sy; + q.z = cr * cp * sy - sr * sp * cy; + + return q; +} +vr::HmdVector3_t QuaternionToEuler(vr::HmdQuaternion_t q) { + vr::HmdVector3_t angles; + + // roll (x-axis rotation) + double sinr_cosp = 2 * (q.w * q.x + q.y * q.z); + double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y); + angles.v[0] = std::atan2(sinr_cosp, cosr_cosp); + + // pitch (y-axis rotation) + double sinp = 2 * (q.w * q.y - q.z * q.x); + if (std::abs(sinp) >= 1) + angles.v[1] = std::copysign(M_PI / 2, sinp); // use 90 degrees if out of range + else + angles.v[1] = std::asin(sinp); + + // yaw (z-axis rotation) + double siny_cosp = 2 * (q.w * q.z + q.x * q.y); + double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z); + angles.v[2] = std::atan2(siny_cosp, cosy_cosp); + vr::HmdVector3_t result = { RadToDeg(angles.v[2]), RadToDeg(angles.v[1]), RadToDeg(angles.v[0]) }; return result; } \ No newline at end of file diff --git a/src/Util/NamedPipe.cpp b/src/Util/NamedPipe.cpp new file mode 100644 index 00000000..52a42baa --- /dev/null +++ b/src/Util/NamedPipe.cpp @@ -0,0 +1,189 @@ +#include "Util/NamedPipe.h" + +#include + +#include "DriverLog.h" + +static std::string GetLastErrorAsString() { + // Get the error message ID, if any. + DWORD errorMessageID = ::GetLastError(); + + if (errorMessageID == 0) { + return std::string(); // No error message has been recorded + } + + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + LocalFree(messageBuffer); + + return message; +}; + +static VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap) { + LPPIPEINST lpPipeInst; + BOOL fWrite = FALSE; + + lpPipeInst = (LPPIPEINST)lpOverLap; + + if ((dwErr == 0) && (cbBytesRead != 0)) { + lpPipeInst->callback(&lpPipeInst->chRequest); + } +}; + +NamedPipeUtil::NamedPipeUtil(std::string pipeName, size_t pipeSize) : m_pipeSize(pipeSize), m_pipeName(pipeName), m_listenerActive(false), m_hPipe(0), m_lpPipeInst(0){}; + +bool NamedPipeUtil::Start(const std::function &callback) { + m_listenerActive = true; + m_pipeThread = std::thread(&NamedPipeUtil::PipeListenerThread, this, callback); + + return true; +} + +// Returns true if pending, false if the operation has completed. +bool NamedPipeUtil::CreateAndConnectInstance(LPOVERLAPPED lpo, std::string &pipeName) { + m_hPipe = CreateNamedPipe(pipeName.c_str(), // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_OVERLAPPED, // overlapped mode + PIPE_TYPE_MESSAGE | // message-type pipe + PIPE_READMODE_MESSAGE | // message read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // unlimited instances + m_pipeSize, // output buffer size + m_pipeSize, // input buffer size + 5000, // client time-out + NULL); // default security attributes + if (m_hPipe == INVALID_HANDLE_VALUE) { + DriverLog("CreateNamedPipe failed with with error: %s.\n", GetLastErrorAsString().c_str()); + return 0; + } else { + DriverLog("Pipe created successfully: %s", m_pipeName.c_str()); + } + + return ConnectToNewClient(lpo); +} + +bool NamedPipeUtil::ConnectToNewClient(LPOVERLAPPED lpo) { + BOOL fConnected, fPendingIO = FALSE; + + // Start an overlapped connection for this pipe instance. + fConnected = ConnectNamedPipe(&m_hPipe, lpo); + + if (fConnected) { + DriverLog("ConnectNamedPipe failed with %c.\n", GetLastErrorAsString().c_str()); + return 0; + } + + switch (GetLastError()) { + case ERROR_IO_PENDING: + fPendingIO = TRUE; + break; + + case ERROR_PIPE_CONNECTED: + if (SetEvent(lpo->hEvent)) break; + default: { + DriverLog("ConnectNamedPipe failed with: %s", GetLastErrorAsString().c_str()); + return 0; + } + } + return fPendingIO; +} + +void NamedPipeUtil::PipeListenerThread(const std::function &callback) { + OVERLAPPED oConnect; + HANDLE hConnectEvent; + bool fPendingIO, fSuccess; + DWORD dwWait, cbRet; + hConnectEvent = CreateEvent(NULL, // default security attribute + TRUE, // manual reset event + TRUE, // initial state = signaled + NULL); // unnamed event object + + if (hConnectEvent == NULL) { + DriverLog("CreateEvent failed with with error: %s.\n", GetLastErrorAsString().c_str()); + return; + } + + oConnect.hEvent = hConnectEvent; + DriverLog("Creating pipe: %s", m_pipeName.c_str()); + + fPendingIO = CreateAndConnectInstance(&oConnect, m_pipeName); + + while (m_listenerActive) { + dwWait = WaitForSingleObjectEx(hConnectEvent, // event object to wait for + INFINITE, // waits indefinitely + TRUE); // alertable wait enabled + switch (dwWait) { + case 0: { + if (fPendingIO) { + fSuccess = GetOverlappedResult(m_hPipe, // pipe handle + &oConnect, // OVERLAPPED structure + &cbRet, // bytes transferred + FALSE); // does not wait + if (!fSuccess) { + DriverLog("ConnectNamedPipe with error: %s.\n", GetLastErrorAsString().c_str()); + return; + } + } + + m_lpPipeInst = (LPPIPEINST)GlobalAlloc(GPTR, sizeof(PIPEINST)); + + if (m_lpPipeInst == NULL) { + DriverLog("GlobalAlloc failed with error: %s.\n", GetLastErrorAsString().c_str()); + return; + } + + m_lpPipeInst->hPipeInst = m_hPipe; + + m_lpPipeInst->cbToWrite = 0; + m_lpPipeInst->callback = callback; + + bool fRead = + ReadFileEx(m_lpPipeInst->hPipeInst, &m_lpPipeInst->chRequest, m_pipeSize, (LPOVERLAPPED)m_lpPipeInst, (LPOVERLAPPED_COMPLETION_ROUTINE)CompletedReadRoutine); + if (fRead) break; + + switch (GetLastError()) { + case ERROR_BROKEN_PIPE: + DriverLog("Detected that a client disconnected for pipe: %s", m_pipeName.c_str()); + ClosePipe(); + PipeListenerThread(callback); + break; + } + break; + } + case WAIT_IO_COMPLETION: { + break; + } + default: { + DriverLog("WaitForSingleObjectEx with error: %s.\n", GetLastErrorAsString().c_str()); + return; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } +} + +void NamedPipeUtil::ClosePipe() { + DriverLog("Closing pipe: %s", m_pipeName.c_str()); + if (!DisconnectNamedPipe(m_lpPipeInst->hPipeInst)) { + DriverLog("DisconnectNamedPipe failed with error: %s.\n", GetLastErrorAsString().c_str()); + } + + CloseHandle(m_lpPipeInst->hPipeInst); + + // Release the storage for the pipe instance. + if (m_lpPipeInst != NULL) GlobalFree(m_lpPipeInst); +} + +void NamedPipeUtil::Stop() { + if (m_listenerActive) { + m_listenerActive = false; + m_pipeThread.join(); + ClosePipe(); + } +} + +NamedPipeUtil::~NamedPipeUtil() { Stop(); }; \ No newline at end of file