diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e9ea35a2..96c89228b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,8 @@ option(XRT_FEATURE_SERVICE "Enable separate service module for OpenXR runtime" O cmake_dependent_option(XRT_HAVE_SYSTEMD "Enable systemd support (for socket activation of service)" ON "Systemd_FOUND AND XRT_FEATURE_SERVICE" OFF) cmake_dependent_option(XRT_INSTALL_SYSTEMD_UNIT_FILES "Install user unit files for systemd socket activation on installation" ON "XRT_HAVE_SYSTEMD" OFF) cmake_dependent_option(XRT_INSTALL_ABSOLUTE_SYSTEMD_UNIT_FILES "Use an absolute path to monado-system in installed user unit files for systemd socket activation" ON "XRT_INSTALL_SYSTEMD_UNIT_FILES" OFF) +option(XRT_FEATURE_STEAMVR_PLUGIN "Build SteamVR plugin" ON) + if(NOT DEFINED XRT_FEATURE_OPENXR_LAYER_DEPTH) set(XRT_FEATURE_OPENXR_LAYER_DEPTH ON) endif() @@ -283,6 +285,7 @@ message(STATUS "# FEATURE_OPENXR_LAYER_CUBE: ${XRT_FEATURE_OPENXR_ message(STATUS "# FEATURE_OPENXR_LAYER_CYLINDER: ${XRT_FEATURE_OPENXR_LAYER_CYLINDER}") message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT}") message(STATUS "# FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY: ${XRT_FEATURE_OPENXR_LAYER_EQUIRECT_LEGACY}") +message(STATUS "# FEATURE_STEAMVR_PLUGIN: ${XRT_FEATURE_STEAMVR_PLUGIN}") message(STATUS "#") message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}") message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}") diff --git a/meson.build b/meson.build index 699bbecaf..8d1ae88ca 100644 --- a/meson.build +++ b/meson.build @@ -245,3 +245,9 @@ if build_tracking else message(' tracking: no') endif + +if get_option('steamvr_plugin') + message('steamvr plugin: yes') +else + message('steamvr plugin: no') +endif diff --git a/meson_options.txt b/meson_options.txt index 764468ad1..6df07004c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -107,3 +107,10 @@ option('layer_equirect2', value: true, description: 'Enable support for Equirect2 Layers' ) + +option('steamvr_plugin', + type: 'boolean', + value: true, + description: 'Enable SteamVR Plugin' +) + diff --git a/src/external/meson.build b/src/external/meson.build index 89feeefb9..ab83322cd 100644 --- a/src/external/meson.build +++ b/src/external/meson.build @@ -1,7 +1,7 @@ # Copyright 2019-2020, Collabora, Ltd. # SPDX-License-Identifier: BSL-1.0 -openvr_include = include_directories('openvr_includes') +openvr_include = include_directories('openvr_includes', is_system: true) cjson_include = include_directories('cjson') flexkalman_include = include_directories('flexkalman') glad_include = include_directories('glad/include') diff --git a/src/xrt/include/meson.build b/src/xrt/include/meson.build index fcda78e04..b58e39138 100644 --- a/src/xrt/include/meson.build +++ b/src/xrt/include/meson.build @@ -1,4 +1,6 @@ # Copyright 2020, Collabora, Ltd. # SPDX-License-Identifier: BSL-1.0 +xrt_include = include_directories('.') + subdir('xrt') diff --git a/src/xrt/state_trackers/CMakeLists.txt b/src/xrt/state_trackers/CMakeLists.txt index 698554e32..5ad98d8c1 100644 --- a/src/xrt/state_trackers/CMakeLists.txt +++ b/src/xrt/state_trackers/CMakeLists.txt @@ -6,3 +6,7 @@ if(XRT_HAVE_OPENGL) endif() add_subdirectory(oxr) add_subdirectory(prober) + +if(XRT_FEATURE_STEAMVR_PLUGIN) + add_subdirectory(steamvr_drv) +endif() diff --git a/src/xrt/state_trackers/meson.build b/src/xrt/state_trackers/meson.build index 24e413d37..449670090 100644 --- a/src/xrt/state_trackers/meson.build +++ b/src/xrt/state_trackers/meson.build @@ -6,3 +6,7 @@ st_include = include_directories('.') subdir('gui') subdir('oxr') subdir('prober') + +if get_option('steamvr_plugin') + subdir('steamvr_drv') +endif diff --git a/src/xrt/state_trackers/steamvr_drv/CMakeLists.txt b/src/xrt/state_trackers/steamvr_drv/CMakeLists.txt new file mode 100644 index 000000000..733ac21ff --- /dev/null +++ b/src/xrt/state_trackers/steamvr_drv/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +set(OVRD_SOURCE_FILES + ovrd_driver.cpp + ovrd_interface.h + ) + +add_library(st_ovrd STATIC + ${OVRD_SOURCE_FILES} + ) +target_include_directories(st_ovrd INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} + ) +target_link_libraries(st_ovrd PRIVATE + xrt-interfaces + xrt-external-openvr + aux_math + ) diff --git a/src/xrt/state_trackers/steamvr_drv/meson.build b/src/xrt/state_trackers/steamvr_drv/meson.build new file mode 100644 index 000000000..17aa5806b --- /dev/null +++ b/src/xrt/state_trackers/steamvr_drv/meson.build @@ -0,0 +1,20 @@ +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +st_ovrd_include = include_directories('.') + +lib_st_ovrd = static_library( + 'st_ovrd', + files( + 'ovrd_driver.cpp', + 'ovrd_interface.h' + ), + include_directories: [ + openvr_include, + st_include, # Sigh debian meson requires this. + xrt_include, + ], + dependencies: aux_util, + c_args: compile_args, + cpp_args: compile_args, +) diff --git a/src/xrt/state_trackers/steamvr_drv/ovrd_driver.cpp b/src/xrt/state_trackers/steamvr_drv/ovrd_driver.cpp new file mode 100644 index 000000000..50cf15bb7 --- /dev/null +++ b/src/xrt/state_trackers/steamvr_drv/ovrd_driver.cpp @@ -0,0 +1,1558 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Main driver code for @ref st_ovrd. + * @author Jakob Bornecrantz + * @author Christoph Haag + * @ingroup st_ovrd + */ + +#include +#include + +#include "ovrd_log.hpp" +#include "openvr_driver.h" + +extern "C" { +#include "ovrd_interface.h" + +#include + +#include +#include "os/os_time.h" +#include "util/u_debug.h" +#include "util/u_device.h" + +#include "xrt/xrt_defines.h" +#include "xrt/xrt_device.h" +#include "xrt/xrt_instance.h" +} + +//! When set, all controllers pretend to be Index controllers. Provides best +//! compatibility with legacy games due to steamvr's legacy binding for Index +//! controllers, but input mapping may be incomplete or not ideal. +DEBUG_GET_ONCE_BOOL_OPTION(emulate_index_controller, + "STEAMVR_EMULATE_INDEX_CONTROLLER", + true) + +DEBUG_GET_ONCE_NUM_OPTION(scale_percentage, + "XRT_COMPOSITOR_SCALE_PERCENTAGE", + 140) + +//#define DUMP_POSE +//#define DUMP_POSE_CONTROLLERS + + +/* + * Controller + */ + +struct MonadoInputComponent +{ + bool has_component; + bool x; + bool y; +}; + +struct SteamVRDriverControl +{ + const char *steamvr_control_path; + vr::VRInputComponentHandle_t control_handle; + enum xrt_input_type monado_input_type; + enum xrt_input_name monado_input_name; + struct MonadoInputComponent component; +}; + +static void +copy_vec3(struct xrt_vec3 *from, double *to) +{ + to[0] = from->x; + to[1] = from->y; + to[2] = from->z; +} + +static void +copy_quat(struct xrt_quat *from, vr::HmdQuaternion_t *to) +{ + to->x = from->x; + to->y = from->y; + to->z = from->z; + to->w = from->w; +} + +static void +apply_pose(struct xrt_space_relation *rel, vr::DriverPose_t *m_pose) +{ + if ((rel->relation_flags & + XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT) != 0) { + copy_quat(&rel->pose.orientation, &m_pose->qRotation); + } else { + m_pose->result = vr::TrackingResult_Running_OutOfRange; + m_pose->poseIsValid = false; + } + + if ((rel->relation_flags & XRT_SPACE_RELATION_POSITION_TRACKED_BIT) != + 0) { + copy_vec3(&rel->pose.position, m_pose->vecPosition); + } else { + } + + if ((rel->relation_flags & + XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT) != 0) { + // linear velocity in world space + copy_vec3(&rel->linear_velocity, m_pose->vecVelocity); + } + + if ((rel->relation_flags & + XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) != 0) { + // angular velocity reported by monado in world space, + // expected by steamvr to be in "controller space" + struct xrt_quat orientation_inv; + math_quat_invert(&rel->pose.orientation, &orientation_inv); + + struct xrt_vec3 vel; + math_quat_rotate_derivative(&orientation_inv, + &rel->angular_velocity, &vel); + + copy_vec3(&vel, m_pose->vecAngularVelocity); + } +} + +class CDeviceDriver_Monado_Controller : public vr::ITrackedDeviceServerDriver +{ +public: + CDeviceDriver_Monado_Controller(struct xrt_instance *xinst, + struct xrt_device *xdev, + enum xrt_hand hand) + : m_xdev(xdev), m_hand(hand) + { + ovrd_log("Creating Controller %s\n", xdev->str); + + m_handed_controller = true; + + m_emulate_index_controller = + debug_get_bool_option_emulate_index_controller(); + + if (m_emulate_index_controller) { + ovrd_log("Emulating Index Controller\n"); + } else { + ovrd_log("Using Monado Controller profile\n"); + } + + m_unObjectId = vr::k_unTrackedDeviceIndexInvalid; + m_pose = {}; + + strncpy(m_sSerialNumber, xdev->str, XRT_DEVICE_NAME_LEN); + strncpy(m_sModelNumber, xdev->str, XRT_DEVICE_NAME_LEN); + + + switch (this->m_xdev->name) { + case XRT_DEVICE_INDEX_CONTROLLER: + if (hand == XRT_HAND_LEFT) { + m_render_model = + "{indexcontroller}valve_controller_knu_1_0_" + "left"; + } + if (hand == XRT_HAND_RIGHT) { + m_render_model = + "{indexcontroller}valve_controller_knu_1_0_" + "right"; + } + break; + case XRT_DEVICE_VIVE_WAND: + m_render_model = "vr_controller_vive_1_5"; + break; + case XRT_DEVICE_VIVE_TRACKER_GEN1: + case XRT_DEVICE_VIVE_TRACKER_GEN2: + m_render_model = "{htc}vr_tracker_vive_1_0"; + break; + case XRT_DEVICE_PSMV: + case XRT_DEVICE_HYDRA: + case XRT_DEVICE_DAYDREAM: + case XRT_DEVICE_GENERIC_HMD: + default: m_render_model = "locator_one_sided"; break; + } + + ovrd_log("Render model based on Monado: %s\n", m_render_model); + } + virtual ~CDeviceDriver_Monado_Controller() {} + + void + AddControl(enum xrt_input_type monado_input_type, + const char *steamvr_control_path, + enum xrt_input_name monado_input_name, + struct MonadoInputComponent *component) + { + m_controls[m_control_count].monado_input_type = + monado_input_type; + m_controls[m_control_count].steamvr_control_path = + steamvr_control_path; + m_controls[m_control_count].monado_input_name = + monado_input_name; + if (component != NULL) { + m_controls[m_control_count].component = *component; + } else { + m_controls[m_control_count].component.has_component = + false; + } + + if (monado_input_type == XRT_INPUT_TYPE_BOOLEAN) { + vr::VRDriverInput()->CreateBooleanComponent( + m_ulPropertyContainer, steamvr_control_path, + &m_controls[m_control_count].control_handle); + + } else if (monado_input_type == + XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE) { + vr::VRDriverInput()->CreateScalarComponent( + m_ulPropertyContainer, steamvr_control_path, + &m_controls[m_control_count].control_handle, + vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + + } else if (monado_input_type == + XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE) { + vr::VRDriverInput()->CreateScalarComponent( + m_ulPropertyContainer, steamvr_control_path, + &m_controls[m_control_count].control_handle, + vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedOneSided); + + } else if (monado_input_type == + XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) { + vr::VRDriverInput()->CreateScalarComponent( + m_ulPropertyContainer, steamvr_control_path, + &m_controls[m_control_count].control_handle, + vr::VRScalarType_Absolute, + vr::VRScalarUnits_NormalizedTwoSided); + } + + ovrd_log("Added input %s\n", steamvr_control_path); + m_control_count++; + } + + void + AddEmulatedIndexControls() + { + switch (this->m_xdev->name) { + case XRT_DEVICE_INDEX_CONTROLLER: { + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trigger/value", + XRT_INPUT_INDEX_TRIGGER_VALUE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/click", + XRT_INPUT_INDEX_TRIGGER_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/touch", + XRT_INPUT_INDEX_TRIGGER_TOUCH, NULL); + + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/click", + XRT_INPUT_INDEX_SYSTEM_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/touch", + XRT_INPUT_INDEX_SYSTEM_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", + XRT_INPUT_INDEX_A_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/touch", + XRT_INPUT_INDEX_A_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", + XRT_INPUT_INDEX_B_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/touch", + XRT_INPUT_INDEX_B_TOUCH, NULL); + + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/grip/force", + XRT_INPUT_INDEX_SQUEEZE_FORCE, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/grip/value", + XRT_INPUT_INDEX_SQUEEZE_VALUE, NULL); + + struct MonadoInputComponent x = {true, true, false}; + struct MonadoInputComponent y = {true, false, true}; + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/thumbstick/click", + XRT_INPUT_INDEX_THUMBSTICK_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/thumbstick/touch", + XRT_INPUT_INDEX_THUMBSTICK_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/thumbstick/x", + XRT_INPUT_INDEX_THUMBSTICK, &x); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/thumbstick/y", + XRT_INPUT_INDEX_THUMBSTICK, &y); + + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trackpad/force", + XRT_INPUT_INDEX_TRACKPAD_FORCE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trackpad/touch", + XRT_INPUT_INDEX_TRACKPAD_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/x", + XRT_INPUT_INDEX_TRACKPAD, &x); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/y", + XRT_INPUT_INDEX_TRACKPAD, &y); + + vr::VRDriverInput()->CreateHapticComponent( + m_ulPropertyContainer, "/output/haptic", + &m_hapticHandle); + } + + break; + case XRT_DEVICE_VIVE_WAND: { + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trigger/value", + XRT_INPUT_VIVE_TRIGGER_VALUE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/click", + XRT_INPUT_VIVE_TRIGGER_CLICK, NULL); + + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/click", + XRT_INPUT_VIVE_SYSTEM_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", + XRT_INPUT_VIVE_TRACKPAD_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", + XRT_INPUT_VIVE_MENU_CLICK, NULL); + + struct MonadoInputComponent x = {true, true, false}; + struct MonadoInputComponent y = {true, false, true}; + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trackpad/touch", + XRT_INPUT_VIVE_TRACKPAD_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/x", XRT_INPUT_VIVE_TRACKPAD, + &x); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/y", XRT_INPUT_VIVE_TRACKPAD, + &y); + + vr::VRDriverInput()->CreateHapticComponent( + m_ulPropertyContainer, "/output/haptic", + &m_hapticHandle); + } break; + case XRT_DEVICE_VIVE_TRACKER_GEN1: + case XRT_DEVICE_VIVE_TRACKER_GEN2: break; + case XRT_DEVICE_PSMV: { + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trigger/value", + XRT_INPUT_PSMV_TRIGGER_VALUE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/click", + XRT_INPUT_PSMV_MOVE_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/click", + XRT_INPUT_PSMV_PS_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", + XRT_INPUT_PSMV_CROSS_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", + XRT_INPUT_PSMV_SQUARE_CLICK, NULL); + + vr::VRDriverInput()->CreateHapticComponent( + m_ulPropertyContainer, "/output/haptic", + &m_hapticHandle); + } + + break; + case XRT_DEVICE_HYDRA: + case XRT_DEVICE_DAYDREAM: + case XRT_DEVICE_GENERIC_HMD: + default: break; + } + } + + void + AddMonadoControls() + { + switch (this->m_xdev->name) { + case XRT_DEVICE_INDEX_CONTROLLER: { + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trigger/value", + XRT_INPUT_INDEX_TRIGGER_VALUE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/click", + XRT_INPUT_INDEX_TRIGGER_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trigger/touch", + XRT_INPUT_INDEX_TRIGGER_TOUCH, NULL); + + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/click", + XRT_INPUT_INDEX_SYSTEM_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/system/touch", + XRT_INPUT_INDEX_SYSTEM_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/click", + XRT_INPUT_INDEX_A_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/a/touch", + XRT_INPUT_INDEX_A_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/click", + XRT_INPUT_INDEX_B_CLICK, NULL); + AddControl(XRT_INPUT_TYPE_BOOLEAN, "/input/b/touch", + XRT_INPUT_INDEX_B_TOUCH, NULL); + + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/grip/force", + XRT_INPUT_INDEX_SQUEEZE_FORCE, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/grip/value", + XRT_INPUT_INDEX_SQUEEZE_VALUE, NULL); + + struct MonadoInputComponent x = {true, true, false}; + struct MonadoInputComponent y = {true, false, true}; + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/thumbstick/click", + XRT_INPUT_INDEX_THUMBSTICK_CLICK, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/thumbstick/touch", + XRT_INPUT_INDEX_THUMBSTICK_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/thumbstick/x", + XRT_INPUT_INDEX_THUMBSTICK, &x); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/thumbstick/y", + XRT_INPUT_INDEX_THUMBSTICK, &y); + + + AddControl(XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE, + "/input/trackpad/force", + XRT_INPUT_INDEX_TRACKPAD_FORCE, NULL); + + AddControl(XRT_INPUT_TYPE_BOOLEAN, + "/input/trackpad/touch", + XRT_INPUT_INDEX_TRACKPAD_TOUCH, NULL); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/x", + XRT_INPUT_INDEX_TRACKPAD, &x); + + AddControl(XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE, + "/input/trackpad/y", + XRT_INPUT_INDEX_TRACKPAD, &y); + + vr::VRDriverInput()->CreateHapticComponent( + m_ulPropertyContainer, "/output/haptic", + &m_hapticHandle); + } + + break; + case XRT_DEVICE_VIVE_WAND: break; + case XRT_DEVICE_VIVE_TRACKER_GEN1: + case XRT_DEVICE_VIVE_TRACKER_GEN2: break; + case XRT_DEVICE_PSMV: + case XRT_DEVICE_HYDRA: + case XRT_DEVICE_DAYDREAM: + case XRT_DEVICE_GENERIC_HMD: + default: break; + } + } + + void + PoseUpdateThreadFunction() + { + ovrd_log("Starting controller pose update thread for %s\n", + m_xdev->str); + + while (m_poseUpdating) { + //! @todo figure out the best pose update rate + std::this_thread::sleep_for( + std::chrono::milliseconds(1)); + + if (m_unObjectId != vr::k_unTrackedDeviceIndexInvalid) { + vr::VRServerDriverHost() + ->TrackedDevicePoseUpdated( + m_unObjectId, GetPose(), + sizeof(vr::DriverPose_t)); + } + } + + ovrd_log("Stopping controller pose update thread for %s\n", + m_xdev->str); + } + + vr::EVRInitError + Activate(vr::TrackedDeviceIndex_t unObjectId) + { + ovrd_log("Activating Controller SteamVR[%d]\n", unObjectId); + + if (!m_handed_controller) { + //! @todo handle trackers etc + ovrd_log("Unhandled: %s\n", m_xdev->str); + return vr::VRInitError_Unknown; + } + + m_unObjectId = unObjectId; + + if (this->m_xdev == NULL) { + ovrd_log("Error: xdev NULL\n"); + return vr::VRInitError_Init_InterfaceNotFound; + } + + // clang-format off + + + m_ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(m_unObjectId); + + // return a constant that's not 0 (invalid) or 1 (reserved for Oculus) + vr::VRProperties()->SetUint64Property(m_ulPropertyContainer, vr::Prop_CurrentUniverseId_Uint64, 2); + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_Controller); + + if (m_hand == XRT_HAND_LEFT) { + ovrd_log("Left Controller\n"); + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_LeftHand); + } else if (m_hand == XRT_HAND_RIGHT) { + ovrd_log("Right Controller\n"); + vr::VRProperties()->SetInt32Property(m_ulPropertyContainer, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_RightHand); + } + m_pose.poseIsValid = false; + m_pose.deviceIsConnected = true; + m_pose.result = vr::TrackingResult_Uninitialized; + m_pose.willDriftInYaw = !m_xdev->position_tracking_supported; + + if (m_emulate_index_controller) { + m_input_profile = "{indexcontroller}/input/index_controller_profile.json"; + m_controller_type = "knuckles"; + if (m_hand == XRT_HAND_LEFT) { + m_render_model = "{indexcontroller}valve_controller_knu_1_0_left"; + } else if (m_hand == XRT_HAND_RIGHT) { + m_render_model = "{indexcontroller}valve_controller_knu_1_0_right"; + } + } else { + m_input_profile = "{monado}/input/monado_controller_profile.json"; + m_controller_type = "monado_controller"; + } + + ovrd_log("Using input profile %s\n", m_input_profile); + ovrd_log("Using render model%s\n", m_render_model); + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_InputProfilePath_String, m_input_profile); + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_RenderModelName_String, m_render_model); + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_ModelNumber_String, m_xdev->str); + + // clang-format on + + m_control_count = 0; + if (m_emulate_index_controller) { + AddEmulatedIndexControls(); + } else { + AddMonadoControls(); + } + + ovrd_log("Controller %d activated\n", m_unObjectId); + + m_poseUpdateThread = new std::thread( + &CDeviceDriver_Monado_Controller::PoseUpdateThreadFunction, + this); + if (!m_poseUpdateThread) { + ovrd_log( + "Unable to create pose updated thread for %s\n", + m_xdev->str); + return vr::VRInitError_Driver_Failed; + } + + return vr::VRInitError_None; + } + + void + Deactivate() + { + ovrd_log("deactivate controller\n"); + m_poseUpdating = false; + m_poseUpdateThread->join(); + m_unObjectId = vr::k_unTrackedDeviceIndexInvalid; + } + + void + EnterStandby() + { + ovrd_log("standby controller\n"); + } + + void * + GetComponent(const char *pchComponentNameAndVersion) + { + // deprecated API + // ovrd_log("get controller component %s\n", + // pchComponentNameAndVersion); + return NULL; + } + + /** debug request from a client */ + void + DebugRequest(const char *pchRequest, + char *pchResponseBuffer, + uint32_t unResponseBufferSize) + { + if (unResponseBufferSize >= 1) + pchResponseBuffer[0] = 0; + } + + vr::DriverPose_t + GetPose() + { + m_pose.poseIsValid = true; + m_pose.result = vr::TrackingResult_Running_OK; + m_pose.deviceIsConnected = true; + + enum xrt_input_name grip_name; + + //! @todo better method to find grip name + if (m_xdev->name == XRT_DEVICE_VIVE_WAND) { + grip_name = XRT_INPUT_VIVE_GRIP_POSE; + } else if (m_xdev->name == XRT_DEVICE_INDEX_CONTROLLER) { + grip_name = XRT_INPUT_INDEX_GRIP_POSE; + } else if (m_xdev->name == XRT_DEVICE_PSMV) { + grip_name = XRT_INPUT_PSMV_GRIP_POSE; + } else if (m_xdev->name == XRT_DEVICE_DAYDREAM) { + grip_name = XRT_INPUT_DAYDREAM_POSE; + } else if (m_xdev->name == XRT_DEVICE_HYDRA) { + grip_name = XRT_INPUT_HYDRA_POSE; + } else { + ovrd_log("Unhandled device name %u\n", m_xdev->name); + grip_name = XRT_INPUT_GENERIC_HEAD_POSE; // ??? + } + + timepoint_ns now_ns = os_monotonic_get_ns(); + + struct xrt_space_relation rel; + xrt_device_get_tracked_pose(m_xdev, grip_name, now_ns, &rel); + + struct xrt_pose *offset = &m_xdev->tracking_origin->offset; + + struct xrt_space_graph graph = {}; + m_space_graph_add_relation(&graph, &rel); + m_space_graph_add_pose_if_not_identity(&graph, offset); + m_space_graph_resolve(&graph, &rel); + + apply_pose(&rel, &m_pose); + +#ifdef DUMP_POSE_CONTROLLERS + ovrd_log("get controller %d pose %f %f %f %f, %f %f %f\n", + m_unObjectId, m_pose.qRotation.x, m_pose.qRotation.y, + m_pose.qRotation.z, m_pose.qRotation.w, + m_pose.vecPosition[0], m_pose.vecPosition[1], + m_pose.vecPosition[2]); +#endif + + vr::HmdQuaternion_t identityquat{1, 0, 0, 0}; + m_pose.qWorldFromDriverRotation = identityquat; + m_pose.qDriverFromHeadRotation = identityquat; + m_pose.vecDriverFromHeadTranslation[0] = 0.f; + m_pose.vecDriverFromHeadTranslation[1] = 0.f; + m_pose.vecDriverFromHeadTranslation[2] = 0.f; + + return m_pose; + } + + void + RunFrame() + { + m_xdev->update_inputs(m_xdev); + + + for (int i = 0; i < m_control_count; i++) { + + // ovrd_log("Update %d: %s\n", i, + // m_controls[i].steamvr_control_path); + + enum xrt_input_name input_name = + m_controls[i].monado_input_name; + + struct xrt_input *input = NULL; + for (uint32_t ii = 0; ii < m_xdev->num_inputs; ii++) { + if (m_xdev->inputs[ii].name == input_name) { + input = &m_xdev->inputs[ii]; + break; + } + } + + if (input == NULL) { + ovrd_log("Input for %s not found!\n", + m_controls[i].steamvr_control_path); + continue; + } + + vr::VRInputComponentHandle_t handle = + m_controls[i].control_handle; + + if (m_controls[i].monado_input_type == + XRT_INPUT_TYPE_BOOLEAN) { + bool state = input->value.boolean; + vr::VRDriverInput()->UpdateBooleanComponent( + handle, state, 0); + // ovrd_log("Update %s: %d\n", + // m_controls[i].steamvr_control_path, state); + // printf("Update %s: %d\n", + // m_controls[i].steamvr_control_path, + // state); + } + if (m_controls[i].monado_input_type == + XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE || + m_controls[i].monado_input_type == + XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE) { + + float value; + if (m_controls[i].component.has_component && + m_controls[i].component.x) { + value = input->value.vec2.x; + } else if (m_controls[i] + .component.has_component && + m_controls[i].component.y) { + value = input->value.vec2.y; + } else { + value = input->value.vec1.x; + } + + vr::VRDriverInput()->UpdateScalarComponent( + handle, value, 0); + // ovrd_log("Update %s: %f\n", + // m_controls[i].steamvr_control_path, + // state->x); + // printf("Update %s: %f\n", + // m_controls[i].steamvr_control_path, + // value); + } + } + } + + + vr::VRControllerState_t + GetControllerState() + { + // deprecated API + vr::VRControllerState_t controllerstate; + return controllerstate; + } + + bool + TriggerHapticPulse(uint32_t unAxisId, + uint16_t usPulseDurationMicroseconds) + { + // deprecated API + return false; + } + + std::string + GetSerialNumber() const + { + ovrd_log("get controller serial number: %s\n", m_sSerialNumber); + return m_sSerialNumber; + } + + + struct xrt_device *m_xdev; + vr::DriverPose_t m_pose; + vr::TrackedDeviceIndex_t m_unObjectId; + vr::PropertyContainerHandle_t m_ulPropertyContainer; + + bool m_emulate_index_controller = false; + +private: + char m_sSerialNumber[XRT_DEVICE_NAME_LEN]; + char m_sModelNumber[XRT_DEVICE_NAME_LEN]; + + const char *m_controller_type = NULL; + + const char *m_render_model = NULL; + enum xrt_hand m_hand; + bool m_handed_controller; + + const char *m_input_profile = NULL; + + struct SteamVRDriverControl m_controls[50]; + int m_control_count; + + vr::VRInputComponentHandle_t m_hapticHandle = 0; + + bool m_poseUpdating = true; + std::thread *m_poseUpdateThread = NULL; +}; + +/* + * + * Device driver + * + */ + +class CDeviceDriver_Monado : public vr::ITrackedDeviceServerDriver, + public vr::IVRDisplayComponent +{ +public: + CDeviceDriver_Monado(struct xrt_instance *xinst, + struct xrt_device *xdev) + : m_xdev(xdev) + { + //! @todo latency + m_flSecondsFromVsyncToPhotons = 0.011; + + float ns = + (float)m_xdev->hmd->screens->nominal_frame_interval_ns; + m_flDisplayFrequency = 1. / ns * 1000. * 1000. * 1000.; + ovrd_log("display frequency from device: %f\n", + m_flDisplayFrequency); + + // steamvr can really misbehave when freq is inf or so + if (m_flDisplayFrequency < 0 || m_flDisplayFrequency > 1000) { + ovrd_log("Setting display frequency to 60 Hz!\n"); + m_flDisplayFrequency = 60.; + } + + //! @todo get ipd user setting from monado session + float ipd_meters = 0.063; + struct xrt_vec3 ipd_vec = {ipd_meters, 0, 0}; + + for (int view = 0; view < 2; view++) { + xdev->get_view_pose(xdev, &ipd_vec, view, + &m_view_pose[view]); + } + + //! @todo more versatile IPD calculation + float actual_ipd = + -m_view_pose[0].position.x + m_view_pose[1].position.x; + + m_flIPD = actual_ipd; + + ovrd_log("Seconds from Vsync to Photons: %f\n", + m_flSecondsFromVsyncToPhotons); + ovrd_log("Display Frequency: %f\n", m_flDisplayFrequency); + ovrd_log("IPD: %f\n", m_flIPD); + }; + virtual ~CDeviceDriver_Monado(){}; + + // clang-format off + + // ITrackedDeviceServerDriver + virtual vr::EVRInitError Activate(vr::TrackedDeviceIndex_t unObjectId); + virtual void Deactivate(); + virtual void EnterStandby(); + virtual void *GetComponent(const char *pchComponentNameAndVersion); + virtual void DebugRequest(const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize); + virtual vr::DriverPose_t GetPose(); + + // IVRDisplayComponent + virtual void GetWindowBounds(int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight); + virtual bool IsDisplayOnDesktop(); + virtual bool IsDisplayRealDisplay(); + virtual void GetRecommendedRenderTargetSize(uint32_t *pnWidth, uint32_t *pnHeight); + virtual void GetEyeOutputViewport(vr::EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight); + virtual void GetProjectionRaw(vr::EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom); + virtual vr::DistortionCoordinates_t ComputeDistortion(vr::EVREye eEye, float fU, float fV); + + // clang-format on + +private: + struct xrt_device *m_xdev = NULL; + + // clang-format off + + vr::TrackedDeviceIndex_t m_trackedDeviceIndex = 0; + vr::PropertyContainerHandle_t m_ulPropertyContainer = vr::k_ulInvalidPropertyContainer; + + float m_flSecondsFromVsyncToPhotons = -1; + float m_flDisplayFrequency = -1; + float m_flIPD = -1; + + struct xrt_pose m_view_pose[2]; + + bool m_poseUpdating = true; + std::thread *m_poseUpdateThread = NULL; + virtual void PoseUpdateThreadFunction(); + + // clang-format on +}; + +static void +create_translation_rotation_matrix(struct xrt_pose *pose, + struct vr::HmdMatrix34_t *res) +{ + struct xrt_vec3 t = pose->position; + struct xrt_quat r = pose->orientation; + res->m[0][0] = (1.0f - 2.0f * (r.y * r.y + r.z * r.z)); + res->m[1][0] = (r.x * r.y + r.z * r.w) * 2.0f; + res->m[2][0] = (r.x * r.z - r.y * r.w) * 2.0f; + res->m[0][1] = (r.x * r.y - r.z * r.w) * 2.0f; + res->m[1][1] = (1.0f - 2.0f * (r.x * r.x + r.z * r.z)); + res->m[2][1] = (r.y * r.z + r.x * r.w) * 2.0f; + res->m[0][2] = (r.x * r.z + r.y * r.w) * 2.0f; + res->m[1][2] = (r.y * r.z - r.x * r.w) * 2.0f; + res->m[2][2] = (1.0f - 2.0f * (r.x * r.x + r.y * r.y)); + res->m[0][3] = t.x; + res->m[1][3] = t.y; + res->m[2][3] = t.z; +} + +void +CDeviceDriver_Monado::PoseUpdateThreadFunction() +{ + ovrd_log("Starting HMD pose update thread\n"); + + while (m_poseUpdating) { + //! @todo figure out the best pose update rate + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + vr::VRServerDriverHost()->TrackedDevicePoseUpdated( + m_trackedDeviceIndex, GetPose(), sizeof(vr::DriverPose_t)); + } + ovrd_log("Stopping HMD pose update thread\n"); +} + +vr::EVRInitError +CDeviceDriver_Monado::Activate(vr::TrackedDeviceIndex_t unObjectId) +{ + ovrd_log("Activate tracked device %u: %s\n", unObjectId, m_xdev->str); + + m_trackedDeviceIndex = unObjectId; + + // clang-format off + + m_ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(unObjectId); + //! @todo: proper serial and model number + vr::VRProperties()->SetStringProperty(m_ulPropertyContainer, vr::Prop_ModelNumber_String, m_xdev->str); + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_UserIpdMeters_Float, m_flIPD); + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_UserHeadToEyeDepthMeters_Float, 0.f); + vr::VRProperties()->SetFloatProperty(m_ulPropertyContainer, vr::Prop_DisplayFrequency_Float, m_flDisplayFrequency); + vr::VRProperties()->SetFloatProperty( m_ulPropertyContainer, vr::Prop_SecondsFromVsyncToPhotons_Float, m_flSecondsFromVsyncToPhotons); + + // return a constant that's not 0 (invalid) or 1 (reserved for Oculus) + vr::VRProperties()->SetUint64Property( m_ulPropertyContainer, vr::Prop_CurrentUniverseId_Uint64, 2); + + // avoid "not fullscreen" warnings from vrmonitor + //vr::VRProperties()->SetBoolProperty(m_ulPropertyContainer, vr::Prop_IsOnDesktop_Bool, false); + + // clang-format on + + + //! @todo update when ipd changes + vr::HmdMatrix34_t left; + create_translation_rotation_matrix(&m_view_pose[0], &left); + vr::HmdMatrix34_t right; + create_translation_rotation_matrix(&m_view_pose[1], &right); + vr::VRServerDriverHost()->TrackedDeviceDisplayTransformUpdated( + m_trackedDeviceIndex, left, right); + + + m_poseUpdateThread = new std::thread( + &CDeviceDriver_Monado::PoseUpdateThreadFunction, this); + if (!m_poseUpdateThread) { + ovrd_log("Unable to create pose updated thread for %s\n", + m_xdev->str); + return vr::VRInitError_Driver_Failed; + } + + return vr::VRInitError_None; +} + +void +CDeviceDriver_Monado::Deactivate() +{ + m_poseUpdating = false; + m_poseUpdateThread->join(); + ovrd_log("Deactivate\n"); +} + +void +CDeviceDriver_Monado::EnterStandby() +{ + ovrd_log("Enter Standby\n"); +} + +void * +CDeviceDriver_Monado::GetComponent(const char *pchComponentNameAndVersion) +{ + // clang-format off + if (strcmp(pchComponentNameAndVersion, vr::IVRDisplayComponent_Version) == 0) { + return (vr::IVRDisplayComponent *)this; + } + // clang-format on + + return NULL; +} + +void +CDeviceDriver_Monado::DebugRequest(const char *pchRequest, + char *pchResponseBuffer, + uint32_t unResponseBufferSize) +{ + //! @todo +} + +static inline vr::HmdQuaternion_t +HmdQuaternion_Init(double w, double x, double y, double z) +{ + vr::HmdQuaternion_t quat; + quat.w = w; + quat.x = x; + quat.y = y; + quat.z = z; + return quat; +} + +vr::DriverPose_t +CDeviceDriver_Monado::GetPose() +{ + + struct xrt_space_relation rel; + xrt_device_get_tracked_pose(m_xdev, XRT_INPUT_GENERIC_HEAD_POSE, 1, + &rel); + + struct xrt_pose *offset = &m_xdev->tracking_origin->offset; + + struct xrt_space_graph graph = {}; + m_space_graph_add_relation(&graph, &rel); + m_space_graph_add_pose_if_not_identity(&graph, offset); + m_space_graph_resolve(&graph, &rel); + + vr::DriverPose_t t = {}; + + //! @todo: Monado head model? + t.shouldApplyHeadModel = !m_xdev->position_tracking_supported; + t.willDriftInYaw = !m_xdev->position_tracking_supported; + + t.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0); + t.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0); + + t.poseIsValid = + rel.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT; + t.result = vr::TrackingResult_Running_OK; + t.deviceIsConnected = true; + + apply_pose(&rel, &t); + +#ifdef DUMP_POSE + ovrd_log("get hmd pose %f %f %f %f, %f %f %f\n", t.qRotation.x, + t.qRotation.y, t.qRotation.z, t.qRotation.w, t.vecPosition[0], + t.vecPosition[1], t.vecPosition[2]); +#endif + + //! @todo + // copy_vec3(&rel.angular_velocity, t.vecAngularVelocity); + // copy_vec3(&rel.angular_acceleration, t.vecAngularAcceleration); + + // ovrd_log("Vel: %f %f %f\n", t.vecAngularVelocity[0], + // t.vecAngularVelocity[1], t.vecAngularVelocity[2]); ovrd_log("Accel: + // %f %f %f\n", t.vecAngularAcceleration[0], + // t.vecAngularAcceleration[1], t.vecAngularAcceleration[2]); + + return t; +} + +void +CDeviceDriver_Monado::GetWindowBounds(int32_t *pnX, + int32_t *pnY, + uint32_t *pnWidth, + uint32_t *pnHeight) +{ + // offset in extended mode, e.g. to the right of a 1920x1080 monitor + *pnX = 1920; + *pnY = 0; + + *pnWidth = m_xdev->hmd->views[0].viewport.w_pixels + + m_xdev->hmd->views[1].viewport.w_pixels; + + uint32_t max_height = m_xdev->hmd->views[0].viewport.h_pixels > + m_xdev->hmd->views[1].viewport.h_pixels + ? m_xdev->hmd->views[0].viewport.h_pixels + : m_xdev->hmd->views[1].viewport.h_pixels; + *pnHeight = max_height; + + ovrd_log("Window Bounds: %dx%d\n", *pnWidth, *pnHeight); +} + +bool +CDeviceDriver_Monado::IsDisplayOnDesktop() +{ + return false; +} + +bool +CDeviceDriver_Monado::IsDisplayRealDisplay() +{ + return true; +} + +void +CDeviceDriver_Monado::GetRecommendedRenderTargetSize(uint32_t *pnWidth, + uint32_t *pnHeight) +{ + int scale = debug_get_num_option_scale_percentage(); + float fscale = (float)scale / 100.f; + + + + *pnWidth = m_xdev->hmd->screens[0].w_pixels * fscale; + *pnHeight = m_xdev->hmd->screens[0].h_pixels * fscale; + + ovrd_log("Render Target Size: %dx%d (%fx)\n", *pnWidth, *pnHeight, + fscale); +} + +void +CDeviceDriver_Monado::GetEyeOutputViewport(vr::EVREye eEye, + uint32_t *pnX, + uint32_t *pnY, + uint32_t *pnWidth, + uint32_t *pnHeight) +{ + *pnWidth = m_xdev->hmd->views[eEye].viewport.w_pixels; + *pnHeight = m_xdev->hmd->views[eEye].viewport.h_pixels; + + *pnY = 0; + if (eEye == vr::Eye_Left) { + *pnX = 0; + } else { + *pnX = m_xdev->hmd->views[0].viewport.w_pixels; + } + + ovrd_log("Output Viewport for eye %d: %dx%d offset %dx%d\n", eEye, + *pnWidth, *pnHeight, *pnX, *pnY); +} + +void +CDeviceDriver_Monado::GetProjectionRaw(vr::EVREye eEye, + float *pfLeft, + float *pfRight, + float *pfTop, + float *pfBottom) +{ + *pfLeft = tanf(m_xdev->hmd->views[eEye].fov.angle_left); + *pfRight = tanf(m_xdev->hmd->views[eEye].fov.angle_right); + *pfTop = tanf(-m_xdev->hmd->views[eEye].fov.angle_up); + *pfBottom = tanf(-m_xdev->hmd->views[eEye].fov.angle_down); + ovrd_log("Projection Raw: L%f R%f T%f B%f\n", *pfLeft, *pfRight, *pfTop, + *pfBottom); +} + +vr::DistortionCoordinates_t +CDeviceDriver_Monado::ComputeDistortion(vr::EVREye eEye, float fU, float fV) +{ + /** Used to return the post-distortion UVs for each color channel. + * UVs range from 0 to 1 with 0,0 in the upper left corner of the + * source render target. The 0,0 to 1,1 range covers a single eye. */ + + struct xrt_uv_triplet d; + + if (!m_xdev->compute_distortion(m_xdev, eEye, fU, fV, &d)) { + ovrd_log("Failed to compute distortion for view %d at %f,%f!\n", + eEye, fU, 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; + } + + vr::DistortionCoordinates_t coordinates; + coordinates.rfRed[0] = d.r.x; + coordinates.rfRed[1] = d.r.y; + coordinates.rfGreen[0] = d.g.x; + coordinates.rfGreen[1] = d.g.y; + coordinates.rfBlue[0] = d.b.x; + coordinates.rfBlue[1] = d.b.y; + + // ovrd_log("Computed distortion for view %d at %f,%f -> %f,%f | %f,%f | + // %f,%f!\n", eEye, fU, fV, d.r.x, d.r.y, d.g.x, d.g.y, d.b.x, d.b.y); + + return coordinates; +} + + +/* + * + * Device driver server + * + */ + +class CServerDriver_Monado : public vr::IServerTrackedDeviceProvider +{ +public: + CServerDriver_Monado() : m_MonadoDeviceDriver(NULL) {} + + // clang-format off + virtual ~CServerDriver_Monado() {}; + virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext); + virtual void Cleanup(); + virtual const char *const * GetInterfaceVersions() { return vr::k_InterfaceVersions; } + virtual void RunFrame(); + virtual bool ShouldBlockStandbyMode() { return false; } + virtual void EnterStandby() {} + virtual void LeaveStandby() {} + virtual void HandleHapticEvent(vr::VREvent_t *event); + // clang-format on + +private: + struct xrt_instance *m_xinst = NULL; + struct xrt_device *m_xhmd = NULL; + + CDeviceDriver_Monado *m_MonadoDeviceDriver = NULL; + CDeviceDriver_Monado_Controller *m_left = NULL; + CDeviceDriver_Monado_Controller *m_right = NULL; +}; + +CServerDriver_Monado g_serverDriverMonado; + +#define NUM_XDEVS 16 + +vr::EVRInitError +CServerDriver_Monado::Init(vr::IVRDriverContext *pDriverContext) +{ + + VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext); + ovrd_log_init(vr::VRDriverLog()); + + ovrd_log("Initializing Monado driver\n"); + + //! @todo instance initialization is difficult to replicate + + int ret; + ret = xrt_instance_create(NULL, &m_xinst); + if (ret < 0) { + ovrd_log("Failed to create instance\n"); + return vr::VRInitError_Init_HmdNotFound; + } + + struct xrt_device *xdevs[NUM_XDEVS] = {0}; + + ret = xrt_instance_select(m_xinst, xdevs, NUM_XDEVS); + + int head, left, right; + + u_device_assign_xdev_roles(xdevs, NUM_XDEVS, &head, &left, &right); + + if (ret < 0 || head == XRT_DEVICE_ROLE_UNASSIGNED) { + ovrd_log("Failed to select HMD\n"); + xrt_instance_destroy(&m_xinst); + return vr::VRInitError_Init_HmdNotFound; + } + + m_xhmd = xdevs[head]; + + ovrd_log("Selected HMD %s\n", m_xhmd->str); + m_MonadoDeviceDriver = new CDeviceDriver_Monado(m_xinst, m_xhmd); + //! @todo provide a serial number + vr::VRServerDriverHost()->TrackedDeviceAdded( + m_xhmd->str, vr::TrackedDeviceClass_HMD, m_MonadoDeviceDriver); + + struct xrt_device *left_xdev = NULL; + if (left != XRT_DEVICE_ROLE_UNASSIGNED) { + left_xdev = xdevs[left]; + } + struct xrt_device *right_xdev = NULL; + if (right != XRT_DEVICE_ROLE_UNASSIGNED) { + right_xdev = xdevs[right]; + } + + ovrd_log("Left Controller: %s\n", left_xdev ? left_xdev->str : ""); + ovrd_log("Right Controller: %s\n", right_xdev ? right_xdev->str : ""); + + u_device_setup_tracking_origins(m_xhmd, left_xdev, right_xdev); + + if (left) { + m_left = new CDeviceDriver_Monado_Controller(m_xinst, left_xdev, + XRT_HAND_LEFT); + vr::VRServerDriverHost()->TrackedDeviceAdded( + m_left->GetSerialNumber().c_str(), + vr::TrackedDeviceClass_Controller, m_left); + } + if (right) { + m_right = new CDeviceDriver_Monado_Controller( + m_xinst, right_xdev, XRT_HAND_RIGHT); + vr::VRServerDriverHost()->TrackedDeviceAdded( + m_right->GetSerialNumber().c_str(), + vr::TrackedDeviceClass_Controller, m_right); + } + + return vr::VRInitError_None; +} + +void +CServerDriver_Monado::Cleanup() +{ + if (m_MonadoDeviceDriver != NULL) { + delete m_MonadoDeviceDriver; + m_MonadoDeviceDriver = NULL; + } + + if (m_xhmd) { + xrt_device_destroy(&m_xhmd); + } + + if (m_left) { + xrt_device_destroy(&m_left->m_xdev); + } + if (m_right) { + xrt_device_destroy(&m_right->m_xdev); + } + + if (m_xinst) { + xrt_instance_destroy(&m_xinst); + } + + return; +} + +void +CServerDriver_Monado::HandleHapticEvent(vr::VREvent_t *event) +{ + float freq = event->data.hapticVibration.fFrequency; + float amp = event->data.hapticVibration.fAmplitude; + float duration = event->data.hapticVibration.fDurationSeconds; + + + ovrd_log("Haptic vibration %fs %fHz %famp\n", duration, freq, amp); + + struct xrt_device *xdev = NULL; + if (m_left && m_left->m_ulPropertyContainer == + event->data.hapticVibration.containerHandle) { + xdev = m_left->m_xdev; + ovrd_log("Haptic vibration left\n"); + } else if (m_right && m_right->m_ulPropertyContainer == + event->data.hapticVibration.containerHandle) { + xdev = m_right->m_xdev; + ovrd_log("Haptic vibration right\n"); + } else { + ovrd_log("Haptic vibration ignored\n"); + } + + union xrt_output_value out; + out.vibration.amplitude = amp; + if (duration > 0.00001) { + out.vibration.duration = duration * 1000. * 1000. * 1000.; + } else { + out.vibration.duration = XRT_MIN_HAPTIC_DURATION; + } + out.vibration.frequency = freq; + if (xdev != NULL) { + enum xrt_output_name name; + switch (xdev->name) { + case XRT_DEVICE_INDEX_CONTROLLER: + name = XRT_OUTPUT_NAME_INDEX_HAPTIC; + break; + case XRT_DEVICE_VIVE_WAND: + name = XRT_OUTPUT_NAME_VIVE_HAPTIC; + break; + case XRT_DEVICE_PSMV: + name = XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION; + break; + default: + //! @todo + name = XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION; + break; + } + + xdev->set_output(xdev, name, &out); + } +} + +void +CServerDriver_Monado::RunFrame() +{ + if (m_left) { + m_left->RunFrame(); + } + if (m_right) { + m_right->RunFrame(); + } + + // https://github.com/ValveSoftware/openvr/issues/719#issuecomment-358038640 + struct vr::VREvent_t event; + while (vr::VRServerDriverHost()->PollNextEvent( + &event, sizeof(struct vr::VREvent_t))) { + switch (event.eventType) { + case vr::VREvent_Input_HapticVibration: + HandleHapticEvent(&event); + break; + case vr::VREvent_PropertyChanged: + // ovrd_log("Property changed\n"); + break; + case vr::VREvent_TrackedDeviceActivated: + ovrd_log("Device activated %d\n", + event.trackedDeviceIndex); + break; + case vr::VREvent_TrackedDeviceUserInteractionStarted: + ovrd_log("Device interaction started %d\n", + event.trackedDeviceIndex); + break; + case vr::VREvent_IpdChanged: + ovrd_log("ipd changed to %fm\n", + event.data.ipd.ipdMeters); + break; + case vr::VREvent_ActionBindingReloaded: + ovrd_log("action binding reloaded\n"); + break; + case vr::VREvent_StatusUpdate: + ovrd_log("EVRState: %d\n", + event.data.status.statusState); + break; + + case vr::VREvent_TrackedDeviceRoleChanged: + // device roles are for legacy input + case vr::VREvent_ChaperoneUniverseHasChanged: + case vr::VREvent_ProcessQuit: + case vr::VREvent_QuitAcknowledged: + case vr::VREvent_ProcessDisconnected: + case vr::VREvent_ProcessConnected: + case vr::VREvent_DashboardActivated: + case vr::VREvent_DashboardDeactivated: + case vr::VREvent_Compositor_ChaperoneBoundsShown: + case vr::VREvent_Compositor_ChaperoneBoundsHidden: break; + + default: ovrd_log("Unhandled Event: %d\n", event.eventType); + } + } +} + + +/* + * + * Whatchdog code + * + */ + +class CWatchdogDriver_Monado : public vr::IVRWatchdogProvider +{ +public: + CWatchdogDriver_Monado() + { + ovrd_log("Created watchdog object\n"); + m_pWatchdogThread = nullptr; + } + + // clang-format off + virtual ~CWatchdogDriver_Monado() {}; + virtual vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext); + virtual void Cleanup(); + // clang-format on + +private: + std::thread *m_pWatchdogThread; +}; + +CWatchdogDriver_Monado g_watchdogDriverMonado; +bool g_bExiting = false; + +void +WatchdogThreadFunction() +{ + while (!g_bExiting) { +#if defined(_WINDOWS) + // on windows send the event when the Y key is pressed. + if ((0x01 & GetAsyncKeyState('Y')) != 0) { + // Y key was pressed. + vr::VRWatchdogHost()->WatchdogWakeUp(); + } + std::this_thread::sleep_for(std::chrono::microseconds(500)); +#else + ovrd_log("Watchdog wakeup\n"); + // for the other platforms, just send one every five seconds + std::this_thread::sleep_for(std::chrono::seconds(1)); + vr::VRWatchdogHost()->WatchdogWakeUp(); +#endif + } + + ovrd_log("Watchdog exit\n"); +} + +vr::EVRInitError +CWatchdogDriver_Monado::Init(vr::IVRDriverContext *pDriverContext) +{ + VR_INIT_WATCHDOG_DRIVER_CONTEXT(pDriverContext); + ovrd_log_init(vr::VRDriverLog()); + + // Watchdog mode on Windows starts a thread that listens for the 'Y' key + // on the keyboard to be pressed. A real driver should wait for a system + // button event or something else from the the hardware that signals + // that the VR system should start up. + g_bExiting = false; + + ovrd_log("starting watchdog thread\n"); + + m_pWatchdogThread = new std::thread(WatchdogThreadFunction); + if (!m_pWatchdogThread) { + ovrd_log("Unable to create watchdog thread\n"); + return vr::VRInitError_Driver_Failed; + } + + return vr::VRInitError_None; +} + +void +CWatchdogDriver_Monado::Cleanup() +{ + g_bExiting = true; + if (m_pWatchdogThread) { + m_pWatchdogThread->join(); + delete m_pWatchdogThread; + m_pWatchdogThread = nullptr; + } + + ovrd_log_cleanup(); +} + + +/* + * + * 'Exported' functions. + * + */ + +void * +ovrd_hmd_driver_impl(const char *pInterfaceName, int *pReturnCode) +{ + // clang-format off + if (0 == strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName)) { + return &g_serverDriverMonado; + } + if (0 == strcmp(vr::IVRWatchdogProvider_Version, pInterfaceName)) { + return &g_watchdogDriverMonado; + } + // clang-format on + + ovrd_log("Unimplemented interface: %s\n", pInterfaceName); + + if (pReturnCode) { + *pReturnCode = vr::VRInitError_Init_InterfaceNotFound; + } + + return NULL; +} diff --git a/src/xrt/state_trackers/steamvr_drv/ovrd_interface.h b/src/xrt/state_trackers/steamvr_drv/ovrd_interface.h new file mode 100644 index 000000000..d6a13c3a9 --- /dev/null +++ b/src/xrt/state_trackers/steamvr_drv/ovrd_interface.h @@ -0,0 +1,29 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Interface to the Monado SteamVR Driver exporter. + * @author Jakob Bornecrantz + * @ingroup st_ovrd + */ + +#pragma once + +#include "xrt/xrt_defines.h" + +/*! + * @defgroup st_ovrd SteamVR driver provider + * + * Wraps a @ref xrt_instance and one or more @ref xrt_device and exposes those + * to SteamVR via the OpenVR driver interface. + * + * @ingroup xrt + */ + +/*! + * Implementation of the HmdDriverFactory function. + * + * @ingroup st_ovrd + */ +void * +ovrd_hmd_driver_impl(const char *pInterfaceName, int *pReturnCode); diff --git a/src/xrt/state_trackers/steamvr_drv/ovrd_log.hpp b/src/xrt/state_trackers/steamvr_drv/ovrd_log.hpp new file mode 100644 index 000000000..683dffbad --- /dev/null +++ b/src/xrt/state_trackers/steamvr_drv/ovrd_log.hpp @@ -0,0 +1,62 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Logger code. + * @author Jakob Bornecrantz + * @ingroup st_ovrd + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "xrt/xrt_compiler.h" + +#ifdef __cplusplus +} +#endif + +#include "openvr_driver.h" + +static vr::IVRDriverLog *s_pLogFile = NULL; + +static inline void +ovrd_log_init(vr::IVRDriverLog *pDriverLog) +{ + // Noop + s_pLogFile = vr::VRDriverLog(); +} + +// Can not use the XRT_PRINTF_FORMAT macro on a function definition. +static inline void +ovrd_log(const char *fmt, ...) XRT_PRINTF_FORMAT(1, 2); + +static inline void +ovrd_log(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + char buf[1024]; +#if defined(WIN32) + vsprintf_s(buf, pMsgFormat, args); +#else + vsnprintf(buf, sizeof(buf), fmt, args); +#endif + + if (s_pLogFile) + s_pLogFile->Log(buf); + + va_end(args); +} + +static inline void +ovrd_log_cleanup() +{ + s_pLogFile = nullptr; +} diff --git a/src/xrt/targets/CMakeLists.txt b/src/xrt/targets/CMakeLists.txt index 5a805fcda..58a518f91 100644 --- a/src/xrt/targets/CMakeLists.txt +++ b/src/xrt/targets/CMakeLists.txt @@ -29,3 +29,7 @@ if(XRT_FEATURE_SERVICE AND XRT_FEATURE_OPENXR) add_subdirectory(service) endif() endif() + +if(XRT_FEATURE_STEAMVR_PLUGIN) + add_subdirectory(steamvr_drv) +endif() diff --git a/src/xrt/targets/meson.build b/src/xrt/targets/meson.build index e7b07947e..32899dc54 100644 --- a/src/xrt/targets/meson.build +++ b/src/xrt/targets/meson.build @@ -78,3 +78,6 @@ if get_option('service') subdir('ctl') endif +if get_option('steamvr_plugin') + subdir('steamvr_drv') +endif diff --git a/src/xrt/targets/steamvr_drv/CMakeLists.txt b/src/xrt/targets/steamvr_drv/CMakeLists.txt new file mode 100644 index 000000000..85d34bfb3 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + + +add_library(driver_monado MODULE + main.c + ) + +target_link_libraries(driver_monado PRIVATE + xrt-external-openvr + aux_util + st_ovrd + st_prober + target_lists + target_instance_no_comp + ) + + +# meta data that the steamvr plugin needs in the base directory of the steamvr plugin +file(COPY driver.vrdrivermanifest DESTINATION ${CMAKE_BINARY_DIR}/steamvr-monado) +file(COPY resources DESTINATION ${CMAKE_BINARY_DIR}/steamvr-monado) + +#determine the output directory for the steamvr plugin +if (WIN32) + # FIXME need to account for different architectures + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/win64 CACHE TYPE INTERNAL) + else() + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/win32 CACHE TYPE INTERNAL) + endif() +elseif(APPLE) + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/osx64 CACHE STRING INTERNAL) + else() + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/osx32 CACHE STRING INTERNAL) + endif() +elseif(NOT ANDROID) + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/linux64 CACHE STRING INTERNAL) + else() + set(PLUGINDIR ${CMAKE_BINARY_DIR}/steamvr-monado/bin/linux32 CACHE STRING INTERNAL) + endif() +endif() + +MESSAGE("SteamVR plugin path: ${PLUGINDIR}") +set_target_properties(driver_monado PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PLUGINDIR}") + +# don't add lib prefix to driver_monado.so +SET_TARGET_PROPERTIES(driver_monado PROPERTIES PREFIX "") + +install(DIRECTORY "${PROJECT_BINARY_DIR}/steamvr-monado" DESTINATION "${CMAKE_INSTALL_PREFIX}/share") diff --git a/src/xrt/targets/steamvr_drv/copy_assets.py b/src/xrt/targets/steamvr_drv/copy_assets.py new file mode 100644 index 000000000..1681f3e4c --- /dev/null +++ b/src/xrt/targets/steamvr_drv/copy_assets.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +import os, sys, shutil + +print(sys.argv[1], sys.argv[2], sys.argv[3]) + +is_file = sys.argv[1] == "FILE" +is_dir = sys.argv[1] == "DIRECTORY" + +# get absolute input and output paths +input_path = os.path.join( + os.getenv('MESON_SOURCE_ROOT'), + os.getenv('MESON_SUBDIR'), + sys.argv[2]) + +output_path = os.path.join( + os.getenv('MESON_BUILD_ROOT'), + sys.argv[3]) + +# make sure destination directory exists +os.makedirs(os.path.dirname(output_path), exist_ok=True) + +if is_file: + shutil.copyfile(input_path, output_path) +elif is_dir: + shutil.copytree(input_path, output_path) + +print("Copying asset " + str(input_path) + " to " + str(output_path)) diff --git a/src/xrt/targets/steamvr_drv/copy_plugin.py b/src/xrt/targets/steamvr_drv/copy_plugin.py new file mode 100644 index 000000000..e1719d8eb --- /dev/null +++ b/src/xrt/targets/steamvr_drv/copy_plugin.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +import os, sys, shutil + +# get absolute input and output paths +input_path = os.path.join( + sys.argv[1]) + +output_path = os.path.join( + sys.argv[2]) + +# make sure destination directory exists +os.makedirs(os.path.dirname(output_path), exist_ok=True) + +shutil.copyfile(input_path, output_path) + +print("Copying plugin " + str(input_path) + " to " + str(output_path)) diff --git a/src/xrt/targets/steamvr_drv/driver.vrdrivermanifest b/src/xrt/targets/steamvr_drv/driver.vrdrivermanifest new file mode 100644 index 000000000..e572158e6 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/driver.vrdrivermanifest @@ -0,0 +1,11 @@ +{ + "alwaysActivate": false, + "name" : "monado", + "directory" : "", + "resourceOnly" : false, + + "hmd_presence" : + [ + "*.*" + ] +} diff --git a/src/xrt/targets/steamvr_drv/main.c b/src/xrt/targets/steamvr_drv/main.c new file mode 100644 index 000000000..016574ff0 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/main.c @@ -0,0 +1,28 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Very simple target main file to export the right symbol. + * @author Jakob Bornecrantz + * @author Christoph Haag + * @ingroup st_ovrd + */ + +#include "ovrd_interface.h" + +#if defined(_WIN32) +#define HMD_DLL_EXPORT __declspec(dllexport) +#define HMD_DLL_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__) +#define HMD_DLL_EXPORT __attribute__((visibility("default"))) +#define HMD_DLL_IMPORT +#else +#error "Unsupported Platform." +#endif + + +HMD_DLL_EXPORT void * +HmdDriverFactory(const char *pInterfaceName, int *pReturnCode) +{ + return ovrd_hmd_driver_impl(pInterfaceName, pReturnCode); +} diff --git a/src/xrt/targets/steamvr_drv/meson.build b/src/xrt/targets/steamvr_drv/meson.build new file mode 100644 index 000000000..a7d94136d --- /dev/null +++ b/src/xrt/targets/steamvr_drv/meson.build @@ -0,0 +1,65 @@ +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +plugin_dir = 'steamvr-monado' + +driver_monado = shared_library( + 'driver_monado', + files( + 'main.c' + ), + link_whole: [ + lib_target_instance_no_comp, + lib_st_ovrd, + ], + include_directories: [ + openvr_include, + st_ovrd_include, + openvr_include, + aux_include, + xrt_include, + ], + link_with : driver_libs, + dependencies : [pthreads, libjpeg], + # build 'driver_monado.so' instead of 'libdriver_monado.so' + name_prefix: '', +) + +copy_asset = find_program('copy_assets.py') +copy_plugin = find_program('copy_plugin.py') + + +run_command(copy_asset, 'FILE', 'driver.vrdrivermanifest', join_paths(plugin_dir, 'driver.vrdrivermanifest')) +run_command(copy_asset, 'DIRECTORY', 'resources', join_paths(plugin_dir, 'resources')) + +plugin_archdir = '' + +if host_machine.system() == 'windows' + if host_machine.cpu_family() == 'x86' + plugin_archdir = 'win32' + elif host_machine.cpu_family() == 'x86_64' + plugin_archdir = 'win64' + endif +elif host_machine.system() == 'linux' + if host_machine.cpu_family() == 'x86' + plugin_archdir = 'linux32' + elif host_machine.cpu_family() == 'x86_64' + plugin_archdir = 'linux64' + endif +endif + +custom_target( + 'plugin_copy', + depends : driver_monado, + input : driver_monado, + output : 'fake_plugin', + command : [copy_plugin, '@INPUT@', join_paths(plugin_dir, 'bin', plugin_archdir, '@PLAINNAME@')], + build_by_default : true +) + +if meson.version().version_compare('<0.56') + build_root = meson.build_root() +else + build_root = meson.project_build_root() +endif +install_subdir(join_paths(build_root, plugin_dir), install_dir: join_paths(get_option('prefix'), 'share')) diff --git a/src/xrt/targets/steamvr_drv/resources/driver.vrresources b/src/xrt/targets/steamvr_drv/resources/driver.vrresources new file mode 100644 index 000000000..f7e588667 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/resources/driver.vrresources @@ -0,0 +1,47 @@ +{ + "jsonid" : "vrresources", + "statusicons" : { + "HMD" : { + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/headset_monado_status_off.png", + "Prop_NamedIconPathDeviceSearching_String" : "{monado}/icons/headset_monado_status_searching.gif", + "Prop_NamedIconPathDeviceSearchingAlert_String" : "{monado}/icons/headset_monado_status_searching_alert.gif", + "Prop_NamedIconPathDeviceReady_String" : "{monado}/icons/headset_monado_status_ready.png", + "Prop_NamedIconPathDeviceReadyAlert_String" : "{monado}/icons/headset_monado_status_ready_alert.png", + "Prop_NamedIconPathDeviceNotReady_String" : "{monado}/icons/headset_monado_status_error.png", + "Prop_NamedIconPathDeviceStandby_String" : "{monado}/icons/headset_monado_status_standby.png", + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_monado_status_ready_low.png" + }, + + "Model-v Defaults" : { + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/headset_monado_status_off.png", + "Prop_NamedIconPathDeviceSearching_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceSearchingAlert_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceReady_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceReadyAlert_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceNotReady_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceStandby_String" : "Prop_NamedIconPathDeviceOff_String", + "Prop_NamedIconPathDeviceAlertLow_String" : "Prop_NamedIconPathDeviceOff_String" + }, + + "Model-v1.0" : { + "Alias" : "Model-v Defaults", + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_model1_alertlow.png" + }, + + "Model-v2.0" : { + "Alias" : "Model-v1.0", + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/headset_model2_alertlow.png" + }, + + "Controller" : { + "Prop_NamedIconPathDeviceOff_String" : "{monado}/icons/controller_status_off.png", + "Prop_NamedIconPathDeviceSearching_String" : "{monado}/icons/controller_status_searching.gif", + "Prop_NamedIconPathDeviceSearchingAlert_String" : "{monado}/icons/controller_status_searching_alert.gif", + "Prop_NamedIconPathDeviceReady_String" : "{monado}/icons/controller_status_ready.png", + "Prop_NamedIconPathDeviceReadyAlert_String" : "{monado}/icons/controller_status_ready_alert.png", + "Prop_NamedIconPathDeviceNotReady_String" : "{monado}/icons/controller_status_error.png", + "Prop_NamedIconPathDeviceStandby_String" : "{monado}/icons/controller_status_standby.png", + "Prop_NamedIconPathDeviceAlertLow_String" : "{monado}/icons/controller_status_ready_low.png" + } + } +} diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_error.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_error.png new file mode 100644 index 000000000..1cc1636eb Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_error.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_off.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_off.png new file mode 100644 index 000000000..4403086be Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_off.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready.png new file mode 100644 index 000000000..8a5be67c6 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_alert.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_alert.png new file mode 100644 index 000000000..fdba49077 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_alert.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_low.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_low.png new file mode 100644 index 000000000..671e0cee6 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_ready_low.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching.gif b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching.gif new file mode 100644 index 000000000..3dde000e3 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching.gif differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching_alert.gif b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching_alert.gif new file mode 100644 index 000000000..a75e4a627 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_searching_alert.gif differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/controller_status_standby.png b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_standby.png new file mode 100644 index 000000000..f4007b034 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/controller_status_standby.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_error.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_error.png new file mode 100644 index 000000000..c9401de23 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_error.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_off.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_off.png new file mode 100644 index 000000000..22e884f82 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_off.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready.png new file mode 100644 index 000000000..5fe9fe20c Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_alert.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_alert.png new file mode 100644 index 000000000..1da03a463 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_alert.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_low.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_low.png new file mode 100644 index 000000000..bbaf61d53 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_ready_low.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching.gif b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching.gif new file mode 100644 index 000000000..85bb8134d Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching.gif differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching_alert.gif b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching_alert.gif new file mode 100644 index 000000000..7c23fae09 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_searching_alert.gif differ diff --git a/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_standby.png b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_standby.png new file mode 100644 index 000000000..ff9e40254 Binary files /dev/null and b/src/xrt/targets/steamvr_drv/resources/icons/headset_monado_status_standby.png differ diff --git a/src/xrt/targets/steamvr_drv/resources/localization/localization.json b/src/xrt/targets/steamvr_drv/resources/localization/localization.json new file mode 100644 index 000000000..208091b68 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/resources/localization/localization.json @@ -0,0 +1,6 @@ +[ + { + "language_tag": "en_US", + "monado_controller" : "Monado Driver Controller" + } +] diff --git a/src/xrt/targets/steamvr_drv/resources/settings/default.vrsettings b/src/xrt/targets/steamvr_drv/resources/settings/default.vrsettings new file mode 100644 index 000000000..039cf3fa6 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/resources/settings/default.vrsettings @@ -0,0 +1,5 @@ +{ + "driver_monado" : { + "enable" : true + } +} diff --git a/src/xrt/targets/steamvr_drv/steamvr.vrsettings b/src/xrt/targets/steamvr_drv/steamvr.vrsettings new file mode 100644 index 000000000..a49a22921 --- /dev/null +++ b/src/xrt/targets/steamvr_drv/steamvr.vrsettings @@ -0,0 +1,16 @@ +{ + "jsonid" : "vrsettings", + "driver_lighthouse": { + "enable": false + }, + "driver_oculus": { + "enable": false + }, + "driver_oculus_legacy": { + "enable": false + }, + "driver_monado" : { + "enable": true + }, + "version" : "1" +}