From 774cc129d93a0001c36434f47ec0614c653824be Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 6 Oct 2016 02:45:08 +0300 Subject: [PATCH] Add support for motion controllers --- source/motioncontroller.cpp | 43 ++++++++++ source/motioncontroller.h | 33 ++++++++ source/openvr/openvrcontroller.cpp | 102 +++++++++++++++++++++++ source/openvr/openvrcontroller.h | 38 +++++++++ source/openvr/openvrcontroller_private.h | 17 ++++ source/openvr/openvrsystem.cpp | 81 +++++++++++++++++- source/openvr/openvrsystem.h | 6 ++ source/stereoview.cpp | 17 ++++ source/stereoview.h | 5 ++ source/system.cpp | 5 ++ source/system.h | 2 + 11 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 source/motioncontroller.cpp create mode 100644 source/motioncontroller.h create mode 100644 source/openvr/openvrcontroller.cpp create mode 100644 source/openvr/openvrcontroller.h create mode 100644 source/openvr/openvrcontroller_private.h diff --git a/source/motioncontroller.cpp b/source/motioncontroller.cpp new file mode 100644 index 0000000..0f82911 --- /dev/null +++ b/source/motioncontroller.cpp @@ -0,0 +1,43 @@ +#include +#include "headtrackingcamera.h" +#include "motioncontroller.h" +#include "stereoview.h" + +using namespace std; + +namespace Msp { +namespace VR { + +MotionController::MotionController(): + view(0) +{ } + +MotionController::~MotionController() +{ + detach_from_view(); +} + +void MotionController::attach_to_view(StereoView &v) +{ + detach_from_view(); + view = &v; + view->add_controller(*this); +} + +void MotionController::detach_from_view() +{ + if(view) + view->remove_controller(*this); + view = 0; +} + +void MotionController::update_from_matrix(const GL::Matrix &m) +{ + matrix = m; + if(view) + if(const HeadTrackingCamera *head_camera = view->get_head_camera()) + matrix = head_camera->get_base_matrix()*matrix; +} + +} // namespace VR +} // namespace Msp diff --git a/source/motioncontroller.h b/source/motioncontroller.h new file mode 100644 index 0000000..9cdd622 --- /dev/null +++ b/source/motioncontroller.h @@ -0,0 +1,33 @@ +#ifndef MSP_VR_MOTIONCONTROLLER_H_ +#define MSP_VR_MOTIONCONTROLLER_H_ + +#include +#include + +namespace Msp { +namespace VR { + +class StereoView; + +class MotionController: public Input::Device +{ +protected: + StereoView *view; + Msp::GL::Matrix matrix; + + MotionController(); +public: + virtual ~MotionController(); + + void attach_to_view(StereoView &); + void detach_from_view(); + const GL::Matrix &get_matrix() const { return matrix; } + virtual void update() = 0; +protected: + void update_from_matrix(const Msp::GL::Matrix &); +}; + +} // namespace VR +} // namespace Msp + +#endif diff --git a/source/openvr/openvrcontroller.cpp b/source/openvr/openvrcontroller.cpp new file mode 100644 index 0000000..9ab957d --- /dev/null +++ b/source/openvr/openvrcontroller.cpp @@ -0,0 +1,102 @@ +#include +#include "openvrcontroller.h" +#include "openvrcontroller_private.h" +#include "openvrsystem.h" + +using namespace std; + +namespace Msp { +namespace VR { + +OpenVRController::OpenVRController(OpenVRSystem &s): + system(s), + index(-1) +{ + name = "OpenVR Controller"; + + system.add_controller(*this); +} + +OpenVRController::~OpenVRController() +{ + system.remove_controller(*this); +} + +string OpenVRController::get_button_name(unsigned btn) const +{ + switch(btn) + { + case vr::k_EButton_System: return "System"; + case vr::k_EButton_ApplicationMenu: return "Menu"; + case vr::k_EButton_Grip: return "Grip"; + case vr::k_EButton_Axis0: return "Trackpad"; + case vr::k_EButton_Axis1: return "Trigger"; + } + + const char *n = vr::VRSystem()->GetButtonIdNameFromEnum(static_cast(btn)); + if(n) + return n; + + return MotionController::get_button_name(btn); +} + +string OpenVRController::get_axis_name(unsigned axis) const +{ + switch(axis) + { + case 0: return "Trackpad X"; + case 1: return "Trackpad Y"; + case 2: return "Trigger"; + } + + return MotionController::get_axis_name(axis); +} + +void OpenVRController::event(const Event &ev) +{ + switch(ev.eventType) + { + case vr::VREvent_TrackedDeviceActivated: + index = ev.trackedDeviceIndex; + break; + case vr::VREvent_TrackedDeviceDeactivated: + index = -1; + break; + case vr::VREvent_ButtonPress: + set_button_state(ev.data.controller.button, true, true); + break; + case vr::VREvent_ButtonUnpress: + set_button_state(ev.data.controller.button, false, true); + break; + } +} + +void OpenVRController::update() +{ + if(index<0) + return; + + update_from_matrix(system.get_tracking_matrix(index)); +} + +void OpenVRController::update_input_state() +{ + if(index<0) + return; + + vr::VRControllerState_t state; + vr::VRSystem()->GetControllerState(index, &state); + if(state.unPacketNum!=last_packet_number) + { + for(unsigned i=0; i<5; ++i) + { + set_axis_value(i*2, state.rAxis[i].x, true); + set_axis_value(i*2+1, state.rAxis[i].y, true); + } + + last_packet_number = state.unPacketNum; + } +} + +} // namespace VR +} // namespace Msp diff --git a/source/openvr/openvrcontroller.h b/source/openvr/openvrcontroller.h new file mode 100644 index 0000000..18c5774 --- /dev/null +++ b/source/openvr/openvrcontroller.h @@ -0,0 +1,38 @@ +#ifndef MSP_VR_OPENVRCONTROLLER_H_ +#define MSP_VR_OPENVRCONTROLLER_H_ + +#include + +namespace Msp { +namespace VR { + +class OpenVRSystem; + +class OpenVRController: public MotionController +{ +public: + struct Event; + +private: + OpenVRSystem &system; + int index; + unsigned last_packet_number; + +public: + OpenVRController(OpenVRSystem &); + virtual ~OpenVRController(); + + virtual std::string get_button_name(unsigned) const; + virtual std::string get_axis_name(unsigned) const; + + int get_index() const { return index; } + + void event(const Event &); + virtual void update(); + void update_input_state(); +}; + +} // namespace VR +} // namespace Msp + +#endif diff --git a/source/openvr/openvrcontroller_private.h b/source/openvr/openvrcontroller_private.h new file mode 100644 index 0000000..ca18a55 --- /dev/null +++ b/source/openvr/openvrcontroller_private.h @@ -0,0 +1,17 @@ +#ifndef MSP_VR_OPENVRCONTROLLER_PRIVATE_H_ +#define MSP_VR_OPENVRCONTROLLER_PRIVATE_H_ + +#include +#include "openvrcontroller.h" + +namespace Msp { +namespace VR { + +struct OpenVRController::Event: vr::VREvent_t +{ +}; + +} // namespace VR +} // namespace Msp + +#endif diff --git a/source/openvr/openvrsystem.cpp b/source/openvr/openvrsystem.cpp index d631f48..e847b20 100644 --- a/source/openvr/openvrsystem.cpp +++ b/source/openvr/openvrsystem.cpp @@ -1,5 +1,6 @@ #include #include +#include "openvrcontroller_private.h" #include "openvrsystem.h" using namespace std; @@ -26,10 +27,11 @@ unsigned OpenVRSystem::n_instances = 0; OpenVRSystem::OpenVRSystem(): n_tracked_devices(0) { + vr::IVRSystem *vr_sys = 0; if(!n_instances) { vr::EVRInitError init_err; - vr::VR_Init(&init_err, vr::VRApplication_Scene); + vr_sys = vr::VR_Init(&init_err, vr::VRApplication_Scene); if(init_err!=vr::VRInitError_None) throw runtime_error("OpenVR initialization failed"); } @@ -43,7 +45,11 @@ OpenVRSystem::OpenVRSystem(): for(unsigned i=0; iIsTrackedDeviceConnected(i)) + { n_tracked_devices = i+1; + if(vr_sys->GetTrackedDeviceClass(i)==vr::TrackedDeviceClass_Controller) + unclaimed_controllers.push_back(i); + } tracking_matrices.resize(n_tracked_devices); } @@ -101,20 +107,58 @@ OpenVRCombiner *OpenVRSystem::create_combiner(GL::View &v) return new OpenVRCombiner(*this, v); } +OpenVRController *OpenVRSystem::create_controller() +{ + return new OpenVRController(*this); +} + void OpenVRSystem::tick() { vr::IVRSystem *vr_sys = vr::VRSystem(); - vr::VREvent_t event; + OpenVRController::Event event; while(vr_sys->PollNextEvent(&event, sizeof(event))) { - if(event.eventType==vr::VREvent_TrackedDeviceActivated) + bool controller_matched = false; + for(vector::iterator i=controllers.begin(); i!=controllers.end(); ++i) + { + int cindex = (*i)->get_index(); + if(cindex>=0 && event.trackedDeviceIndex==static_cast(cindex)) + { + (*i)->event(event); + controller_matched = true; + } + } + + if(!controller_matched && event.eventType==vr::VREvent_TrackedDeviceActivated) + { if(event.trackedDeviceIndex>=n_tracked_devices) { n_tracked_devices = event.trackedDeviceIndex+1; tracking_matrices.resize(n_tracked_devices); } + + vr::ETrackedDeviceClass dev_class = vr_sys->GetTrackedDeviceClass(event.trackedDeviceIndex); + + if(dev_class==vr::TrackedDeviceClass_Controller) + { + bool assigned_to_controller = false; + for(vector::iterator i=controllers.begin(); i!=controllers.end(); ++i) + if((*i)->get_index()<0) + { + (*i)->event(event); + assigned_to_controller = true; + break; + } + + if(!assigned_to_controller) + unclaimed_controllers.push_back(event.trackedDeviceIndex); + } + } } + + for(vector::iterator i=controllers.begin(); i!=controllers.end(); ++i) + (*i)->update_input_state(); } void OpenVRSystem::update_pose_matrices() @@ -141,5 +185,36 @@ const GL::Matrix &OpenVRSystem::get_hmd_matrix() const return get_tracking_matrix(vr::k_unTrackedDeviceIndex_Hmd); } +void OpenVRSystem::add_controller(OpenVRController &controller) +{ + vector::iterator i = find(controllers.begin(), controllers.end(), &controller); + if(i!=controllers.end()) + throw invalid_argument("already added"); + + controllers.push_back(&controller); + + if(!unclaimed_controllers.empty()) + { + OpenVRController::Event event; + event.eventType = vr::VREvent_TrackedDeviceActivated; + event.trackedDeviceIndex = unclaimed_controllers.back(); + event.eventAgeSeconds = 0; + controller.event(event); + unclaimed_controllers.pop_back(); + } +} + +void OpenVRSystem::remove_controller(OpenVRController &controller) +{ + vector::iterator i = find(controllers.begin(), controllers.end(), &controller); + if(i==controllers.end()) + throw invalid_argument("not added"); + + int index = controller.get_index(); + if(index>=0) + unclaimed_controllers.push_back(index); + controllers.erase(i); +} + } // namespace VR } // namespace Msp diff --git a/source/openvr/openvrsystem.h b/source/openvr/openvrsystem.h index 119eb16..7799ad2 100644 --- a/source/openvr/openvrsystem.h +++ b/source/openvr/openvrsystem.h @@ -5,6 +5,7 @@ #include #include "openvrcamera.h" #include "openvrcombiner.h" +#include "openvrcontroller.h" namespace Msp { namespace VR { @@ -14,6 +15,8 @@ class OpenVRSystem: public System private: unsigned n_tracked_devices; std::vector tracking_matrices; + std::vector controllers; + std::vector unclaimed_controllers; static unsigned n_instances; @@ -30,12 +33,15 @@ public: virtual bool get_absolute_tracking() const; virtual OpenVRCamera *create_camera(const GL::Camera &); virtual OpenVRCombiner *create_combiner(GL::View &); + virtual OpenVRController *create_controller(); virtual void tick(); void update_pose_matrices(); const GL::Matrix &get_tracking_matrix(unsigned) const; const GL::Matrix &get_hmd_matrix() const; + void add_controller(OpenVRController &); + void remove_controller(OpenVRController &); }; } // namespace VR diff --git a/source/stereoview.cpp b/source/stereoview.cpp index 73be19c..f08cd79 100644 --- a/source/stereoview.cpp +++ b/source/stereoview.cpp @@ -1,5 +1,6 @@ #include #include "headtrackingcamera.h" +#include "motioncontroller.h" #include "stereocombiner.h" #include "stereoview.h" @@ -59,11 +60,27 @@ void StereoView::set_strabismus(const Geometry::Angle &s) strabismus = s; } +void StereoView::add_controller(MotionController &controller) +{ + if(find(controllers.begin(), controllers.end(), &controller)==controllers.end()) + controllers.push_back(&controller); +} + +void StereoView::remove_controller(MotionController &controller) +{ + vector::iterator i = find(controllers.begin(), controllers.end(), &controller); + if(i!=controllers.end()) + controllers.erase(i); +} + void StereoView::setup_frame() const { if(head_camera) head_camera->update(); + for(vector::const_iterator i=controllers.begin(); i!=controllers.end(); ++i) + (*i)->update(); + EyeParams params; params.fov = combiner.get_field_of_view(); if(params.fov==Geometry::Angle::zero()) diff --git a/source/stereoview.h b/source/stereoview.h index ec1b246..e31fa3d 100644 --- a/source/stereoview.h +++ b/source/stereoview.h @@ -12,6 +12,7 @@ namespace Msp { namespace VR { class HeadTrackingCamera; +class MotionController; class StereoCombiner; class StereoView @@ -54,6 +55,7 @@ private: Eye left; Eye right; Geometry::Angle strabismus; + std::vector controllers; public: StereoView(const StereoCombiner &, const GL::Camera &); @@ -69,6 +71,9 @@ public: void set_eye_matrices(const GL::Matrix &, const GL::Matrix &); void set_strabismus(const Geometry::Angle &); + void add_controller(MotionController &); + void remove_controller(MotionController &); + private: void setup_frame() const; public: diff --git a/source/system.cpp b/source/system.cpp index b4105b0..500e672 100644 --- a/source/system.cpp +++ b/source/system.cpp @@ -57,5 +57,10 @@ void System::set_absolute_tracking(bool a) throw invalid_argument("absolute tracking not supported"); } +MotionController *System::create_controller() +{ + throw runtime_error("controller not supported"); +} + } // namespace VR } // namespace Msp diff --git a/source/system.h b/source/system.h index b7187b9..e52c05f 100644 --- a/source/system.h +++ b/source/system.h @@ -4,6 +4,7 @@ #include #include #include +#include "motioncontroller.h" namespace Msp { namespace VR { @@ -29,6 +30,7 @@ public: virtual bool get_absolute_tracking() const { return false; } virtual HeadTrackingCamera *create_camera(const GL::Camera &) = 0; virtual StereoCombiner *create_combiner(GL::View &) = 0; + virtual MotionController *create_controller(); virtual void tick() { } }; -- 2.43.0