diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..6bfe0ca0
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+ko_fi: sdraw_
diff --git a/.gitignore b/.gitignore
index 7e5b945d..1e8ed921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ ipch
*.opensdf
*.opendb
*.VC.db
+bin
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..fdea109d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "vendor/glm"]
+ path = vendor/glm
+ url = git://github.com/g-truc/glm.git
+[submodule "vendor/pugixml"]
+ path = vendor/pugixml
+ url = https://github.com/zeux/pugixml.git
+[submodule "vendor/openvr"]
+ path = vendor/openvr
+ url = https://github.com/ValveSoftware/openvr.git
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index ee83337d..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2015, Valve Corporation
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors
-may be used to endorse or promote products derived from this software without
-specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Paths.props b/Paths.props
deleted file mode 100644
index bbf5bf66..00000000
--- a/Paths.props
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- D:\Steam\steamapps\common\SteamVR
- C:\Users\Christian Buchner\Documents\Visual Studio 2015\Projects\Leap\openvr-1.0.0
- C:\Users\Christian Buchner\Documents\Visual Studio 2015\Projects\Leap\LeapSDK
- D:\Steam\steamapps\common\SteamVR\drivers\leap
-
-
-
-
-
- $(SteamVRRuntimeDir)
-
-
- $(OpenVRDir)
-
-
- $(LeapSDKDir)
-
-
- $(InstallDir)
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index c1201d5e..17136686 100644
--- a/README.md
+++ b/README.md
@@ -1,161 +1,30 @@
-# Leap Motion Driver for SteamVR
+# Warning
+This projects is closed due to unmanageable memory issues that came along with new Gemini versions.
-## Installation of this driver.
-
-- Get the Leap Motion Orion Beta runtimes https://developer.leapmotion.com/get-started
-- Install the Visual C++ 2015 Update 2 redistributables (32 and 64 bits) https://www.microsoft.com/en-us/download/details.aspx?id=51682
-- Download the most recent zip file from the Releases section of this project and run the contained exe setup program https://github.com/cbuchner1/driver_leap/releases
-
-Start SteamVR to see if two additional controllers show up (they should be blinking if your hands are
-not in the field of view of the Leap Motion, solid otherwise).
-
-### Troubleshooting
-
-If you experience frequent crashes of SteamVR on exit and this bothers you, uninstall my driver. I will try to fix this ASAP, but at the moment I have no clue why SteamVR is crashing.
-
-
-## Note about WORK IN PROGRESS
-
-You're seeing an early version of this software. I've got positional tracking established now as well as hand pose tracking. Some experimental mappings of hand gestures to triggers and buttons were added:
-
-Trigger:
-- bending of the index finger maps to the trigger button, like you would fire a gun.
-
-Grip:
-- clenching the middle, ring, pinky finger to a fist maps to the grabbing buttons
-
-Trackpad:
-- the thumbpress gesture (just point the thumb in the direction of your palm) touches and clicks the trackpad, depending on the intensity of your gesture.
-- pointing the index finger towards the are of the other hand's palm will emulate the touchpad. To press the touchpad in the desired position use the tumbpress gesture simultaneously while pointing.
-
-Menu buttons:
-- Flat hand held in front of you, palm towards face is used for application menu button
-- the Timeout pose (as used in sports), registers as the system menu button
-
-
-Swapped hands?
-
-when SteamVR confuses the left and right controller hands (which will be indicated by little hand icons shown near the bottom of the displayed Wand controllers), simply cross your hands the first time you bring them into view after starting SteamVR. This reverses the hand assignment and can improve the gaming experience for example in Audioshield.
-
-
-
-
-I am working on allowing to freely map gestures to buttons in the steamvr.vrsettings config file in your Steam\config folder. However note that the "leap_gestures" section is currently not parsed yet. It's merely a sign of things to come.
-
-## Supported gestures
-
-There are other gestures detected currently, but not mapped to buttons. If you want to try these out, click on the application "gesture_checker.exe" in the directory C:\Program Files (x86)\SteamVR Leap Motion driver\leap\bin\Win32
-
-Then I also recommend that you simultaneously bring up your Leap Motion's settings and from there start the diagnostic visualizer (the windowed version, not the VR one). Press "v" once to switch it to headmount optimized mode.
-
-Now pull both windows side by side and bring a hand into view. The command prompt running the gesture_checker program should output a series of numbers next to the names of the gestures. A "1.0" means confidential detection, a "0.0" means no detection.
-
-You can practise some gestures this way and also cross-check your pose in the Leap Motion diagnostic visualizer against the detection confidence.
-
- // Finger gestures (these would not throw your hand's orientation off much)
- TriggerFinger, // bend your index finger as if pulling a trigger
- LowerFist, // grab with your middle, ring, pinky fingers
- Pinch, // pinch with your thumb and index fingers
- Thumbpress, // point the thumb towards the direction of your pinky
-
- // Hand gestures (these would significantly change the orientation of your hand)
- FlippingTheBird, // flip someone off with your middle finger
- ILY, // pinky and index finger extended, middle and ring bent
- Victory, // V shape with your index, middle fingers, other fingers curled
- FlatHandPalmUp, // flat hand, palm points upwards (relative to alignment of Leap!)
- FlatHandPalmDown, // flat hand, palm points downwards (relative to alignment of Leap!)
- FlatHandPalmAway, // flat hand, palm points away from self (relative to alignment of Leap!)
- FlatHandPalmTowards, // flat hand, palm points towards self (relative to alignment of Leap!)
- ThumbUp, // thumb points up, remaining fingers form a fist
- ThumbInward, // thumb points towards the left for the right hand and vice versa
-
- // Two handed gestures
- Timeout, // both Hands form a T shape, signals a Timeout in sports
- TouchpadAxisX, // Touchpad emulation: index finger of other hand points towards palm
- TouchpadAxisY, // Touchpad emulation: index finger of other hand points towards palm
-
-
-### Games/Experiences that work mostly
-
-- the Blu (all three stages)
-- Irrational Exuberance: Prologue
-- the Rose and I
-- The Lab (some experiences work, others are tricky)
-- Final Approach
-- Audioshield: somehow the controllers are swapped? Control is tricky and not very precise. Semi-playable though.
+# Driver Leap [![Release](http://img.shields.io/github/release/SDraw/driver_leap.svg)](../../releases/latest)
+Fork with updated vendor libraries.
-### Games/Experiences that are starting but not quite playable yet.
-
-- Tilt Brush: starts and you can start doing things, but there is lack of complete trackpad support in my driver.
-- Brookhaven Experiment: tracking only works while SteamVR window is in focus. Why? Gun in right hand needs a 60 degree uptilt angle (define this in steamvr.vrsettings config file in Steam config folder). Trigger gesture detection is way to imprecise, you won't even survive the first wave of Zombies.
-
-
-### Demos that won't work at all
-- n/a
-
-
-## Known Issues
-
-I am seeing SteamVR Server crash on shutdown a lot. This could be related to my driver, but I have not yet found the root cause for the crash.
-
-The Brookhaven experiment seems to steal focus from StreamVR, so that Steam does not get any position tracking. Clicking on the SteamVR window restores tracking, but mutes the audio on Brookhaven. Meh.
-
-Some games work better when no grip angle is added to the controller pose, other games actually require a steep angle to be playable (Brookhaven, Audioshield). We may have to add a feature to chose the preferred default pose at runtime.
-
-Tracking is not quite reliable to always detect my trigger gestures. I think we will have to integrate small handheld controllers like the Wiimote or the Playstation Move Navigation controller in the future.
-
-I do not think I will be able to get animated hands into the 3D view, as the render model you can assign to each controller is mostly a static object. There are some JSON files to map joystick axes and triggers to animated parts of the displayed controller. But the fingers do not directly map to joystick axes directly and hence cannot be shown. Also not all games make use of SteamVR's internal controller visualization.
-
-
-## Building from Sourcecode (Developers only)
-
-### Install Dependencies
-
-1. Install SteamVR. It is under "Tools" in everyone's Steam Library. steam://install/250820
-2. Install "Leap Motion Orion SDK V3.1.2". https://developer.leapmotion.com/get-started
-3. Fetch the OpenVR SDK 1.0.0 from https://github.com/ValveSoftware/openvr .
-
-The solution and project files are for Visual Studio 2015.
-
-### Configure Paths
-
-Under "Property Manager" in Visual Studio, expand any of the configurations and find "Paths". Right click and select "Properties" and then "User Macros". Modify the paths to match your setup. InstallDir will be created, and will be configured as an external driver path for SteamVR.
-
-### Build
-
-You will probably want to build Release x86. You can also build x64. The post-build step will install the binaries and copy the resources to the configured InstallDir and register that path with SteamVR.
-
-## Preapring The Leap Motion Driver for use (Developers only)
-
-After building, the InstallDir should be a complete binary distribution. To use it:
-
-1. Register it with the SteamVR runtime via "vrpathreg adddriver $(InstallDir)". This is done automatically by a Post-Build step, but if you copy the files elsewhere you will have to do it by hand.
-2. Edit your config/steamvr.vrsettings to enable "activateMultipleDrivers". This is what allows the hydra driver to co-exist with any HMD. **Be sure to mind your commas.** Check vrserver.txt log to see if there were parse errors. Many of the settings are described at https://developer.valvesoftware.com/wiki/SteamVR/steamvr.vrsettings .
-```{
- ...
- "steamvr" : {
- "activateMultipleDrivers" : true
- }
-}```
-3. If you are trying to use the Hydra driver without an HMD, you might want to enable driver_null (no HMD) or set "requireHmd": false.
-
-After starting SteamVR, you should see controllers blinking in the status window until you move your hands into the field of view.
-
-You can use "vrcmd" (with no arguments) to see a list of devices to verify things are working.
-use "vrcmd" to verify things are loading:
-
-```...
-Driver leap : 2 displays
- leap (Serial number leap0_lefthand)
- leap (Serial number leap0_righthand)
-...
+## Installation (for users)
+* Install [Ultraleap Gemini v5.3.1](https://developer.leapmotion.com/tracking-software-download)
+* Extract [latest release archive](../../releases/latest) to `/drivers`
+* Add line in section `steamvr` of `/config/steamvr.vrsettings` file:
+```JSON
+"activateMultipleDrivers": true,
```
-You can also use "vrcmd --pollposes" (followed by an index number to limit the output) to see if things are working.
-
-## Licenses
-
-The code in this distribution is distributed under the terms of the LICENSE file in the root directory.
-
-The compiled driver and the install directory use the Leap Motion Orion SDK. Use subject to the terms of the Leap Motion SDK Agreement available at
-https://developer.leapmotion.com/sdk_agreement.
+## Usage
+### Settings
+Driver settings are configurated by editing `resources/settings.xml`. Available settings:
+* `trackingLevel`: skeleton tracking style for OpenVR. Can be `partial` or `full`. `partial` by default.
+* `handsReset`: marks controllers as out of range if hand for controller isn't detected by Leap Motion. `false` by default.
+* `interpolation`: enables internal Leap Motion data capture interpolation. `false` by default.
+* `useVelocity`: enables velocity data from Leap Motion for hands. `false` by default.
+
+### Gestures
+List of hands gestures that correspond to controller original input:
+* **Grip:** bending of middle, ring and pinky fingers
+* **Trigger:** bending of index finger.
+
+### Notes
+* Testing commits are currently pushed.
+* If you see only green dots that represent tip of your index fingers, force application to launch on dGPU through control panel of your GPU vendor.
diff --git a/driver.vrdrivermanifest b/driver.vrdrivermanifest
new file mode 100644
index 00000000..37c4d97b
--- /dev/null
+++ b/driver.vrdrivermanifest
@@ -0,0 +1,6 @@
+{
+ "alwaysActivate": true,
+ "name" : "leap",
+ "directory" : "",
+ "resourceOnly" : false
+}
diff --git a/driver_leap.sln b/driver_leap.sln
index e27313b9..ab45c045 100644
--- a/driver_leap.sln
+++ b/driver_leap.sln
@@ -1,68 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.1738
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver_leap", "drivers\driver_leap\driver_leap.vcxproj", "{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver_leap", "driver_leap\driver_leap.vcxproj", "{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "leap_monitor", "tools\leap_monitor\leap_monitor.vcxproj", "{BC06AF9C-36D6-455A-B421-00A9635684AD}"
-EndProject
-Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "leap_installer", "tools\leap_installer\leap_installer.vdproj", "{60517323-2772-4341-9161-56C776DC1840}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gesture_checker", "tools\gesture_checker\gesture_checker.vcxproj", "{9C28E205-C4CD-43D4-91BC-4852D7A588EC}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "config_tool", "tools\config_tool\config_tool.vcxproj", "{FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "leap_control", "leap_control\leap_control.csproj", "{B156A0E6-BC15-4987-A1E8-F6D6E69786BC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
Release|x64 = Release|x64
- Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Debug|x64.ActiveCfg = Debug|x64
{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Debug|x64.Build.0 = Debug|x64
- {52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Debug|x86.ActiveCfg = Debug|Win32
- {52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Debug|x86.Build.0 = Debug|Win32
{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Release|x64.ActiveCfg = Release|x64
{52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Release|x64.Build.0 = Release|x64
- {52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Release|x86.ActiveCfg = Release|Win32
- {52D3F16D-A7A5-4D6F-8F17-5E4B459A1440}.Release|x86.Build.0 = Release|Win32
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Debug|x64.ActiveCfg = Debug|x64
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Debug|x64.Build.0 = Debug|x64
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Debug|x86.ActiveCfg = Debug|Win32
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Debug|x86.Build.0 = Debug|Win32
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Release|x64.ActiveCfg = Release|x64
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Release|x64.Build.0 = Release|x64
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Release|x86.ActiveCfg = Release|Win32
- {BC06AF9C-36D6-455A-B421-00A9635684AD}.Release|x86.Build.0 = Release|Win32
- {60517323-2772-4341-9161-56C776DC1840}.Debug|x64.ActiveCfg = Debug
- {60517323-2772-4341-9161-56C776DC1840}.Debug|x64.Build.0 = Debug
- {60517323-2772-4341-9161-56C776DC1840}.Debug|x86.ActiveCfg = Debug
- {60517323-2772-4341-9161-56C776DC1840}.Debug|x86.Build.0 = Debug
- {60517323-2772-4341-9161-56C776DC1840}.Release|x64.ActiveCfg = Release
- {60517323-2772-4341-9161-56C776DC1840}.Release|x64.Build.0 = Release
- {60517323-2772-4341-9161-56C776DC1840}.Release|x86.ActiveCfg = Release
- {60517323-2772-4341-9161-56C776DC1840}.Release|x86.Build.0 = Release
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Debug|x64.ActiveCfg = Debug|x64
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Debug|x64.Build.0 = Debug|x64
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Debug|x86.ActiveCfg = Debug|Win32
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Debug|x86.Build.0 = Debug|Win32
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Release|x64.ActiveCfg = Release|x64
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Release|x64.Build.0 = Release|x64
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Release|x86.ActiveCfg = Release|Win32
- {9C28E205-C4CD-43D4-91BC-4852D7A588EC}.Release|x86.Build.0 = Release|Win32
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Debug|x64.ActiveCfg = Debug|x64
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Debug|x64.Build.0 = Debug|x64
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Debug|x86.ActiveCfg = Debug|Win32
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Debug|x86.Build.0 = Debug|Win32
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Release|x64.ActiveCfg = Release|x64
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Release|x64.Build.0 = Release|x64
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Release|x86.ActiveCfg = Release|Win32
- {FD2D3DBF-C82D-4333-9710-7DD2C51FA8E1}.Release|x86.Build.0 = Release|Win32
+ {B156A0E6-BC15-4987-A1E8-F6D6E69786BC}.Debug|x64.ActiveCfg = Debug|x64
+ {B156A0E6-BC15-4987-A1E8-F6D6E69786BC}.Debug|x64.Build.0 = Debug|x64
+ {B156A0E6-BC15-4987-A1E8-F6D6E69786BC}.Release|x64.ActiveCfg = Release|x64
+ {B156A0E6-BC15-4987-A1E8-F6D6E69786BC}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5B906255-44C8-427C-8F9B-99D222337F78}
+ EndGlobalSection
EndGlobal
diff --git a/driver_leap/Core/CDriverConfig.cpp b/driver_leap/Core/CDriverConfig.cpp
new file mode 100644
index 00000000..8ea7098f
--- /dev/null
+++ b/driver_leap/Core/CDriverConfig.cpp
@@ -0,0 +1,93 @@
+#include "stdafx.h"
+
+#include "Core/CDriverConfig.h"
+
+#include "Utils/Utils.h"
+
+extern char g_modulePath[];
+
+const std::vector g_settingNames
+{
+ "skeleton", "trackingLevel",
+ "handsReset", "interpolation", "velocity"
+};
+
+enum ConfigSetting : size_t
+{
+ CS_TrackingLevel = 0U,
+ CS_HandsReset,
+ CS_Interpolation,
+ CS_Velocity
+};
+
+const std::vector g_trackingLevels
+{
+ "partial", "full"
+};
+
+unsigned char CDriverConfig::ms_trackingLevel = CDriverConfig::TL_Partial;
+bool CDriverConfig::ms_handsReset = false;
+bool CDriverConfig::ms_interpolation = false;
+bool CDriverConfig::ms_useVelocity = false;
+
+void CDriverConfig::Load()
+{
+ std::string l_path(g_modulePath);
+ l_path.erase(l_path.begin() + l_path.rfind('\\'), l_path.end());
+ l_path.append("\\..\\..\\resources\\settings.xml");
+
+ pugi::xml_document *l_document = new pugi::xml_document();
+ if(l_document->load_file(l_path.c_str()))
+ {
+ const pugi::xml_node l_root = l_document->child("settings");
+ if(l_root)
+ {
+ for(pugi::xml_node l_node = l_root.child("setting"); l_node; l_node = l_node.next_sibling("setting"))
+ {
+ const pugi::xml_attribute l_attribName = l_node.attribute("name");
+ const pugi::xml_attribute l_attribValue = l_node.attribute("value");
+ if(l_attribName && l_attribValue)
+ {
+ switch(ReadEnumVector(l_attribName.as_string(), g_settingNames))
+ {
+ case ConfigSetting::CS_TrackingLevel:
+ {
+ const size_t l_tableIndex = ReadEnumVector(l_attribValue.as_string(), g_trackingLevels);
+ if(l_tableIndex != std::numeric_limits::max()) ms_trackingLevel = static_cast(l_tableIndex);
+ } break;
+ case ConfigSetting::CS_HandsReset:
+ ms_handsReset = l_attribValue.as_bool(false);
+ break;
+ case ConfigSetting::CS_Interpolation:
+ ms_interpolation = l_attribValue.as_bool(false);
+ break;
+ case ConfigSetting::CS_Velocity:
+ ms_useVelocity = l_attribValue.as_bool(false);
+ break;
+ }
+ }
+ }
+ }
+ }
+ delete l_document;
+}
+
+unsigned char CDriverConfig::GetTrackingLevel()
+{
+ return ms_trackingLevel;
+}
+
+bool CDriverConfig::IsHandsResetEnabled()
+{
+ return ms_handsReset;
+}
+
+bool CDriverConfig::IsInterpolationEnabled()
+{
+ return ms_interpolation;
+}
+
+bool CDriverConfig::IsVelocityUsed()
+{
+ return ms_useVelocity;
+}
diff --git a/driver_leap/Core/CDriverConfig.h b/driver_leap/Core/CDriverConfig.h
new file mode 100644
index 00000000..2e9e363e
--- /dev/null
+++ b/driver_leap/Core/CDriverConfig.h
@@ -0,0 +1,27 @@
+#pragma once
+
+class CDriverConfig final
+{
+ static unsigned char ms_trackingLevel;
+ static bool ms_handsReset;
+ static bool ms_interpolation;
+ static bool ms_useVelocity;
+
+ CDriverConfig() = delete;
+ ~CDriverConfig() = delete;
+ CDriverConfig(const CDriverConfig &that) = delete;
+ CDriverConfig& operator=(const CDriverConfig &that) = delete;
+public:
+ enum TrackingLevel : unsigned char
+ {
+ TL_Partial = 0U,
+ TL_Full
+ };
+
+ static void Load();
+
+ static unsigned char GetTrackingLevel();
+ static bool IsHandsResetEnabled();
+ static bool IsInterpolationEnabled();
+ static bool IsVelocityUsed();
+};
diff --git a/driver_leap/Core/CLeapPoller.cpp b/driver_leap/Core/CLeapPoller.cpp
new file mode 100644
index 00000000..d3433be7
--- /dev/null
+++ b/driver_leap/Core/CLeapPoller.cpp
@@ -0,0 +1,189 @@
+#include "stdafx.h"
+
+#include "Core/CLeapPoller.h"
+
+CLeapPoller::CLeapPoller()
+{
+ m_active = false;
+ m_thread = nullptr;
+ m_connection = nullptr;
+ m_clockSynchronizer = nullptr;
+ m_connected = false;
+ m_allocator.allocate = CLeapPoller::AllocateMemory;
+ m_allocator.deallocate = CLeapPoller::DeallocateMemory;
+ m_allocator.state = nullptr;
+ m_lastFrame = nullptr;
+ m_newFrame = nullptr;
+ m_device = nullptr;
+}
+
+CLeapPoller::~CLeapPoller()
+{
+ delete m_lastFrame;
+}
+
+bool CLeapPoller::Initialize()
+{
+ if(!m_active)
+ {
+ if(LeapCreateConnection(nullptr, &m_connection) == eLeapRS_Success)
+ {
+ if(LeapOpenConnection(m_connection) == eLeapRS_Success)
+ {
+ LeapSetAllocator(m_connection, &m_allocator);
+ LeapCreateClockRebaser(&m_clockSynchronizer);
+ m_lastFrame = new LEAP_TRACKING_EVENT();
+ m_newFrame = new LEAP_TRACKING_EVENT();
+ m_active = true;
+ m_thread = new std::thread(&CLeapPoller::ThreadUpdate, this);
+ }
+ else
+ {
+ LeapDestroyConnection(m_connection);
+ m_connection = nullptr;
+ }
+ }
+ else m_connection = nullptr;
+ }
+
+ return m_active;
+}
+
+void CLeapPoller::Terminate()
+{
+ if(m_active)
+ {
+ m_active = false;
+ m_thread->join();
+ m_thread = nullptr;
+
+ if(m_device) LeapCloseDevice(m_device);
+ LeapDestroyClockRebaser(m_clockSynchronizer);
+ LeapCloseConnection(m_connection);
+ LeapDestroyConnection(m_connection);
+
+ m_connection = nullptr;
+ m_clockSynchronizer = nullptr;
+ m_interpolatedFrameBuffer.clear();
+ m_device = nullptr;
+
+ delete m_lastFrame;
+ m_lastFrame = nullptr;
+ delete m_newFrame;
+ m_newFrame = nullptr;
+ }
+}
+
+bool CLeapPoller::IsConnected() const
+{
+ return m_connected;
+}
+
+const LEAP_TRACKING_EVENT* CLeapPoller::GetInterpolatedFrame()
+{
+ LEAP_TRACKING_EVENT *l_result = nullptr;
+ if(m_active)
+ {
+ if(!m_interpolatedFrameBuffer.empty()) l_result = reinterpret_cast(m_interpolatedFrameBuffer.data());
+ }
+ return l_result;
+}
+
+const LEAP_TRACKING_EVENT* CLeapPoller::GetFrame()
+{
+ LEAP_TRACKING_EVENT *l_result = nullptr;
+ if(m_active) l_result = m_lastFrame;
+ return l_result;
+}
+
+void CLeapPoller::SetTrackingMode(eLeapTrackingMode f_mode)
+{
+ if(m_active) LeapSetTrackingMode(m_connection, f_mode);
+}
+
+void CLeapPoller::SetPolicy(uint64_t f_set, uint64_t f_clear)
+{
+ if(m_active) LeapSetPolicyFlags(m_connection, f_set, f_clear);
+}
+
+void CLeapPoller::SetPaused(bool f_state)
+{
+ if(m_active) LeapSetPause(m_connection, f_state);
+}
+
+void CLeapPoller::Update()
+{
+ if(m_active)
+ {
+ LeapUpdateRebase(m_clockSynchronizer, std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), LeapGetNow());
+
+ if(m_frameLock.try_lock())
+ {
+ std::memcpy(m_lastFrame, m_newFrame, sizeof(LEAP_TRACKING_EVENT));
+ m_frameLock.unlock();
+ }
+
+ LEAP_CONNECTION_INFO l_info{ sizeof(LEAP_CONNECTION_INFO) };
+ if(LeapGetConnectionInfo(m_connection, &l_info) == eLeapRS_Success) m_connected = (l_info.status == eLeapConnectionStatus_Connected);
+ else m_connected = false;
+ }
+}
+
+void CLeapPoller::UpdateInterpolation()
+{
+ if(m_active)
+ {
+ int64_t l_targetFrameTime = 0;
+ uint64_t l_targetFrameSize = 0U;
+ LeapRebaseClock(m_clockSynchronizer, std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), &l_targetFrameTime);
+
+ if(LeapGetFrameSize(m_connection, l_targetFrameTime, &l_targetFrameSize) == eLeapRS_Success)
+ {
+ // Weird SDK requires weird solutions
+ m_interpolatedFrameBuffer.resize(static_cast(l_targetFrameSize));
+ LeapInterpolateFrame(m_connection, l_targetFrameTime, reinterpret_cast(m_interpolatedFrameBuffer.data()), l_targetFrameSize);
+ }
+ }
+}
+
+void CLeapPoller::ThreadUpdate()
+{
+ const std::chrono::milliseconds l_threadDelay(1U);
+ while(m_active)
+ {
+ // Poll events
+ LEAP_CONNECTION_MESSAGE l_message{ sizeof(LEAP_CONNECTION_MESSAGE) };
+ while(LeapPollConnection(m_connection, 0U, &l_message) == eLeapRS_Success)
+ {
+ if(l_message.type == eLeapEventType_None) break;
+ switch(l_message.type)
+ {
+ case eLeapEventType_Device:
+ {
+ if(!m_device)
+ {
+ if(LeapOpenDevice(l_message.device_event->device, &m_device) != eLeapRS_Success) m_device = nullptr;
+ }
+ } break;
+ case eLeapEventType_Tracking:
+ {
+ m_frameLock.lock();
+ std::memcpy(m_newFrame, l_message.tracking_event, sizeof(LEAP_TRACKING_EVENT));
+ m_frameLock.unlock();
+ } break;
+ }
+ }
+
+ std::this_thread::sleep_for(l_threadDelay);
+ }
+}
+
+void* CLeapPoller::AllocateMemory(uint32_t size, eLeapAllocatorType typeHint, void *state)
+{
+ return new uint8_t[size];
+}
+
+void CLeapPoller::DeallocateMemory(void *ptr, void *state)
+{
+ delete[]reinterpret_cast(ptr);
+}
diff --git a/driver_leap/Core/CLeapPoller.h b/driver_leap/Core/CLeapPoller.h
new file mode 100644
index 00000000..a0cb3666
--- /dev/null
+++ b/driver_leap/Core/CLeapPoller.h
@@ -0,0 +1,41 @@
+#pragma once
+
+class CLeapPoller
+{
+ std::atomic m_active;
+ std::mutex m_frameLock;
+ std::thread *m_thread;
+
+ LEAP_CONNECTION m_connection;
+ LEAP_CLOCK_REBASER m_clockSynchronizer;
+ LEAP_ALLOCATOR m_allocator;
+ LEAP_TRACKING_EVENT *m_lastFrame;
+ LEAP_TRACKING_EVENT *m_newFrame;
+ std::vector m_interpolatedFrameBuffer;
+ LEAP_DEVICE m_device;
+ bool m_connected;
+
+ void ThreadUpdate();
+
+ static void* AllocateMemory(uint32_t size, eLeapAllocatorType typeHint, void *state);
+ static void DeallocateMemory(void *ptr, void *state);
+public:
+ CLeapPoller();
+ ~CLeapPoller();
+
+ bool Initialize();
+ void Terminate();
+
+ bool IsConnected() const;
+ const LEAP_TRACKING_EVENT* GetInterpolatedFrame();
+ const LEAP_TRACKING_EVENT* GetFrame();
+
+ void SetPolicy(uint64_t f_set, uint64_t f_clear = 0U);
+ void SetTrackingMode(eLeapTrackingMode f_mode);
+ void SetPaused(bool f_state);
+
+ void Update();
+ void UpdateInterpolation();
+};
+
+
diff --git a/driver_leap/Core/CServerDriver.cpp b/driver_leap/Core/CServerDriver.cpp
new file mode 100644
index 00000000..9755eee8
--- /dev/null
+++ b/driver_leap/Core/CServerDriver.cpp
@@ -0,0 +1,328 @@
+#include "stdafx.h"
+
+#include "Core/CServerDriver.h"
+#include "Core/CLeapPoller.h"
+#include "Devices/CLeapController/CLeapControllerIndex.h"
+#include "Devices/CLeapStation.h"
+
+#include "Core/CDriverConfig.h"
+#include "Utils/Utils.h"
+
+extern char g_modulePath[];
+
+const std::vector g_debugRequests
+{
+ "input"
+};
+enum DebugRequest : size_t
+{
+ DR_Input = 0U
+};
+
+const std::vector g_inputHands
+{
+ "left", "right"
+};
+enum InputHands : size_t
+{
+ IH_LeftHand = 0U,
+ IH_RightHand
+};
+
+const std::vector g_buttonTypes
+{
+ "button", "axis"
+};
+enum ButtonTypes : size_t
+{
+ BT_Button = 0U,
+ BT_Axis
+};
+
+const std::vector g_buttonNames
+{
+ "a", "b", "system"
+};
+enum ButtonNames : size_t
+{
+ BN_A = 0U,
+ BN_B,
+ BN_System
+};
+
+const std::vector g_axisNames
+{
+ "thumbstick", "touchpad"
+};
+enum AxisNames : size_t
+{
+ AN_Thumbstick = 0U,
+ AN_Touchpad
+};
+
+const std::vector g_buttonStates
+{
+ "none", "touched", "clicked"
+};
+enum ButtonStates : size_t
+{
+ BS_None = 0U,
+ BS_Touched,
+ BS_Clicked
+};
+
+const char* const CServerDriver::ms_interfaces[]
+{
+ vr::ITrackedDeviceServerDriver_Version,
+ vr::IServerTrackedDeviceProvider_Version,
+ nullptr
+};
+
+CServerDriver::CServerDriver()
+{
+ m_leapPoller = nullptr;
+ m_connectionState = false;
+ for(size_t i = 0U; i < LCH_Count; i++) m_controllers[i] = nullptr;
+ m_leapStation = nullptr;
+}
+
+CServerDriver::~CServerDriver()
+{
+}
+
+// vr::IServerTrackedDeviceProvider
+vr::EVRInitError CServerDriver::Init(vr::IVRDriverContext *pDriverContext)
+{
+ VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext);
+ CDriverConfig::Load();
+
+ // Relay device for events from leap_control
+ m_leapStation = new CLeapStation(this);
+ vr::VRServerDriverHost()->TrackedDeviceAdded(m_leapStation->GetSerialNumber().c_str(), vr::TrackedDeviceClass_TrackingReference, m_leapStation);
+
+ m_controllers[LCH_Left] = new CLeapControllerIndex(CLeapController::CH_Left);
+ m_controllers[LCH_Right] = new CLeapControllerIndex(CLeapController::CH_Right);
+
+ for(size_t i = 0U; i < LCH_Count; i++)
+ {
+ vr::VRServerDriverHost()->TrackedDeviceAdded(m_controllers[i]->GetSerialNumber().c_str(), vr::TrackedDeviceClass_Controller, m_controllers[i]);
+ }
+
+ m_leapPoller = new CLeapPoller();
+ if(m_leapPoller->Initialize())
+ {
+ m_leapPoller->SetPolicy(eLeapPolicyFlag_AllowPauseResume);
+ m_leapPoller->SetTrackingMode(_eLeapTrackingMode::eLeapTrackingMode_HMD);
+ m_leapPoller->SetPolicy(eLeapPolicyFlag::eLeapPolicyFlag_OptimizeHMD, eLeapPolicyFlag::eLeapPolicyFlag_OptimizeScreenTop);
+ }
+
+ // Start leap_control
+ std::string l_path(g_modulePath);
+ l_path.erase(l_path.begin() + l_path.rfind('\\'), l_path.end());
+
+ std::string l_appPath(l_path);
+ l_appPath.append("\\leap_control.exe");
+
+ STARTUPINFOA l_infoProcess = { 0 };
+ PROCESS_INFORMATION l_monitorInfo = { 0 };
+ l_infoProcess.cb = sizeof(STARTUPINFOA);
+ CreateProcessA(l_appPath.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, l_path.c_str(), &l_infoProcess, &l_monitorInfo);
+
+ return vr::VRInitError_None;
+}
+
+void CServerDriver::Cleanup()
+{
+ for(size_t i = 0U; i < LCH_Count; i++)
+ {
+ delete m_controllers[i];
+ m_controllers[i] = nullptr;
+ }
+ delete m_leapStation;
+ m_leapStation = nullptr;
+
+ m_leapPoller->Terminate();
+ delete m_leapPoller;
+ m_leapPoller = nullptr;
+
+ VR_CLEANUP_SERVER_DRIVER_CONTEXT();
+}
+
+const char* const* CServerDriver::GetInterfaceVersions()
+{
+ return ms_interfaces;
+}
+
+void CServerDriver::RunFrame()
+{
+ CLeapController::UpdateHMDCoordinates();
+ m_leapPoller->Update();
+
+ if(m_connectionState != m_leapPoller->IsConnected())
+ {
+ m_connectionState = m_leapPoller->IsConnected();
+ m_leapStation->SetTrackingState(m_connectionState ? CLeapStation::TS_Connected : CLeapStation::TS_Search);
+ for(size_t i = 0U; i < LCH_Count; i++)
+ {
+ m_controllers[i]->SetEnabled(m_connectionState);
+ }
+ }
+
+ LEAP_HAND *l_hands[LCH_Count] = { nullptr };
+ if(m_connectionState)
+ {
+ if(CDriverConfig::IsInterpolationEnabled()) m_leapPoller->UpdateInterpolation();
+
+ const LEAP_TRACKING_EVENT *l_frame = (CDriverConfig::IsInterpolationEnabled() ? m_leapPoller->GetInterpolatedFrame() : m_leapPoller->GetFrame());
+ if(l_frame)
+ {
+ for(size_t i = 0U; i < l_frame->nHands; i++)
+ {
+ if(!l_hands[l_frame->pHands[i].type]) l_hands[l_frame->pHands[i].type] = &l_frame->pHands[i];
+ }
+ }
+ }
+
+ // Update devices
+ for(size_t i = 0U; i < LCH_Count; i++)
+ {
+ m_controllers[i]->RunFrame(l_hands[i], l_hands[(i + 1) % LCH_Count]);
+ }
+ m_leapStation->RunFrame();
+}
+
+bool CServerDriver::ShouldBlockStandbyMode()
+{
+ return false;
+}
+
+void CServerDriver::EnterStandby()
+{
+}
+
+void CServerDriver::LeaveStandby()
+{
+}
+
+// CServerDriver
+void CServerDriver::ProcessExternalMessage(const char *f_message)
+{
+ std::stringstream l_stream(f_message);
+ std::string l_event;
+
+ // Scary stuff
+ l_stream >> l_event;
+ if(!l_stream.fail() && !l_event.empty())
+ {
+ switch(ReadEnumVector(l_event, g_debugRequests))
+ {
+ case DR_Input:
+ {
+ std::string l_inputHand;
+ l_stream >> l_inputHand;
+ if(!l_stream.fail() && !l_inputHand.empty())
+ {
+ size_t l_inputHandIndex = ReadEnumVector(l_inputHand, g_inputHands);
+ if(l_inputHandIndex != std::numeric_limits::max())
+ {
+ std::string l_buttonType;
+ l_stream >> l_buttonType;
+ if(!l_stream.fail() && !l_buttonType.empty())
+ {
+ size_t l_buttonTypeIndex = ReadEnumVector(l_buttonType, g_buttonTypes);
+ if(l_buttonTypeIndex != std::numeric_limits::max())
+ {
+ switch(l_buttonTypeIndex)
+ {
+ case ButtonTypes::BT_Button:
+ {
+ std::string l_buttonName;
+ l_stream >> l_buttonName;
+ if(!l_stream.fail() && !l_buttonName.empty())
+ {
+ size_t l_buttonNameIndex = ReadEnumVector(l_buttonName, g_buttonNames);
+ if(l_buttonNameIndex != std::numeric_limits::max())
+ {
+ std::string l_buttonState;
+ l_stream >> l_buttonState;
+ if(!l_stream.fail() && !l_buttonState.empty())
+ {
+ size_t l_buttonStateIndex = ReadEnumVector(l_buttonState, g_buttonStates);
+ if(l_buttonStateIndex != std::numeric_limits::max())
+ {
+ switch(l_buttonNameIndex)
+ {
+ case ButtonNames::BN_A:
+ {
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_ATouch, l_buttonStateIndex >= ButtonStates::BS_Touched);
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_AClick, l_buttonStateIndex >= ButtonStates::BS_Clicked);
+ } break;
+ case ButtonNames::BN_B:
+ {
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_BTouch, l_buttonStateIndex >= ButtonStates::BS_Touched);
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_BClick, l_buttonStateIndex >= ButtonStates::BS_Clicked);
+ } break;
+ case ButtonNames::BN_System:
+ {
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_SystemTouch, l_buttonStateIndex >= ButtonStates::BS_Touched);
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_SystemClick, l_buttonStateIndex >= ButtonStates::BS_Clicked);
+ } break;
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ case ButtonTypes::BT_Axis:
+ {
+ std::string l_axisName;
+ l_stream >> l_axisName;
+ if(!l_stream.fail() && !l_axisName.empty())
+ {
+ size_t l_axisNameIndex = ReadEnumVector(l_axisName, g_axisNames);
+ if(l_axisNameIndex != std::numeric_limits::max())
+ {
+ std::string l_buttonState;
+ l_stream >> l_buttonState;
+ if(!l_stream.fail() && !l_buttonState.empty())
+ {
+ size_t l_buttonStateIndex = ReadEnumVector(l_buttonState, g_buttonStates);
+ if(l_buttonStateIndex != std::numeric_limits::max())
+ {
+ glm::vec2 l_axisValues(0.f);
+ l_stream >> l_axisValues.x >> l_axisValues.y;
+ if(!l_stream.fail())
+ {
+ switch(l_axisNameIndex)
+ {
+ case AxisNames::AN_Thumbstick:
+ {
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_ThumbstickTouch, l_buttonStateIndex >= ButtonStates::BS_Touched);
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_ThumbstickClick, l_buttonStateIndex >= ButtonStates::BS_Clicked);
+ m_controllers[l_inputHandIndex]->SetButtonValue(CLeapControllerIndex::IB_ThumbstickX, (l_buttonStateIndex >= ButtonStates::BS_Touched) ? l_axisValues.x : 0.f);
+ m_controllers[l_inputHandIndex]->SetButtonValue(CLeapControllerIndex::IB_ThumbstickY, (l_buttonStateIndex >= ButtonStates::BS_Touched) ? l_axisValues.y : 0.f);
+ } break;
+ case AxisNames::AN_Touchpad:
+ {
+ m_controllers[l_inputHandIndex]->SetButtonState(CLeapControllerIndex::IB_TrackpadTouch, l_buttonStateIndex >= ButtonStates::BS_Touched);
+ m_controllers[l_inputHandIndex]->SetButtonValue(CLeapControllerIndex::IB_TrackpadForce, (l_buttonStateIndex == ButtonStates::BS_Clicked) ? 1.f : 0.f);
+ m_controllers[l_inputHandIndex]->SetButtonValue(CLeapControllerIndex::IB_TrackpadX, (l_buttonStateIndex >= ButtonStates::BS_Touched) ? l_axisValues.x : 0.f);
+ m_controllers[l_inputHandIndex]->SetButtonValue(CLeapControllerIndex::IB_TrackpadY, (l_buttonStateIndex >= ButtonStates::BS_Touched) ? l_axisValues.y : 0.f);
+ } break;
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+ }
+ }
+ }
+ }
+ }
+ } break;
+ }
+ }
+}
diff --git a/driver_leap/Core/CServerDriver.h b/driver_leap/Core/CServerDriver.h
new file mode 100644
index 00000000..c8babcab
--- /dev/null
+++ b/driver_leap/Core/CServerDriver.h
@@ -0,0 +1,42 @@
+#pragma once
+
+class CLeapPoller;
+class CLeapControllerIndex;
+class CLeapStation;
+
+class CServerDriver final : public vr::IServerTrackedDeviceProvider
+{
+ enum LeapControllerHand : size_t
+ {
+ LCH_Left = 0U,
+ LCH_Right = 1U,
+
+ LCH_Count
+ };
+
+ static const char* const ms_interfaces[];
+
+ bool m_connectionState;
+ CLeapPoller *m_leapPoller;
+ CLeapControllerIndex *m_controllers[LCH_Count];
+ CLeapStation *m_leapStation;
+
+ CServerDriver(const CServerDriver &that) = delete;
+ CServerDriver& operator=(const CServerDriver &that) = delete;
+
+ void TryToPause();
+
+ // vr::IServerTrackedDeviceProvider
+ vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext);
+ void Cleanup();
+ const char* const* GetInterfaceVersions();
+ void RunFrame();
+ bool ShouldBlockStandbyMode();
+ void EnterStandby();
+ void LeaveStandby();
+public:
+ CServerDriver();
+ ~CServerDriver();
+
+ void ProcessExternalMessage(const char *f_message);
+};
diff --git a/driver_leap/Devices/CLeapController/CControllerButton.cpp b/driver_leap/Devices/CLeapController/CControllerButton.cpp
new file mode 100644
index 00000000..01b04311
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CControllerButton.cpp
@@ -0,0 +1,80 @@
+#include "stdafx.h"
+
+#include "Devices/CLeapController/CControllerButton.h"
+
+CControllerButton::CControllerButton()
+{
+ m_handle = vr::k_ulInvalidInputComponentHandle;
+ m_inputType = IT_Boolean;
+ m_state = false;
+ m_value = 0.f;
+ m_updated = false;
+}
+
+CControllerButton::~CControllerButton()
+{
+}
+
+vr::VRInputComponentHandle_t CControllerButton::GetHandle() const
+{
+ return m_handle;
+}
+
+vr::VRInputComponentHandle_t& CControllerButton::GetHandleRef()
+{
+ return m_handle;
+}
+
+void CControllerButton::SetInputType(unsigned char f_type)
+{
+ m_inputType = f_type;
+}
+
+unsigned char CControllerButton::GetInputType() const
+{
+ return m_inputType;
+}
+
+void CControllerButton::SetState(bool f_state)
+{
+ if(m_inputType == IT_Boolean)
+ {
+ if(m_state != f_state)
+ {
+ m_state = f_state;
+ m_updated = true;
+ }
+ }
+}
+
+bool CControllerButton::GetState() const
+{
+ return m_state;
+}
+
+void CControllerButton::SetValue(float f_value)
+{
+ if(m_inputType == IT_Float)
+ {
+ if(m_value != f_value)
+ {
+ m_value = f_value;
+ m_updated = true;
+ }
+ }
+}
+
+float CControllerButton::GetValue() const
+{
+ return m_value;
+}
+
+bool CControllerButton::IsUpdated() const
+{
+ return m_updated;
+}
+
+void CControllerButton::ResetUpdate()
+{
+ m_updated = false;
+}
diff --git a/driver_leap/Devices/CLeapController/CControllerButton.h b/driver_leap/Devices/CLeapController/CControllerButton.h
new file mode 100644
index 00000000..a7eb7332
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CControllerButton.h
@@ -0,0 +1,38 @@
+#pragma once
+
+class CControllerButton final
+{
+ vr::VRInputComponentHandle_t m_handle;
+ unsigned char m_inputType;
+ float m_value;
+ bool m_state;
+ bool m_updated;
+
+ CControllerButton(const CControllerButton &that) = delete;
+ CControllerButton& operator=(const CControllerButton &that) = delete;
+public:
+ enum InputType : unsigned char
+ {
+ IT_None = 0U,
+ IT_Boolean,
+ IT_Float
+ };
+
+ CControllerButton();
+ ~CControllerButton();
+
+ vr::VRInputComponentHandle_t GetHandle() const;
+ vr::VRInputComponentHandle_t& GetHandleRef();
+
+ void SetInputType(unsigned char f_type);
+ unsigned char GetInputType() const;
+
+ void SetState(bool f_state);
+ bool GetState() const;
+
+ void SetValue(float f_value);
+ float GetValue() const;
+
+ bool IsUpdated() const;
+ void ResetUpdate();
+};
diff --git a/driver_leap/Devices/CLeapController/CLeapController.cpp b/driver_leap/Devices/CLeapController/CLeapController.cpp
new file mode 100644
index 00000000..951f5c1b
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CLeapController.cpp
@@ -0,0 +1,235 @@
+#include "stdafx.h"
+
+#include "Devices/CLeapController/CLeapController.h"
+#include "Devices/CLeapController/CControllerButton.h"
+
+#include "Core/CDriverConfig.h"
+#include "Utils/Utils.h"
+
+const glm::quat g_reverseRotation(0.f, 0.f, 0.70106769f, -0.70106769f);
+const glm::quat g_rotateHalfPiZ(0.70106769f, 0.f, 0.f, 0.70106769f);
+const glm::quat g_rotateHalfPiZN(0.70106769f, 0.f, 0.f, -0.70106769f);
+const vr::HmdQuaternion_t g_vrZeroRotation = { 1.0, .0, .0, .0 };
+
+double CLeapController::ms_headPosition[] = { .0, .0, .0 };
+vr::HmdQuaternion_t CLeapController::ms_headRotation = { 1.0, .0, .0, .0 };
+
+CLeapController::CLeapController()
+{
+ m_propertyContainer = vr::k_ulInvalidPropertyContainer;
+ m_trackedDevice = vr::k_unTrackedDeviceIndexInvalid;
+ m_haptic = vr::k_ulInvalidPropertyContainer;
+
+ m_pose = { 0 };
+ m_pose.deviceIsConnected = false;
+ for(size_t i = 0U; i < 3U; i++)
+ {
+ m_pose.vecAcceleration[i] = .0;
+ m_pose.vecAngularAcceleration[i] = .0;
+ m_pose.vecAngularVelocity[i] = .0;
+ m_pose.vecDriverFromHeadTranslation[i] = .0;
+ }
+ m_pose.poseTimeOffset = .0;
+ m_pose.qDriverFromHeadRotation = g_vrZeroRotation;
+ m_pose.qRotation = g_vrZeroRotation;
+ m_pose.qWorldFromDriverRotation = g_vrZeroRotation;
+ m_pose.result = vr::TrackingResult_Uninitialized;
+ m_pose.shouldApplyHeadModel = false;
+ m_pose.willDriftInYaw = false;
+
+ m_hand = CH_Left;
+ m_type = CT_Invalid;
+}
+
+CLeapController::~CLeapController()
+{
+ for(auto l_button : m_buttons) delete l_button;
+}
+
+// vr::ITrackedDeviceServerDriver
+vr::EVRInitError CLeapController::Activate(uint32_t unObjectId)
+{
+ vr::EVRInitError l_resultError = vr::VRInitError_Driver_Failed;
+
+ if(m_trackedDevice == vr::k_unTrackedDeviceIndexInvalid)
+ {
+ m_trackedDevice = unObjectId;
+ m_propertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_trackedDevice);
+
+ ActivateInternal();
+
+ l_resultError = vr::VRInitError_None;
+ }
+
+ return l_resultError;
+}
+
+void CLeapController::Deactivate()
+{
+ ResetControls();
+ m_trackedDevice = vr::k_unTrackedDeviceIndexInvalid;
+}
+
+void CLeapController::EnterStandby()
+{
+}
+
+void* CLeapController::GetComponent(const char* pchComponentNameAndVersion)
+{
+ void *l_result = nullptr;
+ if(!strcmp(pchComponentNameAndVersion, vr::ITrackedDeviceServerDriver_Version)) l_result = dynamic_cast(this);
+ return l_result;
+}
+
+void CLeapController::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize)
+{
+}
+
+vr::DriverPose_t CLeapController::GetPose()
+{
+ return m_pose;
+}
+
+// CLeapController
+bool CLeapController::IsEnabled() const
+{
+ return m_pose.deviceIsConnected;
+}
+
+void CLeapController::SetEnabled(bool f_state)
+{
+ m_pose.deviceIsConnected = f_state;
+ if(m_trackedDevice != vr::k_unTrackedDeviceIndexInvalid) vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_trackedDevice, m_pose, sizeof(vr::DriverPose_t));
+}
+
+const std::string& CLeapController::GetSerialNumber() const
+{
+ return m_serialNumber;
+}
+
+void CLeapController::ResetControls()
+{
+ for(auto l_button : m_buttons)
+ {
+ l_button->SetValue(0.f);
+ l_button->SetState(false);
+ }
+}
+
+void CLeapController::RunFrame(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand)
+{
+ if(m_trackedDevice != vr::k_unTrackedDeviceIndexInvalid)
+ {
+ if(m_pose.deviceIsConnected)
+ {
+ UpdateTransformation(f_hand);
+ vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_trackedDevice, m_pose, sizeof(vr::DriverPose_t));
+
+ UpdateGestures(f_hand, f_oppHand);
+ UpdateInput();
+ }
+ else vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_trackedDevice, m_pose, sizeof(vr::DriverPose_t));
+ }
+}
+
+void CLeapController::UpdateInput()
+{
+ for(auto l_button : m_buttons)
+ {
+ if(l_button->IsUpdated())
+ {
+ switch(l_button->GetInputType())
+ {
+ case CControllerButton::IT_Boolean:
+ vr::VRDriverInput()->UpdateBooleanComponent(l_button->GetHandle(), l_button->GetState(), .0);
+ break;
+ case CControllerButton::IT_Float:
+ vr::VRDriverInput()->UpdateScalarComponent(l_button->GetHandle(), l_button->GetValue(), .0);
+ break;
+ }
+ l_button->ResetUpdate();
+ }
+ }
+ UpdateInputInternal();
+}
+
+void CLeapController::UpdateTransformation(const LEAP_HAND *f_hand)
+{
+ m_pose.poseIsValid = (f_hand != nullptr);
+
+ if(f_hand)
+ {
+ std::memcpy(m_pose.vecWorldFromDriverTranslation, ms_headPosition, sizeof(double) * 3U);
+
+ const LEAP_VECTOR l_palmPosition = f_hand->palm.position;
+ const LEAP_QUATERNION l_palmOrientation = f_hand->palm.orientation;
+
+ std::memcpy(&m_pose.qWorldFromDriverRotation, &ms_headRotation, sizeof(vr::HmdQuaternion_t));
+
+ m_pose.vecPosition[0] = -0.001f*l_palmPosition.x;
+ m_pose.vecPosition[1] = -0.001f*l_palmPosition.z;
+ m_pose.vecPosition[2] = -0.001f*l_palmPosition.y;
+
+ if(CDriverConfig::IsVelocityUsed())
+ {
+ const LEAP_VECTOR l_palmVelocity = f_hand->palm.velocity;
+ glm::vec3 l_resultVelocity(-0.001f*l_palmVelocity.x, -0.001f*l_palmVelocity.z, -0.001f*l_palmVelocity.y);
+ l_resultVelocity = glm::quat(ms_headRotation.w, ms_headRotation.x, ms_headRotation.y, ms_headRotation.z) * l_resultVelocity;
+ m_pose.vecVelocity[0] = l_resultVelocity.x;
+ m_pose.vecVelocity[1] = l_resultVelocity.y;
+ m_pose.vecVelocity[2] = l_resultVelocity.z;
+ }
+
+ glm::quat l_rotation(l_palmOrientation.w, l_palmOrientation.x, l_palmOrientation.y, l_palmOrientation.z);
+ l_rotation = g_reverseRotation * l_rotation;
+ l_rotation *= ((m_hand == CH_Left) ? g_rotateHalfPiZN : g_rotateHalfPiZ);
+ l_rotation = glm::normalize(l_rotation);
+
+ m_pose.qRotation.x = l_rotation.x;
+ m_pose.qRotation.y = l_rotation.y;
+ m_pose.qRotation.z = l_rotation.z;
+ m_pose.qRotation.w = l_rotation.w;
+ m_pose.result = vr::TrackingResult_Running_OK;
+ }
+ else
+ {
+ for(size_t i = 0U; i < 3U; i++) m_pose.vecVelocity[i] = .0;
+ if(CDriverConfig::IsHandsResetEnabled()) m_pose.result = vr::TrackingResult_Running_OutOfRange;
+ else
+ {
+ m_pose.result = vr::TrackingResult_Running_OK;
+ m_pose.poseIsValid = true;
+ }
+ }
+}
+
+void CLeapController::ActivateInternal()
+{
+}
+
+void CLeapController::UpdateGestures(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand)
+{
+}
+
+void CLeapController::UpdateInputInternal()
+{
+}
+
+void CLeapController::UpdateHMDCoordinates()
+{
+ vr::TrackedDevicePose_t l_hmdPose;
+ vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0.f, &l_hmdPose, 1U); // HMD has device ID 0
+ if(l_hmdPose.bPoseIsValid)
+ {
+ glm::mat4 l_rotMat(1.f);
+ ConvertMatrix(l_hmdPose.mDeviceToAbsoluteTracking, l_rotMat);
+
+ const glm::quat l_headRot = glm::quat_cast(l_rotMat);
+ ms_headRotation.x = l_headRot.x;
+ ms_headRotation.y = l_headRot.y;
+ ms_headRotation.z = l_headRot.z;
+ ms_headRotation.w = l_headRot.w;
+
+ for(size_t i = 0U; i < 3U; i++) ms_headPosition[i] = l_hmdPose.mDeviceToAbsoluteTracking.m[i][3];
+ }
+}
diff --git a/driver_leap/Devices/CLeapController/CLeapController.h b/driver_leap/Devices/CLeapController/CLeapController.h
new file mode 100644
index 00000000..5f4f393b
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CLeapController.h
@@ -0,0 +1,68 @@
+#pragma once
+
+class CControllerButton;
+
+class CLeapController : public vr::ITrackedDeviceServerDriver
+{
+ static double ms_headPosition[3U];
+ static vr::HmdQuaternion_t ms_headRotation;
+
+ vr::DriverPose_t m_pose;
+
+ CLeapController(const CLeapController &that) = delete;
+ CLeapController& operator=(const CLeapController &that) = delete;
+
+ void ResetControls();
+ void UpdateInput();
+ void UpdateTransformation(const LEAP_HAND *f_hand);
+
+ // vr::ITrackedDeviceServerDriver
+ 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();
+public:
+ enum ControllerHand : unsigned char
+ {
+ CH_Left = 0U,
+ CH_Right,
+
+ CH_Count
+ };
+ enum ControllerType : unsigned char
+ {
+ CT_ViveWand = 0U,
+ CT_IndexKnuckle,
+ CT_OculusTouch,
+
+ CT_Count,
+ CT_Invalid = 0xFFU
+ };
+
+ CLeapController();
+ virtual ~CLeapController();
+
+ const std::string& GetSerialNumber() const;
+
+ bool IsEnabled() const;
+ void SetEnabled(bool f_state);
+
+ void RunFrame(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand);
+
+ static void UpdateHMDCoordinates();
+protected:
+ uint32_t m_trackedDevice;
+ vr::PropertyContainerHandle_t m_propertyContainer;
+ vr::VRInputComponentHandle_t m_haptic;
+
+ std::string m_serialNumber;
+ unsigned char m_hand;
+ unsigned char m_type;
+ std::vector m_buttons;
+
+ virtual void ActivateInternal();
+ virtual void UpdateGestures(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand);
+ virtual void UpdateInputInternal();
+};
diff --git a/driver_leap/Devices/CLeapController/CLeapControllerIndex.cpp b/driver_leap/Devices/CLeapController/CLeapControllerIndex.cpp
new file mode 100644
index 00000000..d30feb2c
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CLeapControllerIndex.cpp
@@ -0,0 +1,358 @@
+#include "stdafx.h"
+
+#include "Devices/CLeapController/CLeapControllerIndex.h"
+
+#include "Devices/CLeapController/CControllerButton.h"
+#include "Core/CDriverConfig.h"
+#include "Utils/CGestureMatcher.h"
+#include "Utils/Utils.h"
+
+extern const glm::mat4 g_identityMatrix;
+extern const vr::VRBoneTransform_t g_openHandGesture[];
+extern const glm::vec4 g_zeroPoint;
+
+enum HandFinger : size_t
+{
+ HF_Thumb = 0U,
+ HF_Index,
+ HF_Middle,
+ HF_Ring,
+ HF_Pinky,
+
+ HF_Count
+};
+
+CLeapControllerIndex::CLeapControllerIndex(unsigned char f_hand)
+{
+ m_hand = (f_hand % CH_Count);
+ m_type = CT_IndexKnuckle;
+ m_serialNumber.assign((m_hand == CH_Left) ? "LHR-E217CD00" : "LHR-E217CD01");
+
+ for(size_t i = 0U; i < HSB_Count; i++) m_boneTransform[i] = g_openHandGesture[i];
+ m_skeletonHandle = vr::k_ulInvalidInputComponentHandle;
+
+ if(m_hand == CH_Right)
+ {
+ // Transformation inversion along 0YZ plane
+ for(size_t i = 1U; i < HSB_Count; i++)
+ {
+ m_boneTransform[i].position.v[0] *= -1.f;
+
+ switch(i)
+ {
+ case HSB_Wrist:
+ {
+ m_boneTransform[i].orientation.y *= -1.f;
+ m_boneTransform[i].orientation.z *= -1.f;
+ } break;
+
+ case HSB_Thumb0:
+ case HSB_IndexFinger0:
+ case HSB_MiddleFinger0:
+ case HSB_RingFinger0:
+ case HSB_PinkyFinger0:
+ {
+ m_boneTransform[i].orientation.z *= -1.f;
+ std::swap(m_boneTransform[i].orientation.x, m_boneTransform[i].orientation.w);
+ m_boneTransform[i].orientation.w *= -1.f;
+ std::swap(m_boneTransform[i].orientation.y, m_boneTransform[i].orientation.z);
+ } break;
+ }
+ }
+ }
+
+}
+
+CLeapControllerIndex::~CLeapControllerIndex()
+{
+}
+
+void CLeapControllerIndex::ChangeBoneOrientation(glm::quat &f_rot)
+{
+ std::swap(f_rot.x, f_rot.z);
+ f_rot.z *= -1.f;
+ if(m_hand == CH_Left)
+ {
+ f_rot.x *= -1.f;
+ f_rot.y *= -1.f;
+ }
+}
+
+void CLeapControllerIndex::ChangeAuxTransformation(glm::vec3 &f_pos, glm::quat &f_rot)
+{
+ f_pos.y *= -1.f;
+ f_pos.z *= -1.f;
+
+ std::swap(f_rot.x, f_rot.w);
+ f_rot.w *= -1.f;
+ std::swap(f_rot.y, f_rot.z);
+ f_rot.y *= -1.f;
+}
+
+size_t CLeapControllerIndex::GetFingerBoneIndex(size_t f_id)
+{
+ size_t l_result = 0U;
+ switch(f_id)
+ {
+ case HF_Thumb:
+ l_result = HSB_Thumb0;
+ break;
+ case HF_Index:
+ l_result = HSB_IndexFinger0;
+ break;
+ case HF_Middle:
+ l_result = HSB_MiddleFinger0;
+ break;
+ case HF_Ring:
+ l_result = HSB_RingFinger0;
+ break;
+ case HF_Pinky:
+ l_result = HSB_PinkyFinger0;
+ break;
+ }
+ return l_result;
+}
+
+void CLeapControllerIndex::ActivateInternal()
+{
+ // Properties
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_TrackingSystemName_String, "lighthouse");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_SerialNumber_String, m_serialNumber.c_str());
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_WillDriftInYaw_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_DeviceIsWireless_Bool, true);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_DeviceIsCharging_Bool, false);
+ vr::VRProperties()->SetFloatProperty(m_propertyContainer, 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(m_propertyContainer, vr::Prop_StatusDisplayTransform_Matrix34, &l_matrix, sizeof(vr::HmdMatrix34_t), vr::k_unHmdMatrix34PropertyTag);
+
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_Firmware_UpdateAvailable_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_Firmware_ManualUpdate_Bool, false);
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_Firmware_ManualUpdateURL_String, "https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware");
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_DeviceProvidesBatteryStatus_Bool, true);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_DeviceCanPowerOff_Bool, true);
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_Firmware_ForceUpdateRequired_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_Identifiable_Bool, true);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_Firmware_RemindUpdate_Bool, false);
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_Axis0Type_Int32, vr::k_eControllerAxis_TrackPad);
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_Axis1Type_Int32, vr::k_eControllerAxis_Trigger);
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_ControllerRoleHint_Int32, (m_hand == CH_Left) ? vr::TrackedControllerRole_LeftHand : vr::TrackedControllerRole_RightHand);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_HasDisplayComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_HasCameraComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_HasDriverDirectModeComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyContainer, vr::Prop_HasVirtualDisplayComponent_Bool, false);
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_ControllerHandSelectionPriority_Int32, 0);
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_ModelNumber_String, (m_hand == CH_Left) ? "Knuckles Left" : "Knuckles Right");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_RenderModelName_String, (m_hand == CH_Left) ? "{indexcontroller}valve_controller_knu_1_0_left" : "{indexcontroller}valve_controller_knu_1_0_right");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_ManufacturerName_String, "Valve");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, 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(m_propertyContainer, vr::Prop_HardwareRevision_String, "product 17 rev 14.1.9 lot 2019/4/20 0");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_ConnectedWirelessDongle_String, "C2F75F5986-DIY"); // Changed
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_HardwareRevision_Uint64, 286130441U);
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_FirmwareVersion_Uint64, 1562916277U);
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_FPGAVersion_Uint64, 538U);
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_VRCVersion_Uint64, 1562916277U);
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_RadioVersion_Uint64, 1562882729U);
+ vr::VRProperties()->SetUint64Property(m_propertyContainer, vr::Prop_DongleVersion_Uint64, 1558748372U);
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_Firmware_ProgrammingTarget_String, (m_hand == CH_Left) ? "LHR-E217CD00" : "LHR-E217CD01"); // Changed
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_ResourceRoot_String, "indexcontroller");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_RegisteredDeviceType_String, (m_hand == CH_Left) ? "valve/index_controllerLHR-E217CD00" : "valve/index_controllerLHR-E217CD01"); // Changed
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_InputProfilePath_String, "{indexcontroller}/input/index_controller_profile.json");
+ vr::VRProperties()->SetInt32Property(m_propertyContainer, vr::Prop_Axis2Type_Int32, vr::k_eControllerAxis_Trigger);
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceOff_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_off.png" : "{indexcontroller}/icons/right_controller_status_off.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceSearching_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_searching.gif" : "{indexcontroller}/icons/right_controller_status_searching.gif");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceSearchingAlert_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_searching_alert.gif" : "{indexcontroller}/icons//right_controller_status_searching_alert.gif");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceReady_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_ready.png" : "{indexcontroller}/icons//right_controller_status_ready.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceReadyAlert_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_ready_alert.png" : "{indexcontroller}/icons//right_controller_status_ready_alert.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceNotReady_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_error.png" : "{indexcontroller}/icons//right_controller_status_error.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceStandby_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_off.png" : "{indexcontroller}/icons//right_controller_status_off.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_NamedIconPathDeviceAlertLow_String, (m_hand == CH_Left) ? "{indexcontroller}/icons/left_controller_status_ready_low.png" : "{indexcontroller}/icons//right_controller_status_ready_low.png");
+ vr::VRProperties()->SetStringProperty(m_propertyContainer, vr::Prop_ControllerType_String, "knuckles");
+
+ // Input
+ if(m_buttons.empty())
+ {
+ for(size_t i = 0U; i < IB_Count; i++) m_buttons.push_back(new CControllerButton());
+ }
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/system/click", &m_buttons[IB_SystemClick]->GetHandleRef());
+ m_buttons[IB_SystemClick]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/system/touch", &m_buttons[IB_SystemTouch]->GetHandleRef());
+ m_buttons[IB_SystemTouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/trigger/click", &m_buttons[IB_TriggerClick]->GetHandleRef());
+ m_buttons[IB_TriggerClick]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/trigger/value", &m_buttons[IB_TriggerValue]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_TriggerValue]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/trackpad/x", &m_buttons[IB_TrackpadX]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided);
+ m_buttons[IB_TrackpadX]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/trackpad/y", &m_buttons[IB_TrackpadY]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided);
+ m_buttons[IB_TrackpadY]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/trackpad/touch", &m_buttons[IB_TrackpadTouch]->GetHandleRef());
+ m_buttons[IB_TrackpadTouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/trackpad/force", &m_buttons[IB_TrackpadForce]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_TrackpadForce]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/grip/touch", &m_buttons[IB_GripTouch]->GetHandleRef());
+ m_buttons[IB_GripTouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/grip/force", &m_buttons[IB_GripForce]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_GripForce]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/grip/value", &m_buttons[IB_GripValue]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_GripValue]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/thumbstick/click", &m_buttons[IB_ThumbstickClick]->GetHandleRef());
+ m_buttons[IB_ThumbstickClick]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/thumbstick/touch", &m_buttons[IB_ThumbstickTouch]->GetHandleRef());
+ m_buttons[IB_ThumbstickTouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/thumbstick/x", &m_buttons[IB_ThumbstickX]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided);
+ m_buttons[IB_ThumbstickX]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/thumbstick/y", &m_buttons[IB_ThumbstickY]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedTwoSided);
+ m_buttons[IB_ThumbstickY]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/a/click", &m_buttons[IB_AClick]->GetHandleRef());
+ m_buttons[IB_AClick]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/a/touch", &m_buttons[IB_ATouch]->GetHandleRef());
+ m_buttons[IB_ATouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/b/click", &m_buttons[IB_BClick]->GetHandleRef());
+ m_buttons[IB_BClick]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateBooleanComponent(m_propertyContainer, "/input/b/touch", &m_buttons[IB_BTouch]->GetHandleRef());
+ m_buttons[IB_BTouch]->SetInputType(CControllerButton::IT_Boolean);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/finger/index", &m_buttons[IB_FingerIndex]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_FingerIndex]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/finger/middle", &m_buttons[IB_FingerMiddle]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_FingerMiddle]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/finger/ring", &m_buttons[IB_FingerRing]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_FingerRing]->SetInputType(CControllerButton::IT_Float);
+
+ vr::VRDriverInput()->CreateScalarComponent(m_propertyContainer, "/input/finger/pinky", &m_buttons[IB_FingerPinky]->GetHandleRef(), vr::VRScalarType_Absolute, vr::VRScalarUnits_NormalizedOneSided);
+ m_buttons[IB_FingerPinky]->SetInputType(CControllerButton::IT_Float);
+
+ const vr::EVRSkeletalTrackingLevel l_trackingLevel = ((CDriverConfig::GetTrackingLevel() == CDriverConfig::TL_Partial) ? vr::VRSkeletalTracking_Partial : vr::VRSkeletalTracking_Full);
+ switch(m_hand)
+ {
+ case CH_Left:
+ vr::VRDriverInput()->CreateSkeletonComponent(m_propertyContainer, "/input/skeleton/left", "/skeleton/hand/left", "/pose/raw", l_trackingLevel, nullptr, 0U, &m_skeletonHandle);
+ break;
+ case CH_Right:
+ vr::VRDriverInput()->CreateSkeletonComponent(m_propertyContainer, "/input/skeleton/right", "/skeleton/hand/right", "/pose/raw", l_trackingLevel, nullptr, 0U, &m_skeletonHandle);
+ break;
+ }
+
+ vr::VRDriverInput()->CreateHapticComponent(m_propertyContainer, "/output/haptic", &m_haptic);
+}
+
+void CLeapControllerIndex::UpdateGestures(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand)
+{
+ if(f_hand)
+ {
+ std::vector l_gestures;
+ CGestureMatcher::GetGestures(f_hand, l_gestures, f_oppHand);
+
+ m_buttons[IB_TriggerValue]->SetValue(l_gestures[CGestureMatcher::HG_Trigger]);
+ m_buttons[IB_TriggerClick]->SetState(l_gestures[CGestureMatcher::HG_Trigger] >= 0.75f);
+
+ m_buttons[IB_GripValue]->SetValue(l_gestures[CGestureMatcher::HG_Grab]);
+ m_buttons[IB_GripTouch]->SetState(l_gestures[CGestureMatcher::HG_Grab] >= 0.25f);
+ m_buttons[IB_GripForce]->SetValue((l_gestures[CGestureMatcher::HG_Grab] >= 0.75f) ? (l_gestures[CGestureMatcher::HG_Grab] - 0.75f) * 4.f : 0.f);
+
+ m_buttons[IB_FingerIndex]->SetValue(l_gestures[CGestureMatcher::HG_IndexBend]);
+ m_buttons[IB_FingerMiddle]->SetValue(l_gestures[CGestureMatcher::HG_MiddleBend]);
+ m_buttons[IB_FingerRing]->SetValue(l_gestures[CGestureMatcher::HG_RingBend]);
+ m_buttons[IB_FingerPinky]->SetValue(l_gestures[CGestureMatcher::HG_PinkyBend]);
+
+ for(size_t i = 0U; i < 5U; i++)
+ {
+ const LEAP_DIGIT &l_finger = f_hand->digits[i];
+ size_t l_transformIndex = GetFingerBoneIndex(i);
+ if(l_transformIndex != HSB_Thumb0) l_transformIndex++;
+
+ LEAP_QUATERNION l_leapRotation = f_hand->palm.orientation;
+ glm::quat l_segmentRotation;
+ ConvertQuaternion(l_leapRotation, l_segmentRotation);
+
+ for(size_t j = 1U; j < 4U; j++)
+ {
+ const glm::quat l_prevSegmentRotationInv = glm::inverse(l_segmentRotation);
+ l_leapRotation = l_finger.bones[j].rotation;
+ ConvertQuaternion(l_leapRotation, l_segmentRotation);
+
+ glm::quat l_segmentResult = l_prevSegmentRotationInv * l_segmentRotation;
+ ChangeBoneOrientation(l_segmentResult);
+ if(l_transformIndex == HSB_Thumb0)
+ {
+ std::swap(l_segmentResult.z, l_segmentResult.w);
+ l_segmentResult.w *= -1.f;
+ if(m_hand == CH_Right)
+ {
+ std::swap(l_segmentResult.x, l_segmentResult.w);
+ l_segmentResult.w *= -1.f;
+ std::swap(l_segmentResult.y, l_segmentResult.z);
+ l_segmentResult.y *= -1.f;
+ }
+ }
+ ConvertQuaternion(l_segmentResult, m_boneTransform[l_transformIndex].orientation);
+ l_transformIndex++;
+ }
+ }
+
+ // Update aux bones
+ glm::vec3 l_position;
+ glm::quat l_rotation;
+ ConvertVector3(m_boneTransform[HSB_Wrist].position, l_position);
+ ConvertQuaternion(m_boneTransform[HSB_Wrist].orientation, l_rotation);
+ const glm::mat4 l_wristMat = glm::translate(g_identityMatrix, l_position) * glm::mat4_cast(l_rotation);
+
+ for(size_t i = HF_Thumb; i < HF_Count; i++)
+ {
+ glm::mat4 l_chainMat(l_wristMat);
+ const size_t l_chainIndex = GetFingerBoneIndex(i);
+ for(size_t j = 0U; j < ((i == HF_Thumb) ? 3U : 4U); j++)
+ {
+ ConvertVector3(m_boneTransform[l_chainIndex + j].position, l_position);
+ ConvertQuaternion(m_boneTransform[l_chainIndex + j].orientation, l_rotation);
+ l_chainMat = l_chainMat * (glm::translate(g_identityMatrix, l_position)*glm::mat4_cast(l_rotation));
+ }
+ l_position = l_chainMat * g_zeroPoint;
+ l_rotation = glm::quat_cast(l_chainMat);
+ if(m_hand == CH_Left) ChangeAuxTransformation(l_position, l_rotation);
+ ConvertVector3(l_position, m_boneTransform[HSB_Aux_Thumb + i].position);
+ ConvertQuaternion(l_rotation, m_boneTransform[HSB_Aux_Thumb + i].orientation);
+ }
+ }
+}
+
+void CLeapControllerIndex::UpdateInputInternal()
+{
+ vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletonHandle, vr::VRSkeletalMotionRange_WithController, m_boneTransform, HSB_Count);
+ vr::VRDriverInput()->UpdateSkeletonComponent(m_skeletonHandle, vr::VRSkeletalMotionRange_WithoutController, m_boneTransform, HSB_Count);
+}
+
+void CLeapControllerIndex::SetButtonState(size_t f_button, bool f_state)
+{
+ if(f_button < m_buttons.size())
+ m_buttons[f_button]->SetState(f_state);
+}
+
+void CLeapControllerIndex::SetButtonValue(size_t f_button, float f_value)
+{
+ if(f_button < m_buttons.size())
+ m_buttons[f_button]->SetValue(f_value);
+}
diff --git a/driver_leap/Devices/CLeapController/CLeapControllerIndex.h b/driver_leap/Devices/CLeapController/CLeapControllerIndex.h
new file mode 100644
index 00000000..9bf09052
--- /dev/null
+++ b/driver_leap/Devices/CLeapController/CLeapControllerIndex.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "Devices/CLeapController/CLeapController.h"
+
+class CLeapControllerIndex final : public CLeapController
+{
+ enum HandSkeletonBone : size_t
+ {
+ HSB_Root = 0U,
+ HSB_Wrist,
+ HSB_Thumb0,
+ HSB_Thumb1,
+ HSB_Thumb2,
+ HSB_Thumb3, // Last, no effect
+ HSB_IndexFinger0,
+ HSB_IndexFinger1,
+ HSB_IndexFinger2,
+ HSB_IndexFinger3,
+ HSB_IndexFinger4, // Last, no effect
+ HSB_MiddleFinger0,
+ HSB_MiddleFinger1,
+ HSB_MiddleFinger2,
+ HSB_MiddleFinger3,
+ HSB_MiddleFinger4, // Last, no effect
+ HSB_RingFinger0,
+ HSB_RingFinger1,
+ HSB_RingFinger2,
+ HSB_RingFinger3,
+ HSB_RingFinger4, // Last, no effect
+ HSB_PinkyFinger0,
+ HSB_PinkyFinger1,
+ HSB_PinkyFinger2,
+ HSB_PinkyFinger3,
+ HSB_PinkyFinger4, // Last, no effect
+ HSB_Aux_Thumb,
+ HSB_Aux_IndexFinger,
+ HSB_Aux_MiddleFinger,
+ HSB_Aux_RingFinger,
+ HSB_Aux_PinkyFinger,
+
+ HSB_Count
+ };
+
+ vr::VRBoneTransform_t m_boneTransform[HSB_Count];
+ vr::VRInputComponentHandle_t m_skeletonHandle;
+
+ CLeapControllerIndex(const CLeapControllerIndex &that) = delete;
+ CLeapControllerIndex& operator=(const CLeapControllerIndex &that) = delete;
+
+ void ChangeBoneOrientation(glm::quat &f_rot);
+ static void ChangeAuxTransformation(glm::vec3 &f_pos, glm::quat &f_rot);
+ static size_t GetFingerBoneIndex(size_t f_id);
+
+ // CLeapController
+ void ActivateInternal() override;
+ void UpdateGestures(const LEAP_HAND *f_hand, const LEAP_HAND *f_oppHand) override;
+ void UpdateInputInternal() override;
+public:
+ enum IndexButton : size_t
+ {
+ IB_SystemClick = 0U,
+ IB_SystemTouch,
+ IB_TriggerClick,
+ IB_TriggerValue,
+ IB_TrackpadX,
+ IB_TrackpadY,
+ IB_TrackpadTouch,
+ IB_TrackpadForce,
+ IB_GripTouch,
+ IB_GripForce,
+ IB_GripValue,
+ IB_ThumbstickClick,
+ IB_ThumbstickTouch,
+ IB_ThumbstickX,
+ IB_ThumbstickY,
+ IB_AClick,
+ IB_ATouch,
+ IB_BClick,
+ IB_BTouch,
+ IB_FingerIndex,
+ IB_FingerMiddle,
+ IB_FingerRing,
+ IB_FingerPinky,
+
+ IB_Count
+ };
+
+ explicit CLeapControllerIndex(unsigned char f_hand);
+ ~CLeapControllerIndex();
+
+ void SetButtonState(size_t f_button, bool f_state);
+ void SetButtonValue(size_t f_button, float f_value);
+};
diff --git a/driver_leap/Devices/CLeapStation.cpp b/driver_leap/Devices/CLeapStation.cpp
new file mode 100644
index 00000000..5c535b38
--- /dev/null
+++ b/driver_leap/Devices/CLeapStation.cpp
@@ -0,0 +1,147 @@
+#include "stdafx.h"
+
+#include "Devices/CLeapStation.h"
+#include "Core/CServerDriver.h"
+
+CLeapStation::CLeapStation(CServerDriver *f_server)
+{
+ m_pose = { 0 };
+ m_pose.deviceIsConnected = true;
+ m_pose.poseIsValid = true;
+ m_pose.poseTimeOffset = 0.;
+ m_pose.qDriverFromHeadRotation = { 1.0, .0, .0, .0 };
+ m_pose.qRotation = { 1.0, .0, .0, .0 };
+ m_pose.qWorldFromDriverRotation = { 1.0, .0, .0, .0 };
+ m_pose.result = vr::TrackingResult_Running_OK;
+ m_pose.shouldApplyHeadModel = false;
+ for(size_t i = 0U; i < 3U; i++)
+ {
+ m_pose.vecAcceleration[i] = 0.;
+ m_pose.vecAngularAcceleration[i] = 0.;
+ m_pose.vecAngularVelocity[i] = 0.;
+ m_pose.vecDriverFromHeadTranslation[i] = 0.;
+ m_pose.vecPosition[i] = 0.;
+ m_pose.vecVelocity[i] = 0.;
+ }
+ m_pose.willDriftInYaw = false;
+
+ m_propertyHandle = vr::k_ulInvalidPropertyContainer;
+ m_trackedDevice = vr::k_unTrackedDeviceIndexInvalid;
+
+ m_serialNumber.assign("leap_motion_station");
+ m_serverDriver = f_server;
+}
+
+CLeapStation::~CLeapStation()
+{
+}
+
+// vr::ITrackedDeviceServerDriver
+vr::EVRInitError CLeapStation::Activate(uint32_t unObjectId)
+{
+ vr::EVRInitError l_resultError = vr::VRInitError_Driver_CalibrationInvalid;
+
+ if(m_trackedDevice == vr::k_unTrackedDeviceIndexInvalid)
+ {
+ m_trackedDevice = unObjectId;
+ m_propertyHandle = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_trackedDevice);
+
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_TrackingSystemName_String, "leap_motion");
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_SerialNumber_String, m_serialNumber.c_str());
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_ModelNumber_String, m_serialNumber.c_str());
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_ManufacturerName_String, "Leap Motion");
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_ModeLabel_String, "L");
+
+ vr::VRProperties()->SetInt32Property(m_propertyHandle, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_TrackingReference);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_IsOnDesktop_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_NeverTracked_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_WillDriftInYaw_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_CanWirelessIdentify_Bool, false);
+
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_FieldOfViewLeftDegrees_Float, 150.f);
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_FieldOfViewRightDegrees_Float, 150.f);
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_FieldOfViewTopDegrees_Float, 120.f);
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_FieldOfViewBottomDegrees_Float, 120.f);
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_TrackingRangeMinimumMeters_Float, 0.025f);
+ vr::VRProperties()->SetFloatProperty(m_propertyHandle, vr::Prop_TrackingRangeMaximumMeters_Float, 0.6f);
+
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_ResourceRoot_String, "leap");
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_RenderModelName_String, "{leap}leap_motion_1_0");
+
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_NamedIconPathDeviceReady_String, "{leap}/icons/base_status_ready.png");
+ vr::VRProperties()->SetStringProperty(m_propertyHandle, vr::Prop_NamedIconPathDeviceSearching_String, "{leap}/icons/base_status_searching.png");
+
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_HasDisplayComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_HasCameraComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_HasDriverDirectModeComponent_Bool, false);
+ vr::VRProperties()->SetBoolProperty(m_propertyHandle, vr::Prop_HasVirtualDisplayComponent_Bool, false);
+
+ vr::VRProperties()->SetUint64Property(m_propertyHandle, vr::Prop_VendorSpecific_Reserved_Start, 0x4C4D6F74696F6E); // "LMotion", hidden property for leap_monitor
+
+ m_pose.vecWorldFromDriverTranslation[0U] = 0.1f; // Slightly move to avoid base stations
+
+ l_resultError = vr::VRInitError_None;
+ }
+
+ return l_resultError;
+}
+
+void CLeapStation::Deactivate()
+{
+ m_trackedDevice = vr::k_unTrackedDeviceIndexInvalid;
+}
+
+void CLeapStation::EnterStandby()
+{
+}
+
+void* CLeapStation::GetComponent(const char* pchComponentNameAndVersion)
+{
+ void *l_result = nullptr;
+ if(!strcmp(pchComponentNameAndVersion, vr::ITrackedDeviceServerDriver_Version)) l_result = dynamic_cast(this);
+ return l_result;
+}
+
+void CLeapStation::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize)
+{
+ if(m_trackedDevice != vr::k_unTrackedDeviceIndexInvalid)
+ {
+ if(m_serverDriver) m_serverDriver->ProcessExternalMessage(pchRequest);
+ }
+}
+
+vr::DriverPose_t CLeapStation::GetPose()
+{
+ return m_pose;
+}
+
+// CLeapStation
+const std::string& CLeapStation::GetSerialNumber() const
+{
+ return m_serialNumber;
+}
+
+void CLeapStation::SetTrackingState(TrackingState f_state)
+{
+ switch(f_state)
+ {
+ case TS_Connected:
+ {
+ m_pose.result = vr::TrackingResult_Running_OK;
+ m_pose.poseIsValid = true;
+ } break;
+ case TS_Search:
+ {
+ m_pose.result = vr::TrackingResult_Calibrating_OutOfRange;
+ m_pose.poseIsValid = false;
+ } break;
+ }
+}
+
+void CLeapStation::RunFrame()
+{
+ if(m_trackedDevice != vr::k_unTrackedDeviceIndexInvalid)
+ {
+ vr::VRServerDriverHost()->TrackedDevicePoseUpdated(m_trackedDevice, m_pose, sizeof(vr::DriverPose_t));
+ }
+}
diff --git a/driver_leap/Devices/CLeapStation.h b/driver_leap/Devices/CLeapStation.h
new file mode 100644
index 00000000..80cf9163
--- /dev/null
+++ b/driver_leap/Devices/CLeapStation.h
@@ -0,0 +1,39 @@
+#pragma once
+
+class CServerDriver;
+
+class CLeapStation final : public vr::ITrackedDeviceServerDriver
+{
+ vr::DriverPose_t m_pose;
+ vr::PropertyContainerHandle_t m_propertyHandle;
+ uint32_t m_trackedDevice;
+
+ CServerDriver *m_serverDriver;
+ std::string m_serialNumber;
+
+ CLeapStation(const CLeapStation &that) = delete;
+ CLeapStation& operator=(const CLeapStation &that) = delete;
+
+ // vr::ITrackedDeviceServerDriver
+ 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();
+public:
+ enum TrackingState : unsigned char
+ {
+ TS_Connected = 0U,
+ TS_Search
+ };
+
+ explicit CLeapStation(CServerDriver *f_server);
+ ~CLeapStation();
+
+ const std::string& GetSerialNumber() const;
+
+ void SetTrackingState(TrackingState f_state);
+
+ void RunFrame();
+};
diff --git a/driver_leap/Utils/CGestureMatcher.cpp b/driver_leap/Utils/CGestureMatcher.cpp
new file mode 100644
index 00000000..eb7f6662
--- /dev/null
+++ b/driver_leap/Utils/CGestureMatcher.cpp
@@ -0,0 +1,42 @@
+#include "stdafx.h"
+
+#include "Utils/CGestureMatcher.h"
+
+const float g_pi = glm::pi();
+const float g_piHalf = g_pi * 0.5f;
+const float g_piQuarter = g_pi * 0.25f;
+extern const glm::mat4 g_identityMatrix;
+
+void CGestureMatcher::GetGestures(const LEAP_HAND *f_hand, std::vector &f_result, const LEAP_HAND *f_oppHand)
+{
+ f_result.resize(HG_Count, 0.f);
+
+ // Finger bends
+ float l_fingerBend[5U] = { 0.f };
+ for(size_t i = 0U; i < 5U; i++)
+ {
+ const LEAP_DIGIT &l_finger = f_hand->digits[i];
+ glm::vec3 l_prevDirection;
+ const size_t l_startBoneIndex = ((i == 0U) ? 1U : 0U);
+ for(size_t j = l_startBoneIndex; j < 4U; j++)
+ {
+ const LEAP_BONE &l_bone = l_finger.bones[j];
+ glm::vec3 l_direction(l_bone.next_joint.x - l_bone.prev_joint.x, l_bone.next_joint.y - l_bone.prev_joint.y, l_bone.next_joint.z - l_bone.prev_joint.z);
+ l_direction = glm::normalize(l_direction);
+ if(j > l_startBoneIndex) l_fingerBend[i] += glm::acos(glm::dot(l_direction, l_prevDirection));
+ l_prevDirection = l_direction;
+ }
+ }
+
+ for(size_t i = 0U; i <= HG_PinkyBend; i++) f_result[i] = NormalizeRange(l_fingerBend[i], (i == 0U) ? 0.f : g_piQuarter, (i == 0U) ? g_piQuarter : g_pi);
+
+ // Simple gestures
+ f_result[HG_Trigger] = f_result[HG_IndexBend];
+ f_result[HG_Grab] = NormalizeRange((l_fingerBend[2U] + l_fingerBend[3U] + l_fingerBend[4U]) / 3.f, g_piHalf, g_pi);
+}
+
+float CGestureMatcher::NormalizeRange(float f_val, float f_min, float f_max)
+{
+ const float l_mapped = (f_val - f_min) / (f_max - f_min);
+ return glm::clamp(l_mapped, 0.f, 1.f);
+}
diff --git a/driver_leap/Utils/CGestureMatcher.h b/driver_leap/Utils/CGestureMatcher.h
new file mode 100644
index 00000000..cbdeeef2
--- /dev/null
+++ b/driver_leap/Utils/CGestureMatcher.h
@@ -0,0 +1,24 @@
+#pragma once
+
+class CGestureMatcher
+{
+ static float NormalizeRange(float f_val, float f_min, float f_max);
+public:
+ enum HandGesture : size_t
+ {
+ // Finger bends
+ HG_ThumbBend = 0U,
+ HG_IndexBend,
+ HG_MiddleBend,
+ HG_RingBend,
+ HG_PinkyBend,
+
+ // Simple gestures
+ HG_Trigger,
+ HG_Grab,
+
+ HG_Count
+ };
+
+ static void GetGestures(const LEAP_HAND *f_hand, std::vector &f_result, const LEAP_HAND *f_oppHand = nullptr);
+};
diff --git a/driver_leap/Utils/Utils.cpp b/driver_leap/Utils/Utils.cpp
new file mode 100644
index 00000000..d88ca87b
--- /dev/null
+++ b/driver_leap/Utils/Utils.cpp
@@ -0,0 +1,91 @@
+#include "stdafx.h"
+
+#include "Utils/Utils.h"
+
+extern const glm::mat4 g_identityMatrix(1.f);
+
+// Left hand open gesture transformation, thanks to https://github.com/spayne and his soft_knuckles repository
+extern const vr::VRBoneTransform_t g_openHandGesture[31U]
+{
+ { {{ 0.000000f, 0.000000f, 0.000000f, 1.000000f }}, { 1.000000f, -0.000000f, -0.000000f, 0.000000f } },
+ { {{ -0.034038f, 0.026503f, 0.174722f, 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 } }
+};
+
+extern const glm::vec4 g_zeroPoint(0.f, 0.f, 0.f, 1.f);
+
+void ConvertMatrix(const vr::HmdMatrix34_t &f_matVR, glm::mat4 &f_mat)
+{
+ for(int i = 0; i < 4; i++)
+ {
+ for(int j = 0; j < 3; j++) f_mat[i][j] = f_matVR.m[j][i];
+ }
+ for(int i = 0; i < 3; i++) f_mat[i][3] = 0.f;
+ f_mat[3][3] = 1.f;
+}
+
+void ConvertVector3(const vr::HmdVector4_t &f_vrVec, glm::vec3 &f_glmVec)
+{
+ for(size_t i = 0U; i < 3U; i++) f_glmVec[i] = f_vrVec.v[i];
+}
+
+void ConvertVector3(const glm::vec3 &f_glmVec, vr::HmdVector4_t &f_vrVec)
+{
+ for(size_t i = 0U; i < 3U; i++) f_vrVec.v[i] = f_glmVec[i];
+}
+
+size_t ReadEnumVector(const std::string &f_val, const std::vector &f_vec)
+{
+ size_t l_result = std::numeric_limits::max();
+ for(auto iter = f_vec.begin(), iterEnd = f_vec.end(); iter != iterEnd; ++iter)
+ {
+ if(!iter->compare(f_val))
+ {
+ l_result = std::distance(f_vec.begin(), iter);
+ break;
+ }
+ }
+ return l_result;
+}
+
+size_t ReadEnumVector(const char *f_val, const std::vector &f_vec)
+{
+ size_t l_result = std::numeric_limits::max();
+ for(auto iter = f_vec.begin(), iterEnd = f_vec.end(); iter != iterEnd; ++iter)
+ {
+ if(!iter->compare(f_val))
+ {
+ l_result = std::distance(f_vec.begin(), iter);
+ break;
+ }
+ }
+ return l_result;
+}
diff --git a/driver_leap/Utils/Utils.h b/driver_leap/Utils/Utils.h
new file mode 100644
index 00000000..a6a9426d
--- /dev/null
+++ b/driver_leap/Utils/Utils.h
@@ -0,0 +1,15 @@
+#pragma once
+
+void ConvertMatrix(const vr::HmdMatrix34_t &f_matVR, glm::mat4 &f_mat);
+template void ConvertQuaternion(const T &f_quatA, U &f_quatB)
+{
+ f_quatB.x = f_quatA.x;
+ f_quatB.y = f_quatA.y;
+ f_quatB.z = f_quatA.z;
+ f_quatB.w = f_quatA.w;
+}
+void ConvertVector3(const vr::HmdVector4_t &f_vrVec, glm::vec3 &f_glmVec);
+void ConvertVector3(const glm::vec3 &f_glmVec, vr::HmdVector4_t &f_vrVec);
+
+size_t ReadEnumVector(const std::string &f_val, const std::vector &f_vec);
+size_t ReadEnumVector(const char *f_val, const std::vector &f_vec);
diff --git a/driver_leap/dllmain.cpp b/driver_leap/dllmain.cpp
new file mode 100644
index 00000000..b619752d
--- /dev/null
+++ b/driver_leap/dllmain.cpp
@@ -0,0 +1,31 @@
+#include "stdafx.h"
+
+#include "Core/CServerDriver.h"
+
+char g_modulePath[2048U];
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /* lpReserved */)
+{
+ switch(ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ GetModuleFileNameA(hModule, g_modulePath, 2048U);
+ break;
+ case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+CServerDriver g_serverDriver;
+
+extern "C" __declspec(dllexport) void* HmdDriverFactory(const char *pInterfaceName, int *pReturnCode)
+{
+ void *l_result = nullptr;
+ if(!strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName)) l_result = dynamic_cast(&g_serverDriver);
+ else
+ {
+ if(pReturnCode) *pReturnCode = vr::VRInitError_Init_InterfaceNotFound;
+ }
+ return l_result;
+}
diff --git a/driver_leap/driver_leap.vcxproj b/driver_leap/driver_leap.vcxproj
new file mode 100644
index 00000000..f081abcb
--- /dev/null
+++ b/driver_leap/driver_leap.vcxproj
@@ -0,0 +1,160 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {52d3f16d-a7a5-4d6f-8f17-5e4b459a1440}
+ DynamicLibrary
+ driver_leap
+ driver_leap
+ en-US
+ 12.0
+ 8.1
+ 8.1
+
+
+
+ DynamicLibrary
+ true
+ v141
+
+
+ DynamicLibrary
+ false
+ true
+ v141
+
+
+
+
+
+
+
+
+
+ false
+ false
+ false
+ $(SolutionDir)bin\win64\
+ $(SolutionDir)objs\$(ProjectName)\$(PlatformName)\$(Configuration)\
+
+
+ false
+ false
+ true
+ $(SolutionDir)bin\win64\
+ $(SolutionDir)objs\$(ProjectName)\$(PlatformName)\$(Configuration)\
+
+
+
+ Use
+ false
+
+
+ Console
+ false
+ false
+
+
+
+
+ Use
+ false
+
+
+ Console
+ false
+ false
+
+
+
+
+ Use
+ false
+ ./;../vendor/LeapSDK/include;../vendor/openvr/headers;../vendor/pugixml/src;../vendor/glm;%(AdditionalIncludeDirectories)
+ stdafx.h
+
+
+ Windows
+ false
+ false
+ ../vendor/LeapSDK/lib/x64;../vendor/openvr/lib/win64;%(AdditionalLibraryDirectories)
+ openvr_api.lib;LeapC.lib;%(AdditionalDependencies)
+ true
+
+
+
+
+
+ copy /y "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\leap\bin\win64\$(TargetFileName)"
+ Install Binary
+
+
+
+
+ Use
+ false
+ ./;../vendor/LeapSDK/include;../vendor/openvr/headers;../vendor/pugixml/src;../vendor/glm;%(AdditionalIncludeDirectories)
+ stdafx.h
+ true
+
+
+ Windows
+ false
+ false
+ ../vendor/LeapSDK/lib/x64;../vendor/openvr/lib/win64;%(AdditionalLibraryDirectories)
+ openvr_api.lib;LeapC.lib;%(AdditionalDependencies)
+ false
+
+
+
+
+
+ copy /y "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\leap\bin\win64\$(TargetFileName)"
+ Install Binary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NotUsing
+ NotUsing
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/driver_leap/driver_leap.vcxproj.filters b/driver_leap/driver_leap.vcxproj.filters
new file mode 100644
index 00000000..48c82205
--- /dev/null
+++ b/driver_leap/driver_leap.vcxproj.filters
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+ vendor\pugixml
+
+
+ Utils
+
+
+ Utils
+
+
+ Devices
+
+
+ Devices\CLeapController
+
+
+ Devices\CLeapController
+
+
+ Devices\CLeapController
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+
+
+
+ Utils
+
+
+ Utils
+
+
+ Devices
+
+
+ Devices\CLeapController
+
+
+ Devices\CLeapController
+
+
+ Devices\CLeapController
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+
+
+ {4dd100d6-5318-4ebc-a9eb-9ef27f1ae76b}
+
+
+ {21c081d3-867d-441e-90d0-ef26e9d05cae}
+
+
+ {839b3e7e-6f9e-49a2-b8e7-c8c40d0cd982}
+
+
+ {b03c9f12-ff59-41c2-addf-e0d1c366d73a}
+
+
+ {5709dbc5-cf75-4243-a691-22f7d553f0da}
+
+
+ {82e3a6c5-9886-457e-8308-b6c62ec7ce03}
+
+
+
\ No newline at end of file
diff --git a/driver_leap/stdafx.cpp b/driver_leap/stdafx.cpp
new file mode 100644
index 00000000..d1303f39
--- /dev/null
+++ b/driver_leap/stdafx.cpp
@@ -0,0 +1 @@
+#include "stdafx.h"
diff --git a/driver_leap/stdafx.h b/driver_leap/stdafx.h
new file mode 100644
index 00000000..b6d2a751
--- /dev/null
+++ b/driver_leap/stdafx.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include
+#include
+
+#include
+#include
+#include
+#include