summaryrefslogtreecommitdiffhomepage
path: root/tracker-steamvr/steamvr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tracker-steamvr/steamvr.cpp')
-rw-r--r--tracker-steamvr/steamvr.cpp268
1 files changed, 149 insertions, 119 deletions
diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp
index fac0346a..05b5ed35 100644
--- a/tracker-steamvr/steamvr.cpp
+++ b/tracker-steamvr/steamvr.cpp
@@ -19,12 +19,9 @@
#include "steamvr.hpp"
#include "api/plugin-api.hpp"
-#include "compat/util.hpp"
-#include <cstdlib>
#include <cmath>
-#include <type_traits>
-#include <algorithm>
+#include <cstdlib>
#include <QMessageBox>
#include <QDebug>
@@ -32,76 +29,87 @@
QMutex device_list::mtx(QMutex::Recursive);
template<typename F>
-static auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), error_t()))
+auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), vr_error_t()))
{
QMutexLocker l(&device_list::mtx);
- error_t e; vr_t v;
- std::tie(v, e) = device_list::vr_init();
+ auto [v, e] = device_list::vr_init();
return fun(v, e);
}
void device_list::fill_device_specs(QList<device_spec>& list)
{
- with_vr_lock([&](vr_t v, error_t)
+ with_vr_lock([&](vr_t v, vr_error_t)
{
list.clear();
- list.reserve(max_devices);
pose_t device_states[max_devices];
- if (v)
+ if (!v)
+ return;
+
+ v->GetDeviceToAbsoluteTrackingPose(origin::TrackingUniverseSeated, 0,
+ device_states, vr::k_unMaxTrackedDeviceCount);
+
+ constexpr unsigned bufsiz = vr::k_unMaxPropertyStringSize;
+ static char str[bufsiz+1] {}; // vr_lock prevents reentrancy
+
+ for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; k++)
{
- v->GetDeviceToAbsoluteTrackingPose(origin::TrackingUniverseSeated, 0,
- device_states, vr::k_unMaxTrackedDeviceCount);
+ if (v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_Invalid)
+ {
+ qDebug() << "steamvr: no device with index";
+ continue;
+ }
+
+ if (!device_states[k].bDeviceIsConnected)
+ {
+ qDebug() << "steamvr: device not connected but proceeding";
+ continue;
+ }
+
+ unsigned len;
+
+ len = v->GetStringTrackedDeviceProperty(k, vr::ETrackedDeviceProperty::Prop_SerialNumber_String, str, bufsiz);
+ if (!len)
+ {
+ qDebug() << "steamvr: getting serial number failed for" << k;
+ continue;
+ }
- static constexpr unsigned bufsiz = vr::k_unTrackingStringSize;
- static char str[bufsiz] {}; // vr_lock prevents reentrancy
+ device_spec dev;
- for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; k++)
+ dev.serial = str;
+
+ len = v->GetStringTrackedDeviceProperty(k, vr::ETrackedDeviceProperty::Prop_ModelNumber_String, str, bufsiz);
+ if (!len)
+ {
+ qDebug() << "steamvr: getting model number failed for" << k;
+ continue;
+ }
+
+ switch (v->GetTrackedDeviceClass(k))
{
- if (v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_Invalid ||
- v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_TrackingReference)
- continue;
-
- if (!device_states[k].bDeviceIsConnected)
- continue;
-
- unsigned len;
-
- len = v->GetStringTrackedDeviceProperty(k, vr::ETrackedDeviceProperty::Prop_SerialNumber_String, str, bufsiz);
- if (!len)
- {
- qDebug() << "steamvr: getting serial number failed for" << k;
- continue;
- }
-
- device_spec dev;
-
- dev.serial = str;
-
- len = v->GetStringTrackedDeviceProperty(k, vr::ETrackedDeviceProperty::Prop_ModelNumber_String, str, bufsiz);
- if (!len)
- {
- qDebug() << "steamvr: getting model number failed for" << k;
- continue;
- }
-
- switch (v->GetTrackedDeviceClass(k))
- {
- case vr::ETrackedDeviceClass::TrackedDeviceClass_HMD:
- dev.type = "HMD"; break;
- case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller:
- dev.type = "Controller"; break;
- default:
- dev.type = "Unknown"; break;
- }
-
- dev.model = str;
- dev.pose = device_states[k];
- dev.k = k;
-
- list.push_back(dev);
+ using enum vr::ETrackedDeviceClass;
+ case TrackedDeviceClass_HMD:
+ dev.type = "HMD"; break;
+ case TrackedDeviceClass_Controller:
+ dev.type = "Controller"; break;
+ case TrackedDeviceClass_TrackingReference:
+ dev.type = "Tracking reference"; break;
+ case TrackedDeviceClass_DisplayRedirect:
+ dev.type = "Display redirect"; break;
+ case TrackedDeviceClass_GenericTracker:
+ dev.type = "Generic"; break;
+ default:
+ dev.type = "Unknown"; break;
}
+
+ dev.model = str;
+ dev.pose = device_states[k];
+ dev.k = k;
+ dev.is_connected = device_states[k].bDeviceIsConnected;
+
+ list.push_back(dev);
}
});
}
@@ -113,15 +121,17 @@ device_list::device_list()
void device_list::refresh_device_list()
{
+ device_specs.clear();
+ device_specs.reserve(max_devices);
fill_device_specs(device_specs);
}
device_list::maybe_pose device_list::get_pose(int k)
{
- if (k < 0 || !(k < max_devices))
+ if (!(unsigned(k) < max_devices))
return maybe_pose(false, pose_t{});
- return with_vr_lock([k](vr_t v, error_t)
+ return with_vr_lock([k](vr_t v, vr_error_t)
{
static pose_t poses[max_devices] {}; // vr_lock removes reentrancy
@@ -131,14 +141,14 @@ device_list::maybe_pose device_list::get_pose(int k)
const pose_t& pose = poses[k];
if (pose.bPoseIsValid && pose.bDeviceIsConnected)
- return maybe_pose(true, poses[k]);
+ return maybe_pose{ true, poses[k] };
else
- once_only(qDebug() << "steamvr:"
+ eval_once(qDebug() << "steamvr:"
<< "no valid pose from device" << k
<< "valid" << pose.bPoseIsValid
<< "connected" << pose.bDeviceIsConnected);
- return maybe_pose(false, pose_t{});
+ return maybe_pose{ false, {} };
});
}
@@ -150,41 +160,44 @@ tt device_list::vr_init()
tt device_list::vr_init_()
{
- error_t error = error_t::VRInitError_Unknown;
+ vr_error_t error = vr_error_t::VRInitError_Unknown;
vr_t v = vr::VR_Init(&error, vr::EVRApplicationType::VRApplication_Other);
if (v)
std::atexit(vr::VR_Shutdown);
else
- qDebug() << "steamvr: init failure" << error << device_list::strerror(error);
+ qDebug() << "steamvr: init failure" << error << device_list::error_string(error);
- return tt(v, error);
+ return { v, error };
}
-QString device_list::strerror(error_t err)
+QString device_list::error_string(vr_error_t err)
{
- const char* str(vr::VR_GetVRInitErrorAsSymbol(err));
- return QString(str ? str : "No description");
-}
+ const char* str = vr::VR_GetVRInitErrorAsSymbol(err);
+ const char* desc = vr::VR_GetVRInitErrorAsEnglishDescription(err);
-steamvr::steamvr() : device_index(-1)
-{
-}
+ if (!desc)
+ desc = "No description";
-steamvr::~steamvr()
-{
+ if (str)
+ return QStringLiteral("%1: %2").arg(str, desc);
+ else
+ return { "Unknown error" };
}
-void steamvr::start_tracker(QFrame*)
+steamvr::steamvr() = default;
+steamvr::~steamvr() = default;
+
+module_status steamvr::start_tracker(QFrame*)
{
- with_vr_lock([this](vr_t v, error_t e)
+ return with_vr_lock([this](vr_t v, vr_error_t e)
{
+ QString err;
+
if (!v)
{
- QMessageBox::warning(nullptr,
- tr("SteamVR init error"), device_list::strerror(e),
- QMessageBox::Close, QMessageBox::NoButton);
- return;
+ err = device_list::error_string(e);
+ return error(err);
}
const QString serial = s.device_serial().toString();
@@ -193,54 +206,53 @@ void steamvr::start_tracker(QFrame*)
const int sz = specs.count();
if (sz == 0)
- {
- QMessageBox::warning(nullptr,
- tr("SteamVR init error"),
- tr("No HMD connected"),
- QMessageBox::Close, QMessageBox::NoButton);
- return;
- }
-
- device_index = -1;
+ err = tr("No HMD connected");
for (const device_spec& spec : specs)
{
if (serial == "" || serial == spec.to_string())
{
- device_index = int(spec.k);
+ device_index = spec.k;
break;
}
}
- if (device_index == -1)
+ if (device_index == UINT_MAX && err.isEmpty())
+ err = tr("Can't find device with that serial");
+
+ if (err.isEmpty())
{
- QMessageBox::warning(nullptr,
- tr("SteamVR init error"),
- tr("Can't find device with that serial"),
- QMessageBox::Close, QMessageBox::NoButton);
+ if (auto* c = vr::VRCompositor(); c != nullptr)
+ {
+ c->SetTrackingSpace(origin::TrackingUniverseSeated);
+ return status_ok();
+ }
+ else
+ return error("vr::VRCompositor == NULL");
}
+
+ return error(err);
});
}
void steamvr::data(double* data)
{
- if (device_index != -1)
+ if (device_index != UINT_MAX)
{
- pose_t pose; bool ok;
- std::tie(ok, pose) = device_list::get_pose(device_index);
+ auto [ok, pose] = device_list::get_pose(device_index);
if (ok)
{
- static constexpr int c = 10;
+ constexpr int c = 10;
const auto& result = pose.mDeviceToAbsoluteTracking;
- data[TX] = -result.m[0][3] * c;
- data[TY] = result.m[1][3] * c;
- data[TZ] = result.m[2][3] * c;
+ data[TX] = (double)(-result.m[0][3] * c);
+ data[TY] = (double)(result.m[1][3] * c);
+ data[TZ] = (double)(result.m[2][3] * c);
matrix_to_euler(data[Yaw], data[Pitch], data[Roll], result);
- static constexpr double r2d = 180 / M_PI;
+ constexpr double r2d = 180 / M_PI;
data[Yaw] *= r2d; data[Pitch] *= r2d; data[Roll] *= r2d;
}
}
@@ -248,28 +260,41 @@ void steamvr::data(double* data)
bool steamvr::center()
{
- with_vr_lock([&](vr_t v, error_t)
+ return with_vr_lock([&](vr_t v, vr_error_t)
{
if (v)
- // Reset yaw and position
- v->ResetSeatedZeroPose();
+ {
+ if (v->GetTrackedDeviceClass(device_index) == vr::ETrackedDeviceClass::TrackedDeviceClass_HMD)
+ {
+ auto* c = vr::VRChaperone();
+ if (!c)
+ {
+ eval_once(qDebug() << "steamvr: vr::VRChaperone == NULL");
+ return false;
+ }
+ else
+ {
+ c->ResetZeroPose(origin::TrackingUniverseSeated);
+ // Use chaperone universe real world up instead of opentrack's initial pose centering
+ // Note: Controllers will be centered based on initial headset position.
+ return true;
+ }
+ }
+ else
+ // with controllers, resetting the seated pose does nothing
+ return false;
+ }
+ return false;
});
- // Use chaperone universe real world up instead of OpenTrack's initial pose centering
- // Note: Controllers will be centered based on initial headset position.
- // TODO: may want to center controller/tracker yaw and position (only) when used alone
- return true;
}
void steamvr::matrix_to_euler(double& yaw, double& pitch, double& roll, const vr::HmdMatrix34_t& result)
{
- using std::atan2;
- using std::sqrt;
+ using d = double;
- yaw = atan2((double)-result.m[2][0], sqrt(double(result.m[2][1]) * result.m[2][1] + result.m[2][2] * result.m[2][2]));
- pitch = atan2((double)result.m[2][1], (double)result.m[2][2]);
- roll = atan2((double)result.m[1][0], (double)result.m[0][0]);
-
- // TODO: gimbal lock avoidance?
+ yaw = std::atan2(d(result.m[2][0]), d(result.m[0][0]));
+ pitch = std::atan2(-d(result.m[1][2]), d(result.m[1][1]));
+ roll = std::asin(d(result.m[1][0]));
}
steamvr_dialog::steamvr_dialog()
@@ -284,7 +309,12 @@ steamvr_dialog::steamvr_dialog()
device_list list;
for (const device_spec& spec : list.devices())
- ui.device->addItem(spec.to_string(), spec.to_string());
+ {
+ QString text = spec.to_string();
+ if (!spec.is_connected)
+ text = QStringLiteral("%1 [disconnected]").arg(text);
+ ui.device->addItem(text, spec.to_string());
+ }
tie_setting(s.device_serial, ui.device);
}
@@ -304,7 +334,7 @@ void steamvr_dialog::doCancel()
QString device_spec::to_string() const
{
- return QStringLiteral("<%1> %2 [%3]").arg(type).arg(model).arg(serial);
+ return QStringLiteral("<%1> %2 [%3]").arg(type, model, serial);
}
OPENTRACK_DECLARE_TRACKER(steamvr, steamvr_dialog, steamvr_metadata)