diff --git a/.gitmodules b/.gitmodules index 30c4abc..bf5392b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "libraries/openvr"] path = libraries/openvr url = https://github.com/ValveSoftware/openvr.git +[submodule "libraries/linalg"] + path = libraries/linalg + url = https://github.com/sgorsten/linalg.git diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9bd8f7d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "VRServer", + "type": "cppvsdbg", + "request": "launch", + "program": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\bin\\win64\\vrserver.exe", + "args": [ + "--keepalive" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + }, + { + "name": "SteamVR", + "type": "cppvsdbg", + "request": "launch", + "program": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\SteamVR\\bin\\win64\\vrstartup.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + } + ], + "compounds": [ + { + "name": "Launch", + "configurations": ["VRServer", "SteamVR"], + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 99d2707..011f536 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.0.0) project(SlimeVR-OpenVR-Driver VERSION 0.1.0) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(DRIVER_NAME "SlimeVR") include(CTest) enable_testing() @@ -30,6 +31,7 @@ file(GLOB_RECURSE HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp") file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") add_library("${PROJECT_NAME}" SHARED "${HEADERS}" "${SOURCES}") target_include_directories("${PROJECT_NAME}" PUBLIC "${OPENVR_INCLUDE_DIR}") +target_include_directories("${PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libraries/linalg") target_include_directories("${PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src/") target_link_libraries("${PROJECT_NAME}" PUBLIC "${OPENVR_LIB}") set_property(TARGET "${PROJECT_NAME}" PROPERTY CXX_STANDARD 17) @@ -41,3 +43,22 @@ source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" PREFIX "Source Files" FILES set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) + +# Build +# Copy driver assets to output folder +add_custom_command( + TARGET ${PROJECT_NAME} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/driver/ + $/driver +) + +# Copy dll to output folder +add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + $/driver/${DRIVER_NAME}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH}/driver_${DRIVER_NAME}$ +) diff --git a/driver/slimevr/bin/win64/.gitkeep b/driver/slimevr/bin/win64/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/driver/slimevr/driver.vrdrivermanifest b/driver/slimevr/driver.vrdrivermanifest new file mode 100644 index 0000000..fe920fc --- /dev/null +++ b/driver/slimevr/driver.vrdrivermanifest @@ -0,0 +1,6 @@ +{ + "alwaysActivate": true, + "name" : "SlimeVR", + "directory" : "", + "resourceOnly" : false +} diff --git a/driver/slimevr/resources/icons/controller_not_ready_left.png b/driver/slimevr/resources/icons/controller_not_ready_left.png new file mode 100644 index 0000000..63963ce Binary files /dev/null and b/driver/slimevr/resources/icons/controller_not_ready_left.png differ diff --git a/driver/slimevr/resources/icons/controller_not_ready_right.png b/driver/slimevr/resources/icons/controller_not_ready_right.png new file mode 100644 index 0000000..fca9b14 Binary files /dev/null and b/driver/slimevr/resources/icons/controller_not_ready_right.png differ diff --git a/driver/slimevr/resources/icons/controller_ready_left.png b/driver/slimevr/resources/icons/controller_ready_left.png new file mode 100644 index 0000000..13c9858 Binary files /dev/null and b/driver/slimevr/resources/icons/controller_ready_left.png differ diff --git a/driver/slimevr/resources/icons/controller_ready_right.png b/driver/slimevr/resources/icons/controller_ready_right.png new file mode 100644 index 0000000..bec24a0 Binary files /dev/null and b/driver/slimevr/resources/icons/controller_ready_right.png differ diff --git a/driver/slimevr/resources/icons/hmd_not_ready.png b/driver/slimevr/resources/icons/hmd_not_ready.png new file mode 100644 index 0000000..3c2b652 Binary files /dev/null and b/driver/slimevr/resources/icons/hmd_not_ready.png differ diff --git a/driver/slimevr/resources/icons/hmd_ready.png b/driver/slimevr/resources/icons/hmd_ready.png new file mode 100644 index 0000000..753ff4c Binary files /dev/null and b/driver/slimevr/resources/icons/hmd_ready.png differ diff --git a/driver/slimevr/resources/icons/tracker_not_ready.png b/driver/slimevr/resources/icons/tracker_not_ready.png new file mode 100644 index 0000000..7f1521f Binary files /dev/null and b/driver/slimevr/resources/icons/tracker_not_ready.png differ diff --git a/driver/slimevr/resources/icons/tracker_ready.png b/driver/slimevr/resources/icons/tracker_ready.png new file mode 100644 index 0000000..468059e Binary files /dev/null and b/driver/slimevr/resources/icons/tracker_ready.png differ diff --git a/driver/slimevr/resources/icons/trackingreference_not_ready.png b/driver/slimevr/resources/icons/trackingreference_not_ready.png new file mode 100644 index 0000000..e4624fb Binary files /dev/null and b/driver/slimevr/resources/icons/trackingreference_not_ready.png differ diff --git a/driver/slimevr/resources/icons/trackingreference_ready.png b/driver/slimevr/resources/icons/trackingreference_ready.png new file mode 100644 index 0000000..2dfc9cb Binary files /dev/null and b/driver/slimevr/resources/icons/trackingreference_ready.png differ diff --git a/driver/slimevr/resources/input/example_controller_bindings.json b/driver/slimevr/resources/input/example_controller_bindings.json new file mode 100644 index 0000000..51099b1 --- /dev/null +++ b/driver/slimevr/resources/input/example_controller_bindings.json @@ -0,0 +1,83 @@ +{ + "jsonid": "input_profile", + "controller_type": "example_controller", + "device_class": "TrackedDeviceClass_Controller", + "resource_root": "example", + "driver_name": "example", + "input_bindingui_mode": "controller_handed", + "should_show_binding_errors": true, + "input_bindingui_left": { + "image": "{example}/icons/example_controller_left.svg" + }, + "input_bindingui_right": { + "image": "{example}/icons/example_controller_right.svg" + }, + "input_source": { + "/pose/raw" : { + "type" : "pose", + "binding_image_point" : [ 0,0 ] + }, + "/output/haptic": { + "type": "vibration", + "binding_image_point": [0,0] + }, + "/input/a": { + "type": "button", + "click": true, + "touch" : true, + "binding_image_point": [0,0] + }, + "/input/b": { + "type": "button", + "click": true, + "touch" : true, + "binding_image_point": [0,0] + }, + "/input/trigger" : { + "type" : "trigger", + "click" : true, + "touch" : true, + "value" : true, + "binding_image_point" : [ 0, 0 ] + }, + "/input/grip" : { + "type" : "trigger", + "force" : true, + "value" : true, + "touch" : true, + "input_activity_path" : "/input/grip/force", + "input_activity_threshold" : 0.1, + "binding_image_point" : [ 0, 0 ] + }, + "/input/system": { + "type": "button", + "click": true, + "touch" : true, + "binding_image_point": [0,0] + }, + "/input/trackpad": { + "type": "trackpad", + "click": true, + "touch" : true, + "binding_image_point": [0,0] + }, + "/input/joystick": { + "type": "joystick", + "click": true, + "touch": true, + "binding_image_point": [0,0] + }, + "/input/skeleton/left" : { + "type" : "skeleton", + "skeleton": "/skeleton/hand/left", + "side" : "left", + "binding_image_point" : [ 0, 0 ] + }, + "/input/skeleton/right" : { + "type" : "skeleton", + "skeleton": "/skeleton/hand/right", + "side" : "right", + "binding_image_point" : [ 0, 0 ] + } + } +} diff --git a/driver/slimevr/resources/input/example_controller_left.svg b/driver/slimevr/resources/input/example_controller_left.svg new file mode 100644 index 0000000..9a847de --- /dev/null +++ b/driver/slimevr/resources/input/example_controller_left.svg @@ -0,0 +1,1650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/driver/slimevr/resources/input/example_controller_right.svg b/driver/slimevr/resources/input/example_controller_right.svg new file mode 100644 index 0000000..760deb8 --- /dev/null +++ b/driver/slimevr/resources/input/example_controller_right.svg @@ -0,0 +1,1651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/driver/slimevr/resources/input/example_tracker_bindings.json b/driver/slimevr/resources/input/example_tracker_bindings.json new file mode 100644 index 0000000..763d09e --- /dev/null +++ b/driver/slimevr/resources/input/example_tracker_bindings.json @@ -0,0 +1,31 @@ +{ + "jsonid": "input_profile", + "controller_type": "example_tracker", + "device_class": "TrackedDeviceClass_GenericTracker", + "resource_root": "example", + "driver_name": "example", + "input_bindingui_mode": "single_device", + "should_show_binding_errors": true, + "input_bindingui_left": { + "transform": "scale(-1,1)", + "image": "{example}/icons/example_tracker.svg" + }, + "input_bindingui_right": { + "image": "{example}/icons/example_tracker.svg" + }, + "input_source": { + "/pose/raw" : { + "type" : "pose", + "binding_image_point" : [ 0,0 ] + }, + "/output/haptic": { + "type": "vibration", + "binding_image_point": [0,0] + }, + "/input/system": { + "type": "button", + "click": true, + "binding_image_point": [0,0] + } + } +} diff --git a/driver/slimevr/resources/localization/localization.json b/driver/slimevr/resources/localization/localization.json new file mode 100644 index 0000000..72b6980 --- /dev/null +++ b/driver/slimevr/resources/localization/localization.json @@ -0,0 +1,16 @@ +[ + { + "language_tag": "en_US", + "example_controller": "Example Controller", + "example_tracker": "Example Tracker", + "example_basestation": "Example BaseStation", + "/input/a": "A Button", + "/input/b": "B Button", + "/input/system": "System Button", + "/input/trackpad": "Trackpad", + "/input/joystick": "Joystick", + "/input/skeleton": "Skeleton", + "/input/trigger": "Trigger", + "/output/haptic": "Haptic" + } +] diff --git a/driver/slimevr/resources/rendermodels/example_controller/example_controller.fbx b/driver/slimevr/resources/rendermodels/example_controller/example_controller.fbx new file mode 100644 index 0000000..9ab3105 Binary files /dev/null and b/driver/slimevr/resources/rendermodels/example_controller/example_controller.fbx differ diff --git a/driver/slimevr/resources/rendermodels/example_controller/example_controller.mtl b/driver/slimevr/resources/rendermodels/example_controller/example_controller.mtl new file mode 100644 index 0000000..08f028c --- /dev/null +++ b/driver/slimevr/resources/rendermodels/example_controller/example_controller.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material_75 +Ns 900.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.498039 0.498039 0.498039 +Ks 1.000000 1.000000 1.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 3 +map_Kd example_controller.png diff --git a/driver/slimevr/resources/rendermodels/example_controller/example_controller.obj b/driver/slimevr/resources/rendermodels/example_controller/example_controller.obj new file mode 100644 index 0000000..9d65a72 --- /dev/null +++ b/driver/slimevr/resources/rendermodels/example_controller/example_controller.obj @@ -0,0 +1,969 @@ +# Blender v2.81 (sub 16) OBJ File: '' +# www.blender.org +mtllib example_controller.mtl +usemtl Material_75 +v -0.032265 -0.032265 0.032265 +v -0.032265 0.032265 0.032265 +v -0.032265 0.032265 -0.032265 +v -0.032265 -0.032265 0.032265 +v -0.032265 0.032265 -0.032265 +v -0.032265 -0.032265 -0.032265 +v -0.032265 0.032265 -0.032265 +v 0.032265 0.032265 -0.032265 +v 0.016133 0.016133 -0.032265 +v -0.016133 0.016133 -0.032265 +v 0.032265 0.032265 0.032265 +v 0.032265 -0.032265 0.032265 +v 0.032265 -0.016133 0.016133 +v 0.032265 0.016133 0.016133 +v 0.032265 -0.032265 0.032265 +v 0.032265 0.032265 0.032265 +v -0.032265 0.032265 0.032265 +v -0.032265 -0.032265 0.032265 +v -0.032265 -0.032265 -0.032265 +v 0.032265 -0.032265 -0.032265 +v 0.032265 -0.032265 0.032265 +v -0.032265 -0.032265 0.032265 +v 0.032265 0.032265 -0.032265 +v -0.032265 0.032265 -0.032265 +v -0.016133 0.032265 -0.016133 +v 0.016133 0.032265 -0.016133 +v -0.016133 0.016133 -0.032265 +v 0.016133 0.016133 -0.032265 +v 0.028397 0.028398 -0.113590 +v -0.016133 0.016133 -0.032265 +v 0.028397 0.028398 -0.113590 +v -0.028398 0.028398 -0.113590 +v -0.032265 -0.032265 -0.032265 +v -0.016133 -0.016133 -0.032265 +v 0.032265 0.032265 -0.032265 +v 0.032265 -0.032265 -0.032265 +v 0.016133 -0.016133 -0.032265 +v 0.016133 0.016133 -0.032265 +v -0.032265 -0.032265 -0.032265 +v -0.016133 -0.016133 -0.032265 +v 0.032265 -0.016133 -0.016133 +v 0.032265 0.016133 -0.016133 +v 0.113590 0.028398 -0.028397 +v 0.032265 -0.016133 -0.016133 +v 0.113590 0.028398 -0.028397 +v 0.113590 -0.028397 -0.028398 +v 0.032265 -0.032265 0.032265 +v 0.032265 -0.032265 -0.032265 +v 0.032265 -0.016133 -0.016133 +v 0.032265 -0.016133 0.016133 +v 0.032265 0.032265 -0.032265 +v 0.032265 0.016133 -0.016133 +v 0.032265 0.032265 -0.032265 +v 0.032265 0.016133 -0.016133 +v -0.016133 0.032265 0.016133 +v 0.016133 0.032265 0.016133 +v 0.028397 0.113590 0.028398 +v -0.016133 0.032265 0.016133 +v 0.028397 0.113590 0.028398 +v -0.028398 0.113590 0.028398 +v 0.032265 0.032265 0.032265 +v 0.016133 0.032265 0.016133 +v -0.032265 0.032265 -0.032265 +v -0.032265 0.032265 0.032265 +v -0.016133 0.032265 0.016133 +v -0.016133 0.032265 -0.016133 +v 0.032265 0.032265 0.032265 +v 0.016133 0.032265 0.016133 +v 0.028397 0.113590 -0.028397 +v -0.028398 0.113590 -0.028397 +v -0.028398 0.113590 0.028398 +v 0.028397 0.113590 0.028398 +v 0.016133 0.032265 -0.016133 +v -0.016133 0.032265 -0.016133 +v -0.028398 0.113590 -0.028397 +v 0.016133 0.032265 -0.016133 +v -0.028398 0.113590 -0.028397 +v 0.028397 0.113590 -0.028397 +v 0.016133 0.032265 0.016133 +v 0.016133 0.032265 -0.016133 +v 0.028397 0.113590 -0.028397 +v 0.016133 0.032265 0.016133 +v 0.028397 0.113590 -0.028397 +v 0.028397 0.113590 0.028398 +v -0.016133 0.032265 -0.016133 +v -0.016133 0.032265 0.016133 +v -0.028398 0.113590 0.028398 +v -0.016133 0.032265 -0.016133 +v -0.028398 0.113590 0.028398 +v -0.028398 0.113590 -0.028397 +v 0.113590 -0.028397 -0.028398 +v 0.113590 0.028398 -0.028397 +v 0.113590 0.028397 0.028398 +v 0.113590 -0.028398 0.028397 +v 0.032265 0.016133 0.016133 +v 0.032265 -0.016133 0.016133 +v 0.113590 -0.028398 0.028397 +v 0.032265 0.016133 0.016133 +v 0.113590 -0.028398 0.028397 +v 0.113590 0.028397 0.028398 +v 0.032265 -0.016133 0.016133 +v 0.032265 -0.016133 -0.016133 +v 0.113590 -0.028397 -0.028398 +v 0.032265 -0.016133 0.016133 +v 0.113590 -0.028397 -0.028398 +v 0.113590 -0.028398 0.028397 +v 0.032265 0.016133 -0.016133 +v 0.032265 0.016133 0.016133 +v 0.113590 0.028397 0.028398 +v 0.032265 0.016133 -0.016133 +v 0.113590 0.028397 0.028398 +v 0.113590 0.028398 -0.028397 +v -0.028398 -0.028397 -0.113590 +v -0.028398 0.028398 -0.113590 +v 0.028397 0.028398 -0.113590 +v -0.028398 -0.028397 -0.113590 +v 0.028397 0.028398 -0.113590 +v 0.028397 -0.028397 -0.113590 +v -0.016133 -0.016133 -0.032265 +v -0.016133 0.016133 -0.032265 +v -0.028398 0.028398 -0.113590 +v -0.016133 -0.016133 -0.032265 +v -0.028398 0.028398 -0.113590 +v -0.028398 -0.028397 -0.113590 +v 0.016133 0.016133 -0.032265 +v 0.016133 -0.016133 -0.032265 +v 0.028397 -0.028397 -0.113590 +v 0.016133 0.016133 -0.032265 +v 0.028397 -0.028397 -0.113590 +v 0.028397 0.028398 -0.113590 +v 0.016133 -0.016133 -0.032265 +v -0.016133 -0.016133 -0.032265 +v -0.028398 -0.028397 -0.113590 +v 0.016133 -0.016133 -0.032265 +v -0.028398 -0.028397 -0.113590 +v 0.028397 -0.028397 -0.113590 +v -0.001752 0.002383 -0.121335 +v -0.014217 0.018198 -0.121335 +v -0.008403 0.018198 -0.121335 +v -0.001752 0.002383 -0.121335 +v -0.008403 0.018198 -0.121335 +v 0.000612 0.006620 -0.121335 +v 0.009727 0.018198 -0.121335 +v 0.015540 0.018198 -0.121335 +v 0.003076 0.002334 -0.121335 +v -0.001752 -0.015403 -0.121335 +v -0.001752 0.002383 -0.121335 +v 0.003076 0.002334 -0.121335 +v 0.003076 -0.015403 -0.121335 +v -0.001752 0.002383 -0.105327 +v -0.008403 0.018198 -0.105327 +v -0.014217 0.018198 -0.105327 +v 0.000612 0.006620 -0.105327 +v 0.000612 0.006620 -0.105327 +v 0.015540 0.018198 -0.105327 +v 0.009727 0.018198 -0.105327 +v 0.003076 0.002334 -0.105327 +v -0.001752 0.002383 -0.105327 +v -0.001752 -0.015403 -0.105327 +v 0.003076 0.002334 -0.105327 +v -0.001752 0.002383 -0.105327 +v -0.001752 -0.015403 -0.105327 +v 0.003076 -0.015403 -0.105327 +v 0.003076 0.002334 -0.105327 +v 0.003076 0.002334 -0.121335 +v 0.015540 0.018198 -0.121335 +v 0.015540 0.018198 -0.105327 +v 0.003076 0.002334 -0.105327 +v -0.001752 -0.015403 -0.121335 +v 0.003076 -0.015403 -0.121335 +v 0.003076 -0.015403 -0.105327 +v -0.001752 -0.015403 -0.105327 +v -0.014217 0.018198 -0.121335 +v -0.001752 0.002383 -0.121335 +v -0.001752 0.002383 -0.105327 +v -0.014217 0.018198 -0.105327 +v 0.015540 0.018198 -0.121335 +v 0.009727 0.018198 -0.121335 +v 0.009727 0.018198 -0.105327 +v 0.015540 0.018198 -0.121335 +v 0.009727 0.018198 -0.105327 +v 0.015540 0.018198 -0.105327 +v 0.003076 -0.015403 -0.121335 +v 0.003076 0.002334 -0.121335 +v 0.003076 0.002334 -0.105327 +v 0.003076 -0.015403 -0.121335 +v 0.003076 0.002334 -0.105327 +v 0.003076 -0.015403 -0.105327 +v 0.000612 0.006620 -0.121335 +v -0.008403 0.018198 -0.121335 +v -0.008403 0.018198 -0.105327 +v 0.000612 0.006620 -0.105327 +v -0.001752 0.002383 -0.121335 +v -0.001752 -0.015403 -0.121335 +v -0.001752 -0.015403 -0.105327 +v -0.001752 0.002383 -0.121335 +v -0.001752 -0.015403 -0.105327 +v -0.001752 0.002383 -0.105327 +v 0.009727 0.018198 -0.121335 +v 0.000612 0.006620 -0.121335 +v 0.000612 0.006620 -0.105327 +v 0.009727 0.018198 -0.121335 +v 0.000612 0.006620 -0.105327 +v 0.009727 0.018198 -0.105327 +v -0.008403 0.018198 -0.121335 +v -0.014217 0.018198 -0.121335 +v -0.014217 0.018198 -0.105327 +v -0.008403 0.018198 -0.121335 +v -0.014217 0.018198 -0.105327 +v -0.008403 0.018198 -0.105327 +v 0.121335 0.001989 -0.001398 +v 0.121335 0.018198 -0.014848 +v 0.121335 0.018198 -0.008985 +v 0.121335 0.005388 0.001558 +v 0.121335 0.018198 0.012052 +v 0.121335 0.018198 0.017866 +v 0.121335 0.005388 0.001558 +v 0.121335 0.018198 0.017866 +v 0.121335 0.001989 0.004465 +v 0.121335 0.001989 0.004465 +v 0.121335 -0.015403 -0.015882 +v 0.121335 -0.001509 0.001558 +v 0.121335 -0.001509 0.001558 +v 0.121335 -0.015403 0.018802 +v 0.121335 -0.015403 -0.010019 +v 0.121335 -0.015403 0.012988 +v 0.121335 -0.015403 0.018802 +v 0.104411 0.001989 -0.001398 +v 0.104411 0.018198 -0.008985 +v 0.104411 0.018198 -0.014848 +v 0.104411 0.005388 0.001558 +v 0.104411 0.005388 0.001558 +v 0.104411 0.018198 0.017866 +v 0.104411 0.018198 0.012052 +v 0.104411 0.001989 0.004465 +v 0.104411 0.018198 0.017866 +v 0.104411 -0.015403 -0.015882 +v 0.104411 -0.001509 0.001558 +v 0.104411 -0.015403 0.018802 +v 0.104411 -0.015403 -0.015882 +v 0.104411 -0.015403 -0.010019 +v 0.104411 -0.001509 0.001558 +v 0.104411 -0.015403 0.012988 +v 0.104411 -0.015403 0.018802 +v 0.121335 -0.015403 0.012988 +v 0.121335 -0.015403 0.018802 +v 0.104411 -0.015403 0.018802 +v 0.104411 -0.015403 0.012988 +v 0.121335 0.018198 -0.014848 +v 0.121335 0.001989 -0.001398 +v 0.104411 0.001989 -0.001398 +v 0.121335 0.018198 -0.014848 +v 0.104411 0.001989 -0.001398 +v 0.104411 0.018198 -0.014848 +v 0.121335 -0.015403 -0.015882 +v 0.121335 -0.015403 -0.010019 +v 0.104411 -0.015403 -0.010019 +v 0.104411 -0.015403 -0.015882 +v 0.121335 0.018198 0.017866 +v 0.121335 0.018198 0.012052 +v 0.104411 0.018198 0.012052 +v 0.104411 0.018198 0.017866 +v 0.121335 0.005388 0.001558 +v 0.121335 0.018198 -0.008985 +v 0.104411 0.018198 -0.008985 +v 0.121335 0.005388 0.001558 +v 0.104411 0.018198 -0.008985 +v 0.104411 0.005388 0.001558 +v 0.121335 0.001989 -0.001398 +v 0.121335 -0.015403 -0.015882 +v 0.104411 -0.015403 -0.015882 +v 0.121335 0.001989 -0.001398 +v 0.104411 -0.015403 -0.015882 +v 0.104411 0.001989 -0.001398 +v 0.121335 -0.015403 -0.010019 +v 0.121335 -0.001509 0.001558 +v 0.104411 -0.001509 0.001558 +v 0.121335 -0.015403 -0.010019 +v 0.104411 -0.001509 0.001558 +v 0.104411 -0.015403 -0.010019 +v 0.121335 0.018198 0.012052 +v 0.121335 0.005388 0.001558 +v 0.104411 0.005388 0.001558 +v 0.104411 0.018198 0.012052 +v 0.121335 -0.001509 0.001558 +v 0.121335 -0.015403 0.012988 +v 0.104411 -0.015403 0.012988 +v 0.104411 -0.001509 0.001558 +v 0.121335 0.018198 -0.008985 +v 0.121335 0.018198 -0.014848 +v 0.104411 0.018198 -0.014848 +v 0.104411 0.018198 -0.008985 +v 0.121335 0.001989 0.004465 +v 0.121335 0.018198 0.017866 +v 0.104411 0.018198 0.017866 +v 0.104411 0.001989 0.004465 +v 0.121335 -0.015403 0.018802 +v 0.121335 0.001989 0.004465 +v 0.104411 0.001989 0.004465 +v 0.104411 -0.015403 0.018802 +v -0.013862 0.121335 0.014442 +v -0.018198 0.121335 0.014442 +v -0.018198 0.121335 -0.014527 +v -0.013862 0.121335 0.014442 +v -0.018198 0.121335 -0.014527 +v -0.013862 0.121335 -0.005610 +v -0.013862 0.121335 -0.005610 +v -0.018198 0.121335 -0.014527 +v 0.011067 0.121335 0.006855 +v 0.015403 0.121335 0.015772 +v -0.013862 0.121335 -0.005610 +v 0.011067 0.121335 0.006855 +v 0.015403 0.121335 0.015772 +v 0.011067 0.121335 0.006855 +v 0.011067 0.121335 -0.014527 +v 0.015403 0.121335 0.015772 +v 0.011067 0.121335 -0.014527 +v 0.015403 0.121335 -0.014527 +v -0.013862 0.098929 0.014442 +v -0.018198 0.098929 -0.014527 +v -0.018198 0.098929 0.014442 +v -0.013862 0.098929 0.014442 +v -0.013862 0.098929 -0.005610 +v -0.018198 0.098929 -0.014527 +v -0.013862 0.098929 -0.005610 +v 0.011067 0.098929 0.006854 +v -0.018198 0.098929 -0.014527 +v 0.015403 0.098929 0.015772 +v 0.011067 0.098929 0.006854 +v -0.013862 0.098929 -0.005610 +v 0.015403 0.098929 0.015772 +v 0.011067 0.098929 -0.014527 +v 0.011067 0.098929 0.006854 +v 0.015403 0.098929 0.015772 +v 0.015403 0.098929 -0.014527 +v 0.011067 0.098929 -0.014527 +v -0.018198 0.121335 -0.014527 +v -0.018198 0.121335 0.014442 +v -0.018198 0.098929 0.014442 +v -0.018198 0.121335 -0.014527 +v -0.018198 0.098929 0.014442 +v -0.018198 0.098929 -0.014527 +v 0.015403 0.121335 0.015772 +v 0.015403 0.121335 -0.014527 +v 0.015403 0.098929 -0.014527 +v 0.015403 0.121335 0.015772 +v 0.015403 0.098929 -0.014527 +v 0.015403 0.098929 0.015772 +v -0.018198 0.121335 0.014442 +v -0.013862 0.121335 0.014442 +v -0.013862 0.098929 0.014442 +v -0.018198 0.121335 0.014442 +v -0.013862 0.098929 0.014442 +v -0.018198 0.098929 0.014442 +v -0.013862 0.121335 -0.005610 +v 0.015403 0.121335 0.015772 +v 0.015403 0.098929 0.015772 +v -0.013862 0.121335 -0.005610 +v 0.015403 0.098929 0.015772 +v -0.013862 0.098929 -0.005610 +v -0.013862 0.121335 0.014442 +v -0.013862 0.121335 -0.005610 +v -0.013862 0.098929 -0.005610 +v -0.013862 0.121335 0.014442 +v -0.013862 0.098929 -0.005610 +v -0.013862 0.098929 0.014442 +v 0.015403 0.121335 -0.014527 +v 0.011067 0.121335 -0.014527 +v 0.011067 0.098929 -0.014527 +v 0.015403 0.121335 -0.014527 +v 0.011067 0.098929 -0.014527 +v 0.015403 0.098929 -0.014527 +v 0.011067 0.121335 -0.014527 +v 0.011067 0.121335 0.006855 +v 0.011067 0.098929 0.006854 +v 0.011067 0.121335 -0.014527 +v 0.011067 0.098929 0.006854 +v 0.011067 0.098929 -0.014527 +v 0.011067 0.121335 0.006855 +v -0.018198 0.121335 -0.014527 +v -0.018198 0.098929 -0.014527 +v 0.011067 0.121335 0.006855 +v -0.018198 0.098929 -0.014527 +v 0.011067 0.098929 0.006854 +vt 0.877679 0.999989 +vt 0.704361 0.862110 +vt 0.623368 0.995214 +vt 0.549066 0.593772 +vt 0.632255 0.405504 +vt 0.427163 0.450286 +vt 0.590790 0.226030 +vt 0.503462 0.345266 +vt 0.575617 0.338540 +vt 0.634145 0.279193 +vt 0.701116 0.660331 +vt 0.935436 0.738186 +vt 0.886693 0.651541 +vt 0.824282 0.608270 +vt 0.935436 0.738186 +vt 0.701116 0.660331 +vt 0.704361 0.862110 +vt 0.877679 0.999989 +vt 0.427163 0.450286 +vt 0.264037 0.546895 +vt 0.357144 0.682415 +vt 0.549066 0.593772 +vt 0.503462 0.345266 +vt 0.590790 0.226030 +vt 0.517346 0.232950 +vt 0.465179 0.299295 +vt 0.634145 0.279193 +vt 0.575617 0.338540 +vt 0.811297 0.524319 +vt 0.634145 0.279193 +vt 0.811297 0.524319 +vt 0.815519 0.389933 +vt 0.730237 0.173993 +vt 0.711091 0.223332 +vt 0.134165 0.525760 +vt 0.264037 0.546895 +vt 0.257518 0.467648 +vt 0.170177 0.468780 +vt 0.427163 0.450286 +vt 0.343750 0.438493 +vt 0.221377 0.611101 +vt 0.148490 0.590709 +vt 0.028295 0.807064 +vt 0.221377 0.611101 +vt 0.028295 0.807064 +vt 0.123704 0.829921 +vt 0.357144 0.682415 +vt 0.264037 0.546895 +vt 0.221377 0.611101 +vt 0.281763 0.683253 +vt 0.770178 0.550866 +vt 0.792445 0.563409 +vt 0.134165 0.525760 +vt 0.148490 0.590709 +vt 0.637580 0.876969 +vt 0.644000 0.708063 +vt 0.442150 0.778258 +vt 0.637580 0.876969 +vt 0.442150 0.778258 +vt 0.412653 0.876915 +vt 0.355897 0.400451 +vt 0.390530 0.348321 +vt 0.623368 0.995214 +vt 0.704361 0.862110 +vt 0.637580 0.876969 +vt 0.598416 0.970365 +vt 0.701116 0.660331 +vt 0.644000 0.708063 +vt 0.277279 0.702623 +vt 0.301656 0.977486 +vt 0.412653 0.876915 +vt 0.442150 0.778258 +vt 0.465179 0.299295 +vt 0.517346 0.232950 +vt 0.338533 0.075582 +vt 0.465179 0.299295 +vt 0.338533 0.075582 +vt 0.261104 0.194327 +vt 0.390530 0.348321 +vt 0.465179 0.299295 +vt 0.261104 0.194327 +vt 0.644000 0.708063 +vt 0.277279 0.702623 +vt 0.442150 0.778258 +vt 0.598416 0.970365 +vt 0.637580 0.876969 +vt 0.412653 0.876915 +vt 0.598416 0.970365 +vt 0.412653 0.876915 +vt 0.301656 0.977486 +vt 0.123704 0.829921 +vt 0.028295 0.807064 +vt 0.000207 0.999902 +vt 0.179879 0.922875 +vt 0.824282 0.608270 +vt 0.886693 0.651541 +vt 0.999978 0.528948 +vt 0.824282 0.608270 +vt 0.999978 0.528948 +vt 0.955080 0.461952 +vt 0.281763 0.683253 +vt 0.221377 0.611101 +vt 0.123704 0.829921 +vt 0.281763 0.683253 +vt 0.123704 0.829921 +vt 0.179879 0.922875 +vt 0.792445 0.563409 +vt 0.824282 0.608270 +vt 0.955080 0.461952 +vt 0.792445 0.563409 +vt 0.955080 0.461952 +vt 0.922398 0.350776 +vt 0.923406 0.337617 +vt 0.815519 0.389933 +vt 0.811297 0.524319 +vt 0.296472 0.242822 +vt 0.016945 0.256359 +vt 0.190705 0.312874 +vt 0.711091 0.223332 +vt 0.634145 0.279193 +vt 0.815519 0.389933 +vt 0.711091 0.223332 +vt 0.815519 0.389933 +vt 0.923406 0.337617 +vt 0.170177 0.468780 +vt 0.257518 0.467648 +vt 0.190705 0.312874 +vt 0.170177 0.468780 +vt 0.190705 0.312874 +vt 0.016945 0.256359 +vt 0.257518 0.467648 +vt 0.343750 0.438493 +vt 0.296472 0.242822 +vt 0.257518 0.467648 +vt 0.296472 0.242822 +vt 0.190705 0.312874 +vt 0.164647 0.206490 +vt 0.137409 0.237522 +vt 0.148149 0.246926 +vt 0.879209 0.162218 +vt 0.899789 0.136235 +vt 0.866618 0.153517 +vt 0.833631 0.180137 +vt 0.834320 0.192690 +vt 0.873454 0.165845 +vt 0.630495 0.206194 +vt 0.664214 0.194627 +vt 0.663560 0.184439 +vt 0.626594 0.192788 +vt 0.837231 0.112168 +vt 0.876068 0.106100 +vt 0.884365 0.090995 +vt 0.841157 0.123918 +vt 0.665576 0.126268 +vt 0.701825 0.140263 +vt 0.706253 0.126675 +vt 0.660674 0.137415 +vt 0.651633 0.130906 +vt 0.114167 0.149049 +vt 0.100911 0.183177 +vt 0.110940 0.184971 +vt 0.947862 0.768248 +vt 0.959170 0.761371 +vt 0.952077 0.721815 +vt 0.663560 0.184439 +vt 0.728611 0.170526 +vt 0.701825 0.140263 +vt 0.660674 0.137415 +vt 0.993131 0.781178 +vt 0.997378 0.766008 +vt 0.959170 0.761371 +vt 0.947862 0.768248 +vt 0.137409 0.237522 +vt 0.164647 0.206490 +vt 0.110940 0.184971 +vt 0.100277 0.249077 +vt 0.834320 0.192690 +vt 0.833631 0.180137 +vt 0.803714 0.208463 +vt 0.728611 0.170526 +vt 0.706253 0.126675 +vt 0.701825 0.140263 +vt 0.626594 0.192788 +vt 0.663560 0.184439 +vt 0.660674 0.137415 +vt 0.997378 0.766008 +vt 0.952077 0.721815 +vt 0.959170 0.761371 +vt 0.866618 0.153517 +vt 0.899789 0.136235 +vt 0.876068 0.106100 +vt 0.841157 0.123918 +vt 0.987367 0.825299 +vt 0.993131 0.781178 +vt 0.947862 0.768248 +vt 0.164647 0.206490 +vt 0.114167 0.149049 +vt 0.110940 0.184971 +vt 0.833631 0.180137 +vt 0.866618 0.153517 +vt 0.841157 0.123918 +vt 0.706666 0.085370 +vt 0.665576 0.126268 +vt 0.706253 0.126675 +vt 0.148149 0.246926 +vt 0.137409 0.237522 +vt 0.100277 0.249077 +vt 0.899789 0.136235 +vt 0.884365 0.090995 +vt 0.876068 0.106100 +vt 0.901844 0.186326 +vt 0.849583 0.192142 +vt 0.847732 0.210709 +vt 0.877454 0.230132 +vt 0.836064 0.250561 +vt 0.809560 0.270587 +vt 0.607617 0.129696 +vt 0.614474 0.075690 +vt 0.597435 0.137391 +vt 0.893705 0.253191 +vt 0.946146 0.202975 +vt 0.903446 0.248533 +vt 0.608804 0.139682 +vt 0.621541 0.196470 +vt 0.946562 0.220339 +vt 0.947342 0.347581 +vt 0.947818 0.371736 +vt 0.534464 0.174384 +vt 0.482487 0.138172 +vt 0.475490 0.146348 +vt 0.538361 0.155457 +vt 0.819571 0.227383 +vt 0.765189 0.248073 +vt 0.798662 0.247500 +vt 0.551825 0.161264 +vt 0.573868 0.099577 +vt 0.522006 0.217440 +vt 0.547894 0.171843 +vt 0.588497 0.222780 +vt 0.999982 0.156817 +vt 0.984586 0.205234 +vt 0.981163 0.309426 +vt 0.975856 0.340212 +vt 0.993906 0.360793 +vt 0.947342 0.347581 +vt 0.947818 0.371736 +vt 0.993906 0.360793 +vt 0.975856 0.340212 +vt 0.849583 0.192142 +vt 0.901844 0.186326 +vt 0.913311 0.138870 +vt 0.441102 0.160191 +vt 0.534464 0.174384 +vt 0.475490 0.146348 +vt 0.946146 0.202975 +vt 0.946562 0.220339 +vt 0.984586 0.205234 +vt 0.999982 0.156817 +vt 0.809560 0.270587 +vt 0.836064 0.250561 +vt 0.798662 0.247500 +vt 0.765189 0.248073 +vt 0.877454 0.230132 +vt 0.847732 0.210709 +vt 0.799992 0.211497 +vt 0.877454 0.230132 +vt 0.799992 0.211497 +vt 0.819571 0.227383 +vt 0.901844 0.186326 +vt 0.946146 0.202975 +vt 0.999982 0.156817 +vt 0.901844 0.186326 +vt 0.999982 0.156817 +vt 0.913311 0.138870 +vt 0.946562 0.220339 +vt 0.903446 0.248533 +vt 0.981163 0.309426 +vt 0.946562 0.220339 +vt 0.981163 0.309426 +vt 0.984586 0.205234 +vt 0.836064 0.250561 +vt 0.877454 0.230132 +vt 0.819571 0.227383 +vt 0.798662 0.247500 +vt 0.903446 0.248533 +vt 0.947342 0.347581 +vt 0.975856 0.340212 +vt 0.981163 0.309426 +vt 0.431246 0.134617 +vt 0.441102 0.160191 +vt 0.475490 0.146348 +vt 0.482487 0.138172 +vt 0.597435 0.137391 +vt 0.614474 0.075690 +vt 0.573868 0.099577 +vt 0.551825 0.161264 +vt 0.621541 0.196470 +vt 0.597435 0.137391 +vt 0.551825 0.161264 +vt 0.588497 0.222780 +vt 0.025660 0.167277 +vt 0.019460 0.184500 +vt 0.085457 0.197038 +vt 0.025660 0.167277 +vt 0.085457 0.197038 +vt 0.070813 0.179118 +vt 0.244895 0.177499 +vt 0.271388 0.170697 +vt 0.200499 0.139389 +vt 0.182349 0.154977 +vt 0.244895 0.177499 +vt 0.200499 0.139389 +vt 0.182349 0.154977 +vt 0.200499 0.139389 +vt 0.172554 0.101012 +vt 0.950882 0.695534 +vt 0.997250 0.630519 +vt 0.985549 0.630401 +vt 0.968010 0.702894 +vt 0.995974 0.639378 +vt 0.953135 0.699966 +vt 0.968010 0.702894 +vt 0.998149 0.664236 +vt 0.995974 0.639378 +vt 0.294567 0.089810 +vt 0.232375 0.081405 +vt 0.303833 0.115587 +vt 0.172240 0.213102 +vt 0.181083 0.238508 +vt 0.257933 0.241733 +vt 0.180450 0.981100 +vt 0.109542 0.971169 +vt 0.153568 0.987533 +vt 0.180450 0.981100 +vt 0.111298 0.956400 +vt 0.109542 0.971169 +vt 0.085457 0.197038 +vt 0.019460 0.184500 +vt 0.016441 0.244366 +vt 0.085457 0.197038 +vt 0.016441 0.244366 +vt 0.092094 0.249976 +vt 0.950882 0.695534 +vt 0.985549 0.630401 +vt 0.947422 0.591836 +vt 0.184488 0.927119 +vt 0.111298 0.956400 +vt 0.180450 0.981100 +vt 0.985162 0.748387 +vt 0.999011 0.739548 +vt 0.968010 0.702894 +vt 0.985162 0.748387 +vt 0.968010 0.702894 +vt 0.953135 0.699966 +vt 0.244895 0.177499 +vt 0.182349 0.154977 +vt 0.172240 0.213102 +vt 0.244895 0.177499 +vt 0.172240 0.213102 +vt 0.257933 0.241733 +vt 0.025660 0.167277 +vt 0.070813 0.179118 +vt 0.070774 0.128416 +vt 0.999011 0.739548 +vt 0.998149 0.664236 +vt 0.968010 0.702894 +vt 0.985549 0.630401 +vt 0.997250 0.630519 +vt 0.959294 0.584016 +vt 0.985549 0.630401 +vt 0.959294 0.584016 +vt 0.947422 0.591836 +vt 0.172554 0.101012 +vt 0.200499 0.139389 +vt 0.232375 0.081405 +vt 0.997250 0.630519 +vt 0.999791 0.545216 +vt 0.959294 0.584016 +vt 0.200499 0.139389 +vt 0.271388 0.170697 +vt 0.303833 0.115587 +vt 0.200499 0.139389 +vt 0.303833 0.115587 +vt 0.232375 0.081405 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 -0.0000 0.0000 +vn 0.0000 -0.0000 1.0000 +vn 0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 0.0000 +vn 0.0000 0.9888 0.1491 +vn -0.1491 0.0000 -0.9888 +vn 0.0000 -0.1491 0.9888 +vn 0.0000 -0.1491 -0.9888 +vn 0.9888 -0.1491 0.0000 +vn -0.9888 -0.1491 0.0000 +vn -0.1491 -0.0000 0.9888 +vn -0.1491 -0.9888 -0.0000 +vn -0.1491 0.9888 0.0000 +vn -0.9888 0.0000 0.1491 +vn 0.9888 0.0000 0.1491 +vn 0.0000 -0.9888 0.1491 +vn 0.7863 -0.6178 -0.0000 +vn -0.7854 -0.6190 -0.0000 +vn 0.7890 0.6144 0.0000 +vn -0.7857 0.6186 0.0000 +vn 0.0000 -0.6386 -0.7696 +vn 0.0000 0.6355 0.7721 +vn 0.0000 0.6400 -0.7684 +vn 0.0000 -0.6402 0.7682 +vn 0.0000 0.6337 -0.7736 +vn 0.0000 -0.6353 -0.7722 +vn 0.0000 -0.6372 0.7707 +vn 0.0000 0.6361 0.7716 +vn 0.5899 0.0000 -0.8074 +vn -0.5899 0.0000 0.8074 +s 1 +f 1/1/1 2/2/1 3/3/1 +f 4/4/1 5/5/1 6/6/1 +f 7/7/2 8/8/2 9/9/2 +f 7/7/2 9/9/2 10/10/2 +f 11/11/3 12/12/3 13/13/3 +f 11/11/3 13/13/3 14/14/3 +f 15/15/4 16/16/4 17/17/4 +f 15/15/4 17/17/4 18/18/4 +f 19/19/5 20/20/5 21/21/5 +f 19/19/5 21/21/5 22/22/5 +f 23/23/6 24/24/6 25/25/6 +f 23/23/6 25/25/6 26/26/6 +f 27/27/7 28/28/7 29/29/7 +f 30/30/7 31/31/7 32/32/7 +f 33/33/2 7/7/2 10/10/2 +f 33/33/2 10/10/2 34/34/2 +f 35/35/2 36/36/2 37/37/2 +f 35/35/2 37/37/2 38/38/2 +f 36/36/2 39/39/2 40/40/2 +f 36/36/2 40/40/2 37/37/2 +f 41/41/8 42/42/8 43/43/8 +f 44/44/8 45/45/8 46/46/8 +f 47/47/3 48/48/3 49/49/3 +f 47/47/3 49/49/3 50/50/3 +f 51/51/3 11/11/3 14/14/3 +f 51/51/3 14/14/3 52/52/3 +f 48/48/3 53/53/3 54/54/3 +f 48/48/3 54/54/3 49/49/3 +f 55/55/9 56/56/9 57/57/9 +f 58/58/9 59/59/9 60/60/9 +f 61/61/6 23/23/6 26/26/6 +f 61/61/6 26/26/6 62/62/6 +f 63/63/6 64/64/6 65/65/6 +f 63/63/6 65/65/6 66/66/6 +f 64/64/6 67/67/6 68/68/6 +f 64/64/6 68/68/6 65/65/6 +f 69/69/6 70/70/6 71/71/6 +f 69/69/6 71/71/6 72/72/6 +f 73/73/10 74/74/10 75/75/10 +f 76/76/10 77/77/10 78/78/10 +f 79/79/11 80/80/11 81/81/11 +f 82/82/11 83/83/11 84/84/11 +f 85/85/12 86/86/12 87/87/12 +f 88/88/12 89/89/12 90/90/12 +f 91/91/3 92/92/3 93/93/3 +f 91/91/3 93/93/3 94/94/3 +f 95/95/13 96/96/13 97/97/13 +f 98/98/13 99/99/13 100/100/13 +f 101/101/14 102/102/14 103/103/14 +f 104/104/14 105/105/14 106/106/14 +f 107/107/15 108/108/15 109/109/15 +f 110/110/15 111/111/15 112/112/15 +f 113/113/2 114/114/2 115/115/2 +f 116/116/2 117/117/2 118/118/2 +f 119/119/16 120/120/16 121/121/16 +f 122/122/16 123/123/16 124/124/16 +f 125/125/17 126/126/17 127/127/17 +f 128/128/17 129/129/17 130/130/17 +f 131/131/18 132/132/18 133/133/18 +f 134/134/18 135/135/18 136/136/18 +f 137/137/2 138/138/2 139/139/2 +f 140/140/2 141/141/2 142/142/2 +f 142/142/2 143/143/2 144/144/2 +f 142/142/2 144/144/2 145/145/2 +f 140/140/2 142/142/2 145/145/2 +f 146/146/2 147/147/2 148/148/2 +f 146/146/2 148/148/2 149/149/2 +f 150/150/4 151/151/4 152/152/4 +f 150/150/4 153/153/4 151/151/4 +f 154/154/4 155/155/4 156/156/4 +f 154/154/4 157/157/4 155/155/4 +f 158/158/4 157/157/4 154/154/4 +f 159/159/4 160/160/4 161/161/4 +f 162/162/4 163/163/4 164/164/4 +f 165/165/19 166/166/19 167/167/19 +f 165/165/19 167/167/19 168/168/19 +f 169/169/5 170/170/5 171/171/5 +f 169/169/5 171/171/5 172/172/5 +f 173/173/20 174/174/20 175/175/20 +f 173/173/20 175/175/20 176/176/20 +f 177/177/6 178/178/6 179/179/6 +f 180/180/6 181/181/6 182/182/6 +f 183/183/3 184/184/3 185/185/3 +f 186/186/3 187/187/3 188/188/3 +f 189/189/21 190/190/21 191/191/21 +f 189/189/21 191/191/21 192/192/21 +f 193/193/1 194/194/1 195/195/1 +f 196/196/1 197/197/1 198/198/1 +f 199/199/22 200/200/22 201/201/22 +f 202/202/22 203/203/22 204/204/22 +f 205/205/6 206/206/6 207/207/6 +f 208/208/6 209/209/6 210/210/6 +f 211/211/3 212/212/3 213/213/3 +f 211/211/3 213/213/3 214/214/3 +f 214/214/3 215/215/3 216/216/3 +f 217/217/3 218/218/3 219/219/3 +f 211/211/3 214/214/3 220/220/3 +f 221/221/3 211/211/3 220/220/3 +f 221/221/3 220/220/3 222/222/3 +f 223/223/3 219/219/3 224/224/3 +f 221/221/3 222/222/3 225/225/3 +f 226/226/3 222/222/3 227/227/3 +f 228/228/1 229/229/1 230/230/1 +f 228/228/1 231/231/1 229/229/1 +f 232/232/1 233/233/1 234/234/1 +f 231/231/1 235/235/1 236/236/1 +f 228/228/1 235/235/1 231/231/1 +f 237/237/1 235/235/1 228/228/1 +f 237/237/1 238/238/1 235/235/1 +f 238/238/1 239/239/1 235/235/1 +f 240/240/1 241/241/1 242/242/1 +f 243/243/1 244/244/1 242/242/1 +f 245/245/5 246/246/5 247/247/5 +f 245/245/5 247/247/5 248/248/5 +f 249/249/23 250/250/23 251/251/23 +f 252/252/23 253/253/23 254/254/23 +f 255/255/5 256/256/5 257/257/5 +f 255/255/5 257/257/5 258/258/5 +f 259/259/6 260/260/6 261/261/6 +f 259/259/6 261/261/6 262/262/6 +f 263/263/24 264/264/24 265/265/24 +f 266/266/24 267/267/24 268/268/24 +f 269/269/25 270/270/25 271/271/25 +f 272/272/25 273/273/25 274/274/25 +f 275/275/26 276/276/26 277/277/26 +f 278/278/26 279/279/26 280/280/26 +f 281/281/27 282/282/27 283/283/27 +f 281/281/27 283/283/27 284/284/27 +f 285/285/28 286/286/28 287/287/28 +f 285/285/28 287/287/28 288/288/28 +f 289/289/6 290/290/6 291/291/6 +f 289/289/6 291/291/6 292/292/6 +f 293/293/29 294/294/29 295/295/29 +f 293/293/29 295/295/29 296/296/29 +f 297/297/30 298/298/30 299/299/30 +f 297/297/30 299/299/30 300/300/30 +f 301/301/5 302/302/5 303/303/5 +f 304/304/5 305/305/5 306/306/5 +f 307/307/5 308/308/5 309/309/5 +f 310/310/5 311/311/5 312/312/5 +f 313/313/5 314/314/5 315/315/5 +f 316/316/5 317/317/5 318/318/5 +f 319/319/6 320/320/6 321/321/6 +f 322/322/6 323/323/6 324/324/6 +f 325/325/6 326/326/6 327/327/6 +f 328/328/6 329/329/6 330/330/6 +f 331/331/6 332/332/6 333/333/6 +f 334/334/6 335/335/6 336/336/6 +f 337/337/3 338/338/3 339/339/3 +f 340/340/3 341/341/3 342/342/3 +f 343/343/1 344/344/1 345/345/1 +f 346/346/1 347/347/1 348/348/1 +f 349/349/2 350/350/2 351/351/2 +f 352/352/2 353/353/2 354/354/2 +f 355/355/31 356/356/31 357/357/31 +f 358/358/31 359/359/31 360/360/31 +f 361/361/1 362/362/1 363/363/1 +f 364/364/1 365/365/1 366/366/1 +f 367/367/4 368/368/4 369/369/4 +f 370/370/4 371/371/4 372/372/4 +f 373/373/3 374/374/3 375/375/3 +f 376/376/3 377/377/3 378/378/3 +f 379/379/32 380/380/32 381/381/32 +f 382/382/32 383/383/32 384/384/32 diff --git a/driver/slimevr/resources/rendermodels/example_controller/example_controller.png b/driver/slimevr/resources/rendermodels/example_controller/example_controller.png new file mode 100644 index 0000000..246ef8f Binary files /dev/null and b/driver/slimevr/resources/rendermodels/example_controller/example_controller.png differ diff --git a/libraries/linalg b/libraries/linalg new file mode 160000 index 0000000..a3e87da --- /dev/null +++ b/libraries/linalg @@ -0,0 +1 @@ +Subproject commit a3e87da35e32b781a4b6c01cdd5efbe7ae51c737 diff --git a/src/ControllerDevice.cpp b/src/ControllerDevice.cpp new file mode 100644 index 0000000..474bbb8 --- /dev/null +++ b/src/ControllerDevice.cpp @@ -0,0 +1,212 @@ +#include "ControllerDevice.hpp" +#include + +SlimeVRDriver::ControllerDevice::ControllerDevice(std::string serial, ControllerDevice::Handedness handedness): + serial_(serial), + handedness_(handedness) +{ +} + +std::string SlimeVRDriver::ControllerDevice::GetSerial() +{ + return this->serial_; +} + +void SlimeVRDriver::ControllerDevice::Update() +{ + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Check if this device was asked to be identified + auto events = GetDriver()->GetOpenVREvents(); + for (auto event : events) { + // Note here, event.trackedDeviceIndex does not necissarily equal this->device_index_, not sure why, but the component handle will match so we can just use that instead + //if (event.trackedDeviceIndex == this->device_index_) { + if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { + if (event.data.hapticVibration.componentHandle == this->haptic_component_) { + this->did_vibrate_ = true; + } + } + //} + } + + // Check if we need to keep vibrating + if (this->did_vibrate_) { + this->vibrate_anim_state_ += (GetDriver()->GetLastFrameTime().count()/1000.f); + if (this->vibrate_anim_state_ > 1.0f) { + this->did_vibrate_ = false; + this->vibrate_anim_state_ = 0.0f; + } + } + + // Setup pose for this frame + auto pose = IVRDevice::MakeDefaultPose(); + + // Find a HMD + auto devices = GetDriver()->GetDevices(); + auto hmd = std::find_if(devices.begin(), devices.end(), [](const std::shared_ptr& device_ptr) {return device_ptr->GetDeviceType() == DeviceType::HMD; }); + if (hmd != devices.end()) { + // Found a HMD + vr::DriverPose_t hmd_pose = (*hmd)->GetPose(); + + // Here we setup some transforms so our controllers are offset from the headset by a small amount so we can see them + linalg::vec hmd_position{ (float)hmd_pose.vecPosition[0], (float)hmd_pose.vecPosition[1], (float)hmd_pose.vecPosition[2] }; + linalg::vec hmd_rotation{ (float)hmd_pose.qRotation.x, (float)hmd_pose.qRotation.y, (float)hmd_pose.qRotation.z, (float)hmd_pose.qRotation.w }; + + // Do shaking animation if haptic vibration was requested + float controller_y = -0.2f + 0.01f * std::sinf(8 * 3.1415f * vibrate_anim_state_); + + // Left hand controller on the left, right hand controller on the right, any other handedness sticks to the middle + float controller_x = this->handedness_ == Handedness::LEFT ? -0.2f : (this->handedness_ == Handedness::RIGHT ? 0.2f : 0.f); + + linalg::vec hmd_pose_offset = { controller_x, controller_y, -0.5f }; + + hmd_pose_offset = linalg::qrot(hmd_rotation, hmd_pose_offset); + + linalg::vec final_pose = hmd_pose_offset + hmd_position; + + pose.vecPosition[0] = final_pose.x; + pose.vecPosition[1] = final_pose.y; + pose.vecPosition[2] = final_pose.z; + + pose.qRotation.w = hmd_rotation.w; + pose.qRotation.x = hmd_rotation.x; + pose.qRotation.y = hmd_rotation.y; + pose.qRotation.z = hmd_rotation.z; + } + + // Check if we need to press any buttons (I am only hooking up the A button here but the process is the same for the others) + // You will still need to go into the games button bindings and hook up each one (ie. a to left click, b to right click, etc.) for them to work properly + if (GetAsyncKeyState(0x45 /* E */) != 0) { + GetDriver()->GetInput()->UpdateBooleanComponent(this->a_button_click_component_, true, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->a_button_touch_component_, true, 0); + } + else { + GetDriver()->GetInput()->UpdateBooleanComponent(this->a_button_click_component_, false, 0); + GetDriver()->GetInput()->UpdateBooleanComponent(this->a_button_touch_component_, false, 0); + } + + // Post pose + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(this->device_index_, pose, sizeof(vr::DriverPose_t)); + this->last_pose_ = pose; +} + +DeviceType SlimeVRDriver::ControllerDevice::GetDeviceType() +{ + return DeviceType::CONTROLLER; +} + +SlimeVRDriver::ControllerDevice::Handedness SlimeVRDriver::ControllerDevice::GetHandedness() +{ + return this->handedness_; +} + +vr::TrackedDeviceIndex_t SlimeVRDriver::ControllerDevice::GetDeviceIndex() +{ + return this->device_index_; +} + +vr::EVRInitError SlimeVRDriver::ControllerDevice::Activate(uint32_t unObjectId) +{ + this->device_index_ = unObjectId; + + GetDriver()->Log("Activating controller " + this->serial_); + + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + + // Setup inputs and outputs + GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &this->haptic_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/click", &this->a_button_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/a/touch", &this->a_button_touch_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/click", &this->b_button_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/b/touch", &this->b_button_touch_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/click", &this->trigger_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trigger/touch", &this->trigger_touch_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trigger/value", &this->trigger_value_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedOneSided); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/grip/touch", &this->grip_touch_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/value", &this->grip_value_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedOneSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/grip/force", &this->grip_force_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedOneSided); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/touch", &this->system_touch_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/click", &this->trackpad_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/trackpad/touch", &this->trackpad_touch_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/x", &this->trackpad_x_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/trackpad/y", &this->trackpad_y_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedTwoSided); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/click", &this->joystick_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/joystick/touch", &this->joystick_touch_component_); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/x", &this->joystick_x_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedTwoSided); + GetDriver()->GetInput()->CreateScalarComponent(props, "/input/joystick/y", &this->joystick_y_component_, vr::EVRScalarType::VRScalarType_Absolute, vr::EVRScalarUnits::VRScalarUnits_NormalizedTwoSided); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 2); + + // Set up a model "number" (not needed but good to have) + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "example_controller"); + + // Set up a render model path + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "{example}example_controller"); + + // Give SteamVR a hint at what hand this controller is for + if (this->handedness_ == Handedness::LEFT) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_LeftHand); + } + else if (this->handedness_ == Handedness::RIGHT) { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_RightHand); + } + else { + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + } + + // Set controller profile + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{example}/input/example_controller_bindings.json"); + + // Change the icon depending on which handedness this controller is using (ANY uses right) + std::string controller_handedness_str = this->handedness_ == Handedness::LEFT ? "left" : "right"; + std::string controller_ready_file = "{example}/icons/controller_ready_" + controller_handedness_str + ".png"; + std::string controller_not_ready_file = "{example}/icons/controller_not_ready_" + controller_handedness_str + ".png"; + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, controller_ready_file.c_str()); + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, controller_not_ready_file.c_str()); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, controller_not_ready_file.c_str()); + + return vr::EVRInitError::VRInitError_None; +} + +void SlimeVRDriver::ControllerDevice::Deactivate() +{ + this->device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + +void SlimeVRDriver::ControllerDevice::EnterStandby() +{ +} + +void* SlimeVRDriver::ControllerDevice::GetComponent(const char* pchComponentNameAndVersion) +{ + return nullptr; +} + +void SlimeVRDriver::ControllerDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) +{ + if (unResponseBufferSize >= 1) + pchResponseBuffer[0] = 0; +} + +vr::DriverPose_t SlimeVRDriver::ControllerDevice::GetPose() +{ + return last_pose_; +} diff --git a/src/ControllerDevice.hpp b/src/ControllerDevice.hpp new file mode 100644 index 0000000..eb558ce --- /dev/null +++ b/src/ControllerDevice.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace SlimeVRDriver { + class ControllerDevice : public IVRDevice { + public: + + enum class Handedness { + LEFT, + RIGHT, + ANY + }; + + ControllerDevice(std::string serial, Handedness handedness = Handedness::ANY); + ~ControllerDevice() = default; + + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + virtual Handedness GetHandedness(); + + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void* GetComponent(const char* pchComponentNameAndVersion) override; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + + private: + vr::TrackedDeviceIndex_t device_index_ = vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + Handedness handedness_; + + vr::DriverPose_t last_pose_; + + bool did_vibrate_ = false; + float vibrate_anim_state_ = 0.f; + + vr::VRInputComponentHandle_t haptic_component_ = 0; + + vr::VRInputComponentHandle_t a_button_click_component_ = 0; + vr::VRInputComponentHandle_t a_button_touch_component_ = 0; + + vr::VRInputComponentHandle_t b_button_click_component_ = 0; + vr::VRInputComponentHandle_t b_button_touch_component_ = 0; + + vr::VRInputComponentHandle_t trigger_value_component_ = 0; + vr::VRInputComponentHandle_t trigger_click_component_ = 0; + vr::VRInputComponentHandle_t trigger_touch_component_ = 0; + + vr::VRInputComponentHandle_t grip_touch_component_ = 0; + vr::VRInputComponentHandle_t grip_value_component_ = 0; + vr::VRInputComponentHandle_t grip_force_component_ = 0; + + vr::VRInputComponentHandle_t system_click_component_ = 0; + vr::VRInputComponentHandle_t system_touch_component_ = 0; + + + vr::VRInputComponentHandle_t trackpad_click_component_ = 0; + vr::VRInputComponentHandle_t trackpad_touch_component_ = 0; + vr::VRInputComponentHandle_t trackpad_x_component_ = 0; + vr::VRInputComponentHandle_t trackpad_y_component_ = 0; + + vr::VRInputComponentHandle_t joystick_click_component_ = 0; + vr::VRInputComponentHandle_t joystick_touch_component_ = 0; + vr::VRInputComponentHandle_t joystick_x_component_ = 0; + vr::VRInputComponentHandle_t joystick_y_component_ = 0; + + //vr::VRInputComponentHandle_t skeleton_left_component_ = 0; + //vr::VRInputComponentHandle_t skeleton_right_component_ = 0; + }; +}; \ No newline at end of file diff --git a/src/DeviceType.hpp b/src/DeviceType.hpp new file mode 100644 index 0000000..6ed83ac --- /dev/null +++ b/src/DeviceType.hpp @@ -0,0 +1,7 @@ +#pragma once +enum class DeviceType { + HMD, + CONTROLLER, + TRACKER, + TRACKING_REFERENCE +}; \ No newline at end of file diff --git a/src/DriverFactory.cpp b/src/DriverFactory.cpp new file mode 100644 index 0000000..deb073f --- /dev/null +++ b/src/DriverFactory.cpp @@ -0,0 +1,27 @@ +#include "DriverFactory.hpp" +#include +#include +#include +#include + +static std::shared_ptr driver; + +void* HmdDriverFactory(const char* interface_name, int* return_code) { + if (std::strcmp(interface_name, vr::IServerTrackedDeviceProvider_Version) == 0) { + if (!driver) { + // Instantiate concrete impl + driver = std::make_shared(); + } + // We always have at least 1 ref to the shared ptr in "driver" so passing out raw pointer is ok + return driver.get(); + } + + if (return_code) + *return_code = vr::VRInitError_Init_InterfaceNotFound; + + return nullptr; +} + +std::shared_ptr SlimeVRDriver::GetDriver() { + return driver; +} diff --git a/src/DriverFactory.hpp b/src/DriverFactory.hpp new file mode 100644 index 0000000..f4103d7 --- /dev/null +++ b/src/DriverFactory.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include + +#include + +extern "C" __declspec(dllexport) void* HmdDriverFactory(const char* interface_name, int* return_code); + +namespace SlimeVRDriver { + std::shared_ptr GetDriver(); +} \ No newline at end of file diff --git a/src/HMDDevice.cpp b/src/HMDDevice.cpp new file mode 100644 index 0000000..7da4d6e --- /dev/null +++ b/src/HMDDevice.cpp @@ -0,0 +1,223 @@ +#include "HMDDevice.hpp" +#include + +SlimeVRDriver::HMDDevice::HMDDevice(std::string serial):serial_(serial) +{ +} + +std::string SlimeVRDriver::HMDDevice::GetSerial() +{ + return this->serial_; +} + +void SlimeVRDriver::HMDDevice::Update() +{ + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Setup pose for this frame + auto pose = IVRDevice::MakeDefaultPose(); + + float delta_seconds = GetDriver()->GetLastFrameTime().count() / 1000.0f; + + // Get orientation + this->rot_y_ += (1.0f * (GetAsyncKeyState(VK_RIGHT) == 0) - 1.0f * (GetAsyncKeyState(VK_LEFT) == 0)) * delta_seconds; + this->rot_x_ += (-1.0f * (GetAsyncKeyState(VK_UP) == 0) + 1.0f * (GetAsyncKeyState(VK_DOWN) == 0)) * delta_seconds; + this->rot_x_ = std::fmax(this->rot_x_, -3.14159f/2); + this->rot_x_ = std::fmin(this->rot_x_, 3.14159f/2); + + linalg::vec y_quat{ 0, std::sinf(this->rot_y_ / 2), 0, std::cosf(this->rot_y_ / 2) }; + + linalg::vec x_quat{ std::sinf(this->rot_x_ / 2), 0, 0, std::cosf(this->rot_x_ / 2) }; + + linalg::vec pose_rot = linalg::qmul(y_quat, x_quat); + + pose.qRotation.w = (float) pose_rot.w; + pose.qRotation.x = (float) pose_rot.x; + pose.qRotation.y = (float) pose_rot.y; + pose.qRotation.z = (float) pose_rot.z; + + // Update position based on rotation + linalg::vec forward_vec{-1.0f * (GetAsyncKeyState(0x44) == 0) + 1.0f * (GetAsyncKeyState(0x41) == 0), 0, 0}; + linalg::vec right_vec{0, 0, 1.0f * (GetAsyncKeyState(0x57) == 0) - 1.0f * (GetAsyncKeyState(0x53) == 0) }; + linalg::vec final_dir = forward_vec + right_vec; + if (linalg::length(final_dir) > 0.01) { + final_dir = linalg::normalize(final_dir) * (float)delta_seconds; + final_dir = linalg::qrot(pose_rot, final_dir); + this->pos_x_ += final_dir.x; + this->pos_y_ += final_dir.y; + this->pos_z_ += final_dir.z; + } + + pose.vecPosition[0] = (float) this->pos_x_; + pose.vecPosition[1] = (float) this->pos_y_; + pose.vecPosition[2] = (float) this->pos_z_; + + // Post pose + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(this->device_index_, pose, sizeof(vr::DriverPose_t)); + this->last_pose_ = pose; +} + +DeviceType SlimeVRDriver::HMDDevice::GetDeviceType() +{ + return DeviceType::HMD; +} + +vr::TrackedDeviceIndex_t SlimeVRDriver::HMDDevice::GetDeviceIndex() +{ + return this->device_index_; +} + +vr::EVRInitError SlimeVRDriver::HMDDevice::Activate(uint32_t unObjectId) +{ + this->device_index_ = unObjectId; + + GetDriver()->Log("Activating HMD " + this->serial_); + + // Load settings values + // Could probably make this cleaner with making a wrapper class + try { + int window_x = std::get(GetDriver()->GetSettingsValue("window_x")); + if (window_x > 0) + this->window_x_ = window_x; + } + catch (const std::bad_variant_access&) {}; // Wrong type or doesnt exist + + try { + int window_y = std::get(GetDriver()->GetSettingsValue("window_y")); + if (window_y > 0) + this->window_x_ = window_y; + } + catch (const std::bad_variant_access&) {}; // Wrong type or doesnt exist + + try { + int window_width = std::get(GetDriver()->GetSettingsValue("window_width")); + if (window_width > 0) + this->window_width_ = window_width; + } + catch (const std::bad_variant_access&) {}; // Wrong type or doesnt exist + + try { + int window_height = std::get(GetDriver()->GetSettingsValue("window_height")); + if (window_height > 0) + this->window_height_ = window_height; + } + catch (const std::bad_variant_access&) {}; // Wrong type or doesnt exist + + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 2); + + // Set the IPD to be whatever steam has configured + GetDriver()->GetProperties()->SetFloatProperty(props, vr::Prop_UserIpdMeters_Float, vr::VRSettings()->GetFloat(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_IPD_Float)); + + // Set the display FPS + GetDriver()->GetProperties()->SetFloatProperty(props, vr::Prop_DisplayFrequency_Float, 90.f); + + // Set up a model "number" (not needed but good to have) + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "EXAMPLE_HMD_DEVICE"); + + // Set up icon paths + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{example}/icons/hmd_ready.png"); + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{example}/icons/hmd_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{example}/icons/hmd_not_ready.png"); + + + + + return vr::EVRInitError::VRInitError_None; +} + +void SlimeVRDriver::HMDDevice::Deactivate() +{ + this->device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + +void SlimeVRDriver::HMDDevice::EnterStandby() +{ +} + +void* SlimeVRDriver::HMDDevice::GetComponent(const char* pchComponentNameAndVersion) +{ + if (!_stricmp(pchComponentNameAndVersion, vr::IVRDisplayComponent_Version)) { + return static_cast(this); + } + return nullptr; +} + +void SlimeVRDriver::HMDDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) +{ + if (unResponseBufferSize >= 1) + pchResponseBuffer[0] = 0; +} + +vr::DriverPose_t SlimeVRDriver::HMDDevice::GetPose() +{ + return this->last_pose_; +} + +void SlimeVRDriver::HMDDevice::GetWindowBounds(int32_t* pnX, int32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight) +{ + *pnX = this->window_x_; + *pnY = this->window_y_; + *pnWidth = this->window_width_; + *pnHeight = this->window_height_; +} + +bool SlimeVRDriver::HMDDevice::IsDisplayOnDesktop() +{ + return true; +} + +bool SlimeVRDriver::HMDDevice::IsDisplayRealDisplay() +{ + return false; +} + +void SlimeVRDriver::HMDDevice::GetRecommendedRenderTargetSize(uint32_t* pnWidth, uint32_t* pnHeight) +{ + *pnWidth = this->window_width_; + *pnHeight = this->window_height_; +} + +void SlimeVRDriver::HMDDevice::GetEyeOutputViewport(vr::EVREye eEye, uint32_t* pnX, uint32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight) +{ + *pnY = 0; + *pnWidth = this->window_width_ / 2; + *pnHeight = this->window_height_; + + if (eEye == vr::EVREye::Eye_Left) { + *pnX = 0; + } + else { + *pnX = this->window_width_ / 2; + } +} + +void SlimeVRDriver::HMDDevice::GetProjectionRaw(vr::EVREye eEye, float* pfLeft, float* pfRight, float* pfTop, float* pfBottom) +{ + *pfLeft = -1; + *pfRight = 1; + *pfTop = -1; + *pfBottom = 1; +} + +vr::DistortionCoordinates_t SlimeVRDriver::HMDDevice::ComputeDistortion(vr::EVREye eEye, float fU, float fV) +{ + vr::DistortionCoordinates_t coordinates; + coordinates.rfBlue[0] = fU; + coordinates.rfBlue[1] = fV; + coordinates.rfGreen[0] = fU; + coordinates.rfGreen[1] = fV; + coordinates.rfRed[0] = fU; + coordinates.rfRed[1] = fV; + return coordinates; +} \ No newline at end of file diff --git a/src/HMDDevice.hpp b/src/HMDDevice.hpp new file mode 100644 index 0000000..f58c95d --- /dev/null +++ b/src/HMDDevice.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace SlimeVRDriver { + class HMDDevice : public IVRDevice, public vr::IVRDisplayComponent { + public: + HMDDevice(std::string serial); + ~HMDDevice() = default; + + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void* GetComponent(const char* pchComponentNameAndVersion) override; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + + // Inherited via IVRDisplayComponent + virtual void GetWindowBounds(int32_t* pnX, int32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight) override; + virtual bool IsDisplayOnDesktop() override; + virtual bool IsDisplayRealDisplay() override; + virtual void GetRecommendedRenderTargetSize(uint32_t* pnWidth, uint32_t* pnHeight) override; + virtual void GetEyeOutputViewport(vr::EVREye eEye, uint32_t* pnX, uint32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight) override; + virtual void GetProjectionRaw(vr::EVREye eEye, float* pfLeft, float* pfRight, float* pfTop, float* pfBottom) override; + virtual vr::DistortionCoordinates_t ComputeDistortion(vr::EVREye eEye, float fU, float fV) override; + private: + vr::TrackedDeviceIndex_t device_index_ = vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + + vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); + + uint32_t window_x_ = 0; + uint32_t window_y_ = 0; + uint32_t window_width_ = 1920; + uint32_t window_height_ = 1080; + + float pos_x_ = 0, pos_y_ = 0, pos_z_ = 0; + float rot_y_ = 0, rot_x_ = 0; + + }; +}; \ No newline at end of file diff --git a/src/IVRDevice.hpp b/src/IVRDevice.hpp new file mode 100644 index 0000000..18d8bf5 --- /dev/null +++ b/src/IVRDevice.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +namespace SlimeVRDriver { + + class IVRDevice : public vr::ITrackedDeviceServerDriver { + public: + /// + /// Returns the serial string for this device + /// + /// Device serial + virtual std::string GetSerial() = 0; + + /// + /// Runs any update logic for this device. + /// Called once per frame + /// + virtual void Update() = 0; + + /// + /// Returns the OpenVR device index + /// This should be 0 for HMDs + /// + /// OpenVR device index + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() = 0; + + /// + /// Returns which type of device this device is + /// + /// The type of device + virtual DeviceType GetDeviceType() = 0; + + /// + /// Makes a default device pose + /// + /// Default initialised pose + static inline vr::DriverPose_t MakeDefaultPose(bool connected = true, bool tracking = true) { + vr::DriverPose_t out_pose = { 0 }; + + out_pose.deviceIsConnected = connected; + out_pose.poseIsValid = tracking; + out_pose.result = tracking ? vr::ETrackingResult::TrackingResult_Running_OK : vr::ETrackingResult::TrackingResult_Running_OutOfRange; + out_pose.willDriftInYaw = false; + out_pose.shouldApplyHeadModel = false; + out_pose.qDriverFromHeadRotation.w = out_pose.qWorldFromDriverRotation.w = out_pose.qRotation.w = 1.0; + + return out_pose; + } + + // Inherited via ITrackedDeviceServerDriver + virtual vr::EVRInitError Activate(uint32_t unObjectId) = 0; + virtual void Deactivate() = 0; + virtual void EnterStandby() = 0; + virtual void* GetComponent(const char* pchComponentNameAndVersion) = 0; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) = 0; + virtual vr::DriverPose_t GetPose() = 0; + + ~IVRDevice() = default; + }; +}; \ No newline at end of file diff --git a/src/IVRDriver.hpp b/src/IVRDriver.hpp new file mode 100644 index 0000000..88c1ec8 --- /dev/null +++ b/src/IVRDriver.hpp @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include +#include +#include +#include "IVRDevice.hpp" + +namespace SlimeVRDriver { + + typedef std::variant SettingsValue; + + class IVRDriver : protected vr::IServerTrackedDeviceProvider { + public: + + /// + /// Returns all devices being managed by this driver + /// + /// All managed devices + virtual std::vector> GetDevices() = 0; + + /// + /// Returns all OpenVR events that happened on the current frame + /// + /// Current frame's OpenVR events + virtual std::vector GetOpenVREvents() = 0; + + /// + /// Returns the milliseconds between last frame and this frame + /// + /// MS between last frame and this frame + virtual std::chrono::milliseconds GetLastFrameTime() = 0; + + /// + /// Adds a device to the driver + /// + /// Device instance + /// True on success, false on failure + virtual bool AddDevice(std::shared_ptr device) = 0; + + /// + /// Returns the value of a settings key + /// + /// The settings key + /// Value of the key, std::monostate if the value is malformed or missing + virtual SettingsValue GetSettingsValue(std::string key) = 0; + + /// + /// Gets the OpenVR VRDriverInput pointer + /// + /// OpenVR VRDriverInput pointer + virtual vr::IVRDriverInput* GetInput() = 0; + + /// + /// Gets the OpenVR VRDriverProperties pointer + /// + /// OpenVR VRDriverProperties pointer + virtual vr::CVRPropertyHelpers* GetProperties() = 0; + + /// + /// Gets the OpenVR VRServerDriverHost pointer + /// + /// OpenVR VRServerDriverHost pointer + virtual vr::IVRServerDriverHost* GetDriverHost() = 0; + + /// + /// Writes a log message + /// + /// Message to log + virtual void Log(std::string message) = 0; + + virtual inline const char* const* GetInterfaceVersions() override { + return vr::k_InterfaceVersions; + }; + + virtual ~IVRDriver() {} + + }; +} \ No newline at end of file diff --git a/src/SlimeVR-OpenVR-Driver.cpp b/src/SlimeVR-OpenVR-Driver.cpp deleted file mode 100644 index f91a67f..0000000 --- a/src/SlimeVR-OpenVR-Driver.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -void say_hello(){ - std::cout << "Hello, from SlimeVR-OpenVR-Driver!\n"; -} diff --git a/src/TrackerDevice.cpp b/src/TrackerDevice.cpp new file mode 100644 index 0000000..6a623c4 --- /dev/null +++ b/src/TrackerDevice.cpp @@ -0,0 +1,194 @@ +#include "TrackerDevice.hpp" +#include + +SlimeVRDriver::TrackerDevice::TrackerDevice(std::string serial, HANDLE pipe, int deviceId): + serial_(serial), hpipe(pipe) +{ + this->deviceId = deviceId; + this->last_pose_ = MakeDefaultPose(); + this->isSetup = false; +} + +std::string SlimeVRDriver::TrackerDevice::GetSerial() +{ + return this->serial_; +} + +void SlimeVRDriver::TrackerDevice::Update() +{ + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + // Check if this device was asked to be identified + auto events = GetDriver()->GetOpenVREvents(); + for (auto event : events) { + // Note here, event.trackedDeviceIndex does not necissarily equal this->device_index_, not sure why, but the component handle will match so we can just use that instead + //if (event.trackedDeviceIndex == this->device_index_) { + if (event.eventType == vr::EVREventType::VREvent_Input_HapticVibration) { + if (event.data.hapticVibration.componentHandle == this->haptic_component_) { + this->did_vibrate_ = true; + } + } + //} + } + + // Check if we need to keep vibrating + if (this->did_vibrate_) { + this->vibrate_anim_state_ += (GetDriver()->GetLastFrameTime().count()/1000.f); + if (this->vibrate_anim_state_ > 1.0f) { + this->did_vibrate_ = false; + this->vibrate_anim_state_ = 0.0f; + } + } + + // Setup pose for this frame + auto pose = this->last_pose_; + + if (PeekNamedPipe(hpipe, NULL, 0, NULL, &dwRead, NULL) != FALSE) + { + //if data is ready, + if (dwRead > 0) + { + //we go and read it into our buffer + if (ReadFile(hpipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE) + { + + buffer[dwRead] = '\0'; //add terminating zero + //convert our buffer to string + std::string s = buffer; + + //first three variables are a position vector + double a; + double b; + double c; + + //second four are rotation quaternion + double qw; + double qx; + double qy; + double qz; + + //convert to string stream + std::istringstream iss(s); + + //read to our variables + iss >> a; + iss >> b; + iss >> c; + iss >> qw; + iss >> qx; + iss >> qy; + iss >> qz; + + //send the new position and rotation from the pipe to the tracker object + pose.vecPosition[0] = a; + pose.vecPosition[1] = b; + pose.vecPosition[2] = c; + + pose.qRotation.w = qw; + pose.qRotation.x = qx; + pose.qRotation.y = qy; + pose.qRotation.z = qz; + + // Post pose + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(this->device_index_, pose, sizeof(vr::DriverPose_t)); + this->last_pose_ = pose; + } + } + } + + +} + +DeviceType SlimeVRDriver::TrackerDevice::GetDeviceType() +{ + return DeviceType::TRACKER; +} + +vr::TrackedDeviceIndex_t SlimeVRDriver::TrackerDevice::GetDeviceIndex() +{ + return this->device_index_; +} + +vr::EVRInitError SlimeVRDriver::TrackerDevice::Activate(uint32_t unObjectId) +{ + this->device_index_ = unObjectId; + + GetDriver()->Log("[SlimeVR] Activating tracker " + this->serial_); + + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + + // Setup inputs and outputs + GetDriver()->GetInput()->CreateHapticComponent(props, "/output/haptic", &this->haptic_component_); + + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/click", &this->system_click_component_); + GetDriver()->GetInput()->CreateBooleanComponent(props, "/input/system/touch", &this->system_touch_component_); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 2); + + // Set up a model "number" (not needed but good to have) + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "Vive Tracker Pro MV"); + + // Opt out of hand selection + GetDriver()->GetProperties()->SetInt32Property(props, vr::Prop_ControllerRoleHint_Int32, vr::ETrackedControllerRole::TrackedControllerRole_OptOut); + + // Set up a render model path + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "vr_controller_05_wireless_b"); + + // Set controller profile + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_InputProfilePath_String, "{example}/input/example_tracker_bindings.json"); + + // Set the icon + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{example}/icons/tracker_ready.png"); + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{example}/icons/tracker_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{example}/icons/tracker_not_ready.png"); + + switch (deviceId) + { + case 0: + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "vive_tracker_waist"); + break; + case 1: + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "vive_tracker_left_foot"); + break; + case 2: + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ControllerType_String, "vive_tracker_right_foot"); + break; + } + + return vr::EVRInitError::VRInitError_None; +} + +void SlimeVRDriver::TrackerDevice::Deactivate() +{ + this->device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + +void SlimeVRDriver::TrackerDevice::EnterStandby() +{ +} + +void* SlimeVRDriver::TrackerDevice::GetComponent(const char* pchComponentNameAndVersion) +{ + return nullptr; +} + +void SlimeVRDriver::TrackerDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) +{ + if (unResponseBufferSize >= 1) + pchResponseBuffer[0] = 0; +} + +vr::DriverPose_t SlimeVRDriver::TrackerDevice::GetPose() +{ + return last_pose_; +} + diff --git a/src/TrackerDevice.hpp b/src/TrackerDevice.hpp new file mode 100644 index 0000000..b3f744d --- /dev/null +++ b/src/TrackerDevice.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace SlimeVRDriver { + class TrackerDevice : public IVRDevice { + public: + + TrackerDevice(std::string serial, HANDLE pipe, int deviceId); + ~TrackerDevice() = default; + + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void* GetComponent(const char* pchComponentNameAndVersion) override; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + + private: + vr::TrackedDeviceIndex_t device_index_ = vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + HANDLE hpipe; + bool isSetup; + + char buffer[1024]; + DWORD dwWritten; + DWORD dwRead; + int deviceId; + + vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); + + bool did_vibrate_ = false; + float vibrate_anim_state_ = 0.f; + + vr::VRInputComponentHandle_t haptic_component_ = 0; + + vr::VRInputComponentHandle_t system_click_component_ = 0; + vr::VRInputComponentHandle_t system_touch_component_ = 0; + + }; +}; \ No newline at end of file diff --git a/src/TrackingReferenceDevice.cpp b/src/TrackingReferenceDevice.cpp new file mode 100644 index 0000000..5091b4f --- /dev/null +++ b/src/TrackingReferenceDevice.cpp @@ -0,0 +1,115 @@ +#include "TrackingReferenceDevice.hpp" +#include + +SlimeVRDriver::TrackingReferenceDevice::TrackingReferenceDevice(std::string serial): + serial_(serial) +{ + + // Get some random angle to place this tracking reference at in the scene + this->random_angle_rad_ = fmod(rand() / 10000.f, 2 * 3.14159f); +} + +std::string SlimeVRDriver::TrackingReferenceDevice::GetSerial() +{ + return this->serial_; +} + +void SlimeVRDriver::TrackingReferenceDevice::Update() +{ + if (this->device_index_ == vr::k_unTrackedDeviceIndexInvalid) + return; + + + // Setup pose for this frame + auto pose = IVRDevice::MakeDefaultPose(); + + linalg::vec device_position{ 0.f, 1.f, 1.f }; + + linalg::vec y_quat{ 0, std::sinf(this->random_angle_rad_ / 2), 0, std::cosf(this->random_angle_rad_ / 2) }; // Point inwards (z- is forward) + + linalg::vec x_look_down{ std::sinf((-3.1415f/4) / 2), 0, 0, std::cosf((-3.1415f / 4) / 2) }; // Tilt downwards to look at the centre + + linalg::vec device_rotation = linalg::qmul(y_quat, x_look_down); + + device_position = linalg::qrot(y_quat, device_position); + + pose.vecPosition[0] = device_position.x; + pose.vecPosition[1] = device_position.y; + pose.vecPosition[2] = device_position.z; + + pose.qRotation.w = device_rotation.w; + pose.qRotation.x = device_rotation.x; + pose.qRotation.y = device_rotation.y; + pose.qRotation.z = device_rotation.z; + + // Post pose + GetDriver()->GetDriverHost()->TrackedDevicePoseUpdated(this->device_index_, pose, sizeof(vr::DriverPose_t)); + this->last_pose_ = pose; +} + +DeviceType SlimeVRDriver::TrackingReferenceDevice::GetDeviceType() +{ + return DeviceType::TRACKING_REFERENCE; +} + +vr::TrackedDeviceIndex_t SlimeVRDriver::TrackingReferenceDevice::GetDeviceIndex() +{ + return this->device_index_; +} + +vr::EVRInitError SlimeVRDriver::TrackingReferenceDevice::Activate(uint32_t unObjectId) +{ + this->device_index_ = unObjectId; + + GetDriver()->Log("Activating tracking reference " + this->serial_); + + // Get the properties handle + auto props = GetDriver()->GetProperties()->TrackedDeviceToPropertyContainer(this->device_index_); + + // Set some universe ID (Must be 2 or higher) + GetDriver()->GetProperties()->SetUint64Property(props, vr::Prop_CurrentUniverseId_Uint64, 2); + + // Set up a model "number" (not needed but good to have) + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_ModelNumber_String, "example_trackingreference"); + + // Set up a render model path + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_RenderModelName_String, "locator"); + + // Set the icons + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReady_String, "{example}/icons/trackingreference_ready.png"); + + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceOff_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearching_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceSearchingAlert_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceReadyAlert_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceNotReady_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceStandby_String, "{example}/icons/trackingreference_not_ready.png"); + GetDriver()->GetProperties()->SetStringProperty(props, vr::Prop_NamedIconPathDeviceAlertLow_String, "{example}/icons/trackingreference_not_ready.png"); + + return vr::EVRInitError::VRInitError_None; +} + +void SlimeVRDriver::TrackingReferenceDevice::Deactivate() +{ + this->device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + +void SlimeVRDriver::TrackingReferenceDevice::EnterStandby() +{ +} + +void* SlimeVRDriver::TrackingReferenceDevice::GetComponent(const char* pchComponentNameAndVersion) +{ + return nullptr; +} + +void SlimeVRDriver::TrackingReferenceDevice::DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) +{ + if (unResponseBufferSize >= 1) + pchResponseBuffer[0] = 0; +} + +vr::DriverPose_t SlimeVRDriver::TrackingReferenceDevice::GetPose() +{ + return last_pose_; +} diff --git a/src/TrackingReferenceDevice.hpp b/src/TrackingReferenceDevice.hpp new file mode 100644 index 0000000..134c7e1 --- /dev/null +++ b/src/TrackingReferenceDevice.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace SlimeVRDriver { + class TrackingReferenceDevice : public IVRDevice { + public: + + TrackingReferenceDevice(std::string serial); + ~TrackingReferenceDevice() = default; + + // Inherited via IVRDevice + virtual std::string GetSerial() override; + virtual void Update() override; + virtual vr::TrackedDeviceIndex_t GetDeviceIndex() override; + virtual DeviceType GetDeviceType() override; + + virtual vr::EVRInitError Activate(uint32_t unObjectId) override; + virtual void Deactivate() override; + virtual void EnterStandby() override; + virtual void* GetComponent(const char* pchComponentNameAndVersion) override; + virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) override; + virtual vr::DriverPose_t GetPose() override; + + private: + vr::TrackedDeviceIndex_t device_index_ = vr::k_unTrackedDeviceIndexInvalid; + std::string serial_; + + vr::DriverPose_t last_pose_ = IVRDevice::MakeDefaultPose(); + + float random_angle_rad_; + + }; +}; \ No newline at end of file diff --git a/src/VRDriver.cpp b/src/VRDriver.cpp new file mode 100644 index 0000000..d2a0b8a --- /dev/null +++ b/src/VRDriver.cpp @@ -0,0 +1,281 @@ +#include "VRDriver.hpp" +#include +#include +#include +#include + +vr::EVRInitError SlimeVRDriver::VRDriver::Init(vr::IVRDriverContext* pDriverContext) +{ + // Perform driver context initialisation + if (vr::EVRInitError init_error = vr::InitServerDriverContext(pDriverContext); init_error != vr::EVRInitError::VRInitError_None) { + return init_error; + } + + Log("[SlimeVR] Activating SlimeVR Driver..."); + + // Add a HMD + //this->AddDevice(std::make_shared("Example_HMDDevice")); + + // Add a couple controllers + //this->AddDevice(std::make_shared("Example_ControllerDevice_Left", ControllerDevice::Handedness::LEFT)); + //this->AddDevice(std::make_shared("Example_ControllerDevice_Right", ControllerDevice::Handedness::RIGHT)); + + std::string hmdPipeName = "\\\\.\\pipe\\HMDPipe"; + + //open the pipe + hmdPipe = CreateFileA(hmdPipeName.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hmdPipe == INVALID_HANDLE_VALUE) + { + //if connection was unsuccessful, return an error. This means SteamVR will start without this driver running + return vr::EVRInitError::VRInitError_Driver_Failed; + } + //wait for a second to ensure data was sent and next pipe is set up if there is more than one tracker + + Sleep(1000); + + // Add a tracker + char buffer[1024]; + DWORD dwWritten; + DWORD dwRead; + + //on init, we try to connect to our pipes + for (int i = 0; i < pipeNum; i++) + { + //MessageBoxA(NULL, "It works! " + pipeNum, "Example Driver", MB_OK); + HANDLE pipe; + //pipe name, same as in our server program + std::string pipeName = "\\\\.\\pipe\\TrackPipe" + std::to_string(i); + + //open the pipe + pipe = CreateFileA(pipeName.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (pipe == INVALID_HANDLE_VALUE) + { + //if connection was unsuccessful, return an error. This means SteamVR will start without this driver running + return vr::EVRInitError::VRInitError_Driver_Failed; + } + + //wait for a second to ensure data was sent and next pipe is set up if there is more than one tracker + Sleep(1000); + + //read the number of pipes and smoothing factor from the pipe + if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE) + { + //we receive raw data, so we first add terminating zero and save to a string. + buffer[dwRead] = '\0'; //add terminating zero + std::string s = buffer; + //from a string, we convert to a string stream for easier reading of each sent value + std::istringstream iss(s); + //read each value into our variables + + iss >> pipeNum; + iss >> smoothFactor; + } + //save our pipe to global + this->AddDevice(std::make_shared("SlimeVRTracker"+std::to_string(i),pipe, i)); + } + + // Add a couple tracking references + //this->AddDevice(std::make_shared("Example_TrackingReference_A")); + //this->AddDevice(std::make_shared("Example_TrackingReference_B")); + + Log("[SlimeVR] SlimeVR Driver Loaded Successfully"); + + return vr::VRInitError_None; +} + +void SlimeVRDriver::VRDriver::Cleanup() +{ +} + +void SlimeVRDriver::VRDriver::RunFrame() +{ + // Collect events + vr::VREvent_t event; + std::vector events; + while (vr::VRServerDriverHost()->PollNextEvent(&event, sizeof(event))) + { + events.push_back(event); + } + this->openvr_events_ = events; + + // Update frame timing + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + this->frame_timing_ = std::chrono::duration_cast(now - this->last_frame_time_); + this->last_frame_time_ = now; + + // Update devices + for (auto& device : this->devices_) + device->Update(); + + vr::TrackedDevicePose_t hmd_pose[10]; + vr::VRServerDriverHost()->GetRawTrackedDevicePoses(0, hmd_pose, 10); + + vr::HmdQuaternion_t q = GetRotation(hmd_pose[0].mDeviceToAbsoluteTracking); + vr::HmdVector3_t pos = GetPosition(hmd_pose[0].mDeviceToAbsoluteTracking); + + std::string s; + s = std::to_string(pos.v[0]) + + " " + std::to_string(pos.v[1]) + + " " + std::to_string(pos.v[2]) + + " " + std::to_string(q.w) + + " " + std::to_string(q.x) + + " " + std::to_string(q.y) + + " " + std::to_string(q.z) + "\n"; + + DWORD dwWritten; + WriteFile(hmdPipe, + s.c_str(), + (s.length() + 1), // = length of string + terminating '\0' !!! + &dwWritten, + NULL); + +} + +bool SlimeVRDriver::VRDriver::ShouldBlockStandbyMode() +{ + return false; +} + +void SlimeVRDriver::VRDriver::EnterStandby() +{ +} + +void SlimeVRDriver::VRDriver::LeaveStandby() +{ +} + +std::vector> SlimeVRDriver::VRDriver::GetDevices() +{ + return this->devices_; +} + +std::vector SlimeVRDriver::VRDriver::GetOpenVREvents() +{ + return this->openvr_events_; +} + +std::chrono::milliseconds SlimeVRDriver::VRDriver::GetLastFrameTime() +{ + return this->frame_timing_; +} + +bool SlimeVRDriver::VRDriver::AddDevice(std::shared_ptr device) +{ + vr::ETrackedDeviceClass openvr_device_class; + // Remember to update this switch when new device types are added + switch (device->GetDeviceType()) { + case DeviceType::CONTROLLER: + openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_Controller; + break; + case DeviceType::HMD: + openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_HMD; + break; + case DeviceType::TRACKER: + openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_GenericTracker; + break; + case DeviceType::TRACKING_REFERENCE: + openvr_device_class = vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference; + break; + default: + return false; + } + bool result = vr::VRServerDriverHost()->TrackedDeviceAdded(device->GetSerial().c_str(), openvr_device_class, device.get()); + if(result) + this->devices_.push_back(device); + return result; +} + +SlimeVRDriver::SettingsValue SlimeVRDriver::VRDriver::GetSettingsValue(std::string key) +{ + vr::EVRSettingsError err = vr::EVRSettingsError::VRSettingsError_None; + int int_value = vr::VRSettings()->GetInt32(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return int_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + float float_value = vr::VRSettings()->GetFloat(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return float_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + bool bool_value = vr::VRSettings()->GetBool(settings_key_.c_str(), key.c_str(), &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return bool_value; + } + std::string str_value; + str_value.reserve(1024); + vr::VRSettings()->GetString(settings_key_.c_str(), key.c_str(), str_value.data(), 1024, &err); + if (err == vr::EVRSettingsError::VRSettingsError_None) { + return str_value; + } + err = vr::EVRSettingsError::VRSettingsError_None; + + return SettingsValue(); +} + +void SlimeVRDriver::VRDriver::Log(std::string message) +{ + std::string message_endl = message + "\n"; + vr::VRDriverLog()->Log(message_endl.c_str()); +} + +vr::IVRDriverInput* SlimeVRDriver::VRDriver::GetInput() +{ + return vr::VRDriverInput(); +} + +vr::CVRPropertyHelpers* SlimeVRDriver::VRDriver::GetProperties() +{ + return vr::VRProperties(); +} + +vr::IVRServerDriverHost* SlimeVRDriver::VRDriver::GetDriverHost() +{ + return vr::VRServerDriverHost(); +} + +//----------------------------------------------------------------------------- +// Purpose: Calculates quaternion (qw,qx,qy,qz) representing the rotation +// from: https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp +//----------------------------------------------------------------------------- + +vr::HmdQuaternion_t SlimeVRDriver::VRDriver::GetRotation(vr::HmdMatrix34_t matrix) { + vr::HmdQuaternion_t q; + + q.w = sqrt(fmax(0, 1 + matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2])) / 2; + q.x = sqrt(fmax(0, 1 + matrix.m[0][0] - matrix.m[1][1] - matrix.m[2][2])) / 2; + q.y = sqrt(fmax(0, 1 - matrix.m[0][0] + matrix.m[1][1] - matrix.m[2][2])) / 2; + q.z = sqrt(fmax(0, 1 - matrix.m[0][0] - matrix.m[1][1] + matrix.m[2][2])) / 2; + q.x = copysign(q.x, matrix.m[2][1] - matrix.m[1][2]); + q.y = copysign(q.y, matrix.m[0][2] - matrix.m[2][0]); + q.z = copysign(q.z, matrix.m[1][0] - matrix.m[0][1]); + return q; +} +//----------------------------------------------------------------------------- +// Purpose: Extracts position (x,y,z). +// from: https://github.com/Omnifinity/OpenVR-Tracking-Example/blob/master/HTC%20Lighthouse%20Tracking%20Example/LighthouseTracking.cpp +//----------------------------------------------------------------------------- + +vr::HmdVector3_t SlimeVRDriver::VRDriver::GetPosition(vr::HmdMatrix34_t matrix) { + vr::HmdVector3_t vector; + + vector.v[0] = matrix.m[0][3]; + vector.v[1] = matrix.m[1][3]; + vector.v[2] = matrix.m[2][3]; + + return vector; +} diff --git a/src/VRDriver.hpp b/src/VRDriver.hpp new file mode 100644 index 0000000..0e1c730 --- /dev/null +++ b/src/VRDriver.hpp @@ -0,0 +1,53 @@ +#pragma once +#define NOMINMAX + +#include +#include +#include + +#include + +#include +#include + + +namespace SlimeVRDriver { + class VRDriver : public IVRDriver { + public: + + // Inherited via IVRDriver + virtual std::vector> GetDevices() override; + virtual std::vector GetOpenVREvents() override; + virtual std::chrono::milliseconds GetLastFrameTime() override; + virtual bool AddDevice(std::shared_ptr device) override; + virtual SettingsValue GetSettingsValue(std::string key) override; + virtual void Log(std::string message) override; + + virtual vr::IVRDriverInput* GetInput() override; + virtual vr::CVRPropertyHelpers* GetProperties() override; + virtual vr::IVRServerDriverHost* GetDriverHost() override; + + // Inherited via IServerTrackedDeviceProvider + virtual vr::EVRInitError Init(vr::IVRDriverContext* pDriverContext) override; + virtual void Cleanup() override; + virtual void RunFrame() override; + virtual bool ShouldBlockStandbyMode() override; + virtual void EnterStandby() override; + virtual void LeaveStandby() override; + virtual ~VRDriver() = default; + + private: + HANDLE hmdPipe; + std::vector> devices_; + std::vector openvr_events_; + std::chrono::milliseconds frame_timing_ = std::chrono::milliseconds(16); + std::chrono::system_clock::time_point last_frame_time_ = std::chrono::system_clock::now(); + std::string settings_key_ = "driver_apriltag"; + + vr::HmdQuaternion_t GetRotation(vr::HmdMatrix34_t matrix); + vr::HmdVector3_t GetPosition(vr::HmdMatrix34_t matrix); + + int pipeNum = 1; + double smoothFactor = 0.2; + }; +}; \ No newline at end of file