mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-25 16:11:45 +00:00
1392 lines
42 KiB
C++
1392 lines
42 KiB
C++
// Copyright 2020, Collabora, Ltd.
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
/*!
|
|
* @file
|
|
* @brief Main driver code for @ref st_ovrd.
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
* @author Christoph Haag <christoph.haag@collabora.com>
|
|
* @ingroup st_ovrd
|
|
*/
|
|
|
|
#include <cstring>
|
|
#include <thread>
|
|
#include <sstream>
|
|
|
|
#include "ovrd_log.hpp"
|
|
#include "openvr_driver.h"
|
|
|
|
extern "C" {
|
|
#include "ovrd_interface.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <math/m_space.h>
|
|
#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"
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
|
|
|
//! 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 = {};
|
|
|
|
// append xrt_hand because SteamVR serial must be unique
|
|
std::stringstream ss;
|
|
ss << "[Monado] " << xdev->str << " " << hand;
|
|
std::string name = ss.str();
|
|
|
|
strncpy(m_sSerialNumber, name.c_str(), XRT_DEVICE_NAME_LEN);
|
|
strncpy(m_sModelNumber, name.c_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);
|
|
// U_LOG_D("Update %s: %d",
|
|
// 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);
|
|
// U_LOG_D("Update %s: %f",
|
|
// 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()
|
|
{
|
|
|
|
timepoint_ns now_ns = os_monotonic_get_ns();
|
|
struct xrt_space_relation rel;
|
|
xrt_device_get_tracked_pose(m_xdev, XRT_INPUT_GENERIC_HEAD_POSE, 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);
|
|
|
|
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->screens[0].w_pixels;
|
|
*pnHeight = m_xdev->hmd->screens[0].h_pixels;
|
|
;
|
|
|
|
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;
|
|
|
|
*pnX = m_xdev->hmd->views[eEye].viewport.x_pixels;
|
|
*pnY = m_xdev->hmd->views[eEye].viewport.y_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_vec2 *rot = m_xdev->hmd->views[eEye].rot.vecs;
|
|
|
|
// multiply 2x2 rotation matrix with fU, fV scaled to [-1, 1]
|
|
float U = rot[0].x * (fU * 2 - 1) + rot[0].y * (fV * 2 - 1);
|
|
float V = rot[1].x * (fU * 2 - 1) + rot[1].y * (fV * 2 - 1);
|
|
|
|
// scale U, V back to [0, 1]
|
|
U = (U + 1) / 2;
|
|
V = (V + 1) / 2;
|
|
|
|
struct xrt_uv_triplet d;
|
|
|
|
if (!m_xdev->compute_distortion(m_xdev, eEye, U, V, &d)) {
|
|
ovrd_log("Failed to compute distortion for view %d at %f,%f!\n", eEye, U, V);
|
|
|
|
vr::DistortionCoordinates_t coordinates;
|
|
coordinates.rfBlue[0] = U;
|
|
coordinates.rfBlue[1] = V;
|
|
coordinates.rfGreen[0] = U;
|
|
coordinates.rfGreen[1] = V;
|
|
coordinates.rfRed[0] = U;
|
|
coordinates.rfRed[1] = V;
|
|
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, U, V, 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 : "");
|
|
|
|
// use steamvr room setup instead
|
|
struct xrt_vec3 offset = {0, 0, 0};
|
|
u_device_setup_tracking_origins(m_xhmd, left_xdev, right_xdev, &offset);
|
|
|
|
if (left != XRT_DEVICE_ROLE_UNASSIGNED) {
|
|
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 != XRT_DEVICE_ROLE_UNASSIGNED) {
|
|
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;
|
|
}
|
|
|
|
#pragma GCC diagnostic pop
|