diff options
-rw-r--r-- | tracker-steamvr/dialog.cpp | 22 | ||||
-rw-r--r-- | tracker-steamvr/dialog.ui | 57 | ||||
-rw-r--r-- | tracker-steamvr/steamvr.cpp | 281 | ||||
-rw-r--r-- | tracker-steamvr/steamvr.hpp | 66 |
4 files changed, 296 insertions, 130 deletions
diff --git a/tracker-steamvr/dialog.cpp b/tracker-steamvr/dialog.cpp deleted file mode 100644 index bbb8866d..00000000 --- a/tracker-steamvr/dialog.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "steamvr.hpp" -#include "api/plugin-api.hpp" - -steamvr_dialog::steamvr_dialog() -{ - ui.setupUi(this); - - connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); - connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); -} - -void steamvr_dialog::doOK() -{ - s.b->save(); - close(); -} - -void steamvr_dialog::doCancel() -{ - close(); -} - diff --git a/tracker-steamvr/dialog.ui b/tracker-steamvr/dialog.ui index 9d3b1e85..12a01816 100644 --- a/tracker-steamvr/dialog.ui +++ b/tracker-steamvr/dialog.ui @@ -9,15 +9,15 @@ <rect> <x>0</x> <y>0</y> - <width>281</width> - <height>66</height> + <width>394</width> + <height>85</height> </rect> </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <property name="minimumSize"> + <size> + <width>394</width> + <height>0</height> + </size> </property> <property name="windowTitle"> <string>Valve SteamVR</string> @@ -32,8 +32,40 @@ <property name="autoFillBackground"> <bool>false</bool> </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="1" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>3</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Device</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="device"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>8</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -46,13 +78,6 @@ </property> </widget> </item> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>No settings necessary</string> - </property> - </widget> - </item> </layout> </widget> <resources/> diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp index 2d92c5a4..b60d4896 100644 --- a/tracker-steamvr/steamvr.cpp +++ b/tracker-steamvr/steamvr.cpp @@ -16,45 +16,161 @@ */ #include "steamvr.hpp" + #include "api/plugin-api.hpp" #include "compat/util.hpp" #include <cstdlib> #include <cmath> +#include <type_traits> +#include <algorithm> #include <QMessageBox> #include <QDebug> -void steamvr::vr_deleter() +QMutex device_list::mtx(QMutex::Recursive); + +template<typename F> +static auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), error_t())) { - static std::atomic_flag atexit_done = ATOMIC_FLAG_INIT; + QMutexLocker l(&device_list::mtx); + error_t e; vr_t v; + std::tie(v, e) = device_list::vr_init(); + return fun(v, e); +} - if (atexit_done.test_and_set(std::memory_order_seq_cst)) +void device_list::fill_device_specs(QList<device_spec>& list) +{ + with_vr_lock([&](vr_t v, error_t) { - std::atexit(vr::VR_Shutdown); - } + list.clear(); + list.reserve(max_devices); + + pose_t device_states[max_devices]; + + if (v) + { + v->GetDeviceToAbsoluteTrackingPose(origin::TrackingUniverseSeated, 0, + device_states, vr::k_unMaxTrackedDeviceCount); + + static constexpr unsigned bufsiz = vr::k_unTrackingStringSize; + char str[bufsiz] {}; + + for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; 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; + } + + dev.model = str; + dev.pose = device_states[k]; + dev.k = k; + + list.push_back(dev); + } + } + }); +} + +device_list::device_list() +{ + refresh_device_list(); +} + +void device_list::refresh_device_list() +{ + fill_device_specs(device_specs); +} + +device_list::maybe_pose device_list::get_pose(int k) +{ + if (k < 0 || !(k < max_devices)) + return maybe_pose(false, pose_t{}); + + return with_vr_lock([k](vr_t v, error_t) + { + static pose_t poses[max_devices] {}; // vr_lock removes reentrancy + + v->GetDeviceToAbsoluteTrackingPose(origin::TrackingUniverseSeated, 0, + poses, max_devices); + + const pose_t& pose = poses[k]; + + if (pose.bPoseIsValid && pose.bDeviceIsConnected) + return maybe_pose(true, poses[k]); + else + once_only(qDebug() << "steamvr:" + << "no valid pose from device" << k + << "valid" << pose.bPoseIsValid + << "connected" << pose.bDeviceIsConnected); + + return maybe_pose(false, pose_t{}); + }); +} + +bool device_list::get_all_poses(pose_t* poses) +{ + with_vr_lock([poses](vr_t v, error_t) + { + if (v) + { + v->GetDeviceToAbsoluteTrackingPose(origin::TrackingUniverseSeated, 0, + poses, max_devices); + } + return v != nullptr; + }); + } -steamvr::vr_t steamvr::vr_init(error_t& error) +tt device_list::vr_init() { - error = error_t::VRInitError_Unknown; + static tt t = vr_init_(); + return t; +} - // the background type would surely confuse users - vr_t vr = vr::VR_Init(&error, vr::EVRApplicationType::VRApplication_Other); +tt device_list::vr_init_() +{ + error_t error = error_t::VRInitError_Unknown; + vr_t v = vr::VR_Init(&error, vr::EVRApplicationType::VRApplication_Other); - if (vr) - vr_deleter(); + if (v) + std::atexit(vr::VR_Shutdown); + else + once_only(qDebug() << "steamvr: init failure" << error << strerror(error)); - return vr; + return tt(v, error); } -QString steamvr::strerror(error_t error) +QString device_list::strerror(error_t err) { - const char* str(vr::VR_GetVRInitErrorAsSymbol(error)); + const char* str(vr::VR_GetVRInitErrorAsSymbol(err)); return QString(str ? str : "No description"); } -steamvr::steamvr() : vr(nullptr) +steamvr::steamvr() : device_index(-1) { } @@ -64,97 +180,106 @@ steamvr::~steamvr() void steamvr::start_tracker(QFrame*) { - error_t e(error_t::VRInitError_Unknown); - vr = vr_init(e); - - if (!vr) + with_vr_lock([this](vr_t v, error_t e) { - QMessageBox::warning(nullptr, - tr("Valve SteamVR init error"), strerror(e), - QMessageBox::Close, QMessageBox::NoButton); - return; - } + if (!v) + { + QMessageBox::warning(nullptr, + tr("Valve SteamVR init error"), strerror(e), + QMessageBox::Close, QMessageBox::NoButton); + } - bool ok = false; + const QString serial = s.device_serial; + device_list d; + const QList<device_spec>& specs = d.devices(); + const int sz = specs.count(); - for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; k++) - if (vr->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_HMD) + if (sz == 0) { - ok = true; - break; + QMessageBox::warning(nullptr, + tr("Valve SteamVR init error"), + tr("No HMD connected"), + QMessageBox::Close, QMessageBox::NoButton); } - if (!ok) - { - QMessageBox::warning(nullptr, - tr("Valve SteamVR init warning"), - tr("No HMD connected"), - QMessageBox::Close, QMessageBox::NoButton); - return; - } + for (const device_spec& spec : specs) + { + if (serial == "" || serial == spec.serial) + { + device_index = int(spec.k); + break; + } + } + }); } void steamvr::data(double* data) { - if (vr) + if (device_index != -1) { - vr::TrackedDevicePose_t devices[vr::k_unMaxTrackedDeviceCount] = {}; - - vr->GetDeviceToAbsoluteTrackingPose(vr::ETrackingUniverseOrigin::TrackingUniverseSeated, 0, - devices, vr::k_unMaxTrackedDeviceCount); - - for (unsigned k = 0; k < vr::k_unMaxTrackedDeviceCount; k++) + pose_t pose; bool ok; + std::tie(ok, pose) = device_list::get_pose(device_index); + if (ok) { - if (!devices[k].bPoseIsValid) - continue; - - if (vr->GetTrackedDeviceClass(k) != vr::ETrackedDeviceClass::TrackedDeviceClass_HMD) - continue; - - const vr::HmdMatrix34_t& result = devices[k].mDeviceToAbsoluteTracking; + static constexpr int c = 10; - 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; - using std::atan2; - using std::asin; - - // transformation matrix to euler angles - if (result.m[0][0] == 1.0f || result.m[0][0] == -1.0f) - { - data[Yaw] = -atan2(result.m[0][2], result.m[2][3]); - data[Pitch] = 0; - data[Roll] = 0; - } - else - { - data[Yaw] = -atan2(-result.m[2][0], result.m[0][0]); - data[Pitch] = atan2(-result.m[1][2], result.m[1][1]); - data[Roll] = atan2(result.m[1][1], result.m[0][1]); - } + matrix_to_euler(data[Yaw], data[Pitch], data[Roll], result); static constexpr double r2d = 180 / M_PI; - - data[Yaw] *= r2d; - data[Pitch] *= r2d; - data[Roll] *= r2d; - - break; + data[Yaw] *= r2d; data[Pitch] *= r2d; data[Roll] *= r2d; } } } bool steamvr::center() { - if (vr) - vr->ResetSeatedZeroPose(); + with_vr_lock([&](vr_t v, error_t) + { + if (v) + v->ResetSeatedZeroPose(); + }); return false; } -void steamvr_dialog::register_tracker(ITracker*) {} -void steamvr_dialog::unregister_tracker() {} +void steamvr::matrix_to_euler(double& yaw, double& pitch, double& roll, const vr::HmdMatrix34_t& result) +{ + yaw = atan2(result.m[2][0], result.m[0][0]); + pitch = atan2(result.m[1][1], result.m[1][2]); + roll = atan2(result.m[1][1], result.m[0][1]); +} + +steamvr_dialog::steamvr_dialog() +{ + ui.setupUi(this); + + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); + + ui.device->clear(); + ui.device->addItem(""); + + device_list list; + for (const device_spec& spec : list.devices()) + ui.device->addItem(spec.serial); + + tie_setting(s.device_serial, ui.device); +} + +void steamvr_dialog::doOK() +{ + s.b->save(); + close(); +} + +void steamvr_dialog::doCancel() +{ + close(); +} OPENTRACK_DECLARE_TRACKER(steamvr, steamvr_dialog, steamvr_metadata) diff --git a/tracker-steamvr/steamvr.hpp b/tracker-steamvr/steamvr.hpp index 2842fda3..0c6af65a 100644 --- a/tracker-steamvr/steamvr.hpp +++ b/tracker-steamvr/steamvr.hpp @@ -8,47 +8,86 @@ #include <openvr.h> -#include <atomic> #include <cmath> #include <memory> +#include <tuple> #include <QString> #include <QMutex> #include <QMutexLocker> +#include <QList> using namespace options; +using error_t = vr::EVRInitError; +using vr_t = vr::IVRSystem*; + +using tt = std::tuple<vr_t, error_t>; +using pose_t = vr::TrackedDevicePose_t; +using origin = vr::ETrackingUniverseOrigin; struct settings : opts { + value<QString> device_serial; settings() : - opts("valve-steamvr") + opts("valve-steamvr"), + device_serial(b, "serial", "") {} }; +struct device_spec +{ + vr::TrackedDevicePose_t pose; + QString model, serial; + unsigned k; +}; + +struct device_list final +{ + using maybe_pose = std::tuple<bool, pose_t>; + + device_list(); + void refresh_device_list(); + const QList<device_spec>& devices() const & { return device_specs; } + + static OTR_NEVER_INLINE maybe_pose get_pose(int k); + static bool get_all_poses(pose_t*poses); + static QString strerror(error_t error); + static constexpr int max_devices = int(vr::k_unMaxTrackedDeviceCount); + + template<typename F> + friend static auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), error_t())); + +private: + QList<device_spec> device_specs; + static QMutex mtx; + static tt vr_init_(); + static void vr_deleter(); + static void fill_device_specs(QList<device_spec>& list); + static tt vr_init(); +}; + class steamvr : public QObject, public ITracker { Q_OBJECT + + using error_t = vr::EVRInitError; + using vr_t = vr::IVRSystem*; + public: steamvr(); ~steamvr() override; void start_tracker(QFrame *) override; void data(double *data) override; bool center() override; -private: - using error_t = vr::EVRInitError; - using vr_t = vr::IVRSystem*; - - vr_t vr; +private: + void matrix_to_euler(double &yaw, double &pitch, double &roll, const vr::HmdMatrix34_t& result); settings s; + int device_index; using rmat = euler::rmat; using euler_t = euler::euler_t; - - static void vr_deleter(); - static vr_t vr_init(error_t& error); - static QString strerror(error_t error); }; class steamvr_dialog : public ITrackerDialog @@ -57,12 +96,11 @@ class steamvr_dialog : public ITrackerDialog public: steamvr_dialog(); - void register_tracker(ITracker *) override; - void unregister_tracker() override; private: Ui::dialog ui; settings s; - vr::IVRSystem* vr; + device_list devices; + private slots: void doOK(); void doCancel(); |