Add support for motion controllers
authorMikko Rasa <tdb@tdb.fi>
Wed, 5 Oct 2016 23:45:08 +0000 (02:45 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 5 Oct 2016 23:45:08 +0000 (02:45 +0300)
source/motioncontroller.cpp [new file with mode: 0644]
source/motioncontroller.h [new file with mode: 0644]
source/openvr/openvrcontroller.cpp [new file with mode: 0644]
source/openvr/openvrcontroller.h [new file with mode: 0644]
source/openvr/openvrcontroller_private.h [new file with mode: 0644]
source/openvr/openvrsystem.cpp
source/openvr/openvrsystem.h
source/stereoview.cpp
source/stereoview.h
source/system.cpp
source/system.h

diff --git a/source/motioncontroller.cpp b/source/motioncontroller.cpp
new file mode 100644 (file)
index 0000000..0f82911
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdexcept>
+#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 (file)
index 0000000..9cdd622
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MSP_VR_MOTIONCONTROLLER_H_
+#define MSP_VR_MOTIONCONTROLLER_H_
+
+#include <msp/gl/matrix.h>
+#include <msp/input/device.h>
+
+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 (file)
index 0000000..9ab957d
--- /dev/null
@@ -0,0 +1,102 @@
+#include <openvr.h>
+#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<vr::EVRButtonId>(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 (file)
index 0000000..18c5774
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MSP_VR_OPENVRCONTROLLER_H_
+#define MSP_VR_OPENVRCONTROLLER_H_
+
+#include <msp/vr/motioncontroller.h>
+
+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 (file)
index 0000000..ca18a55
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_VR_OPENVRCONTROLLER_PRIVATE_H_
+#define MSP_VR_OPENVRCONTROLLER_PRIVATE_H_
+
+#include <openvr.h>
+#include "openvrcontroller.h"
+
+namespace Msp {
+namespace VR {
+
+struct OpenVRController::Event: vr::VREvent_t
+{
+};
+
+} // namespace VR
+} // namespace Msp
+
+#endif
index d631f48f8dece03c7a27c074924eafc0ded64b86..e847b201385f4a8c685367dcee8f3799c337ed11 100644 (file)
@@ -1,5 +1,6 @@
 #include <openvr.h>
 #include <msp/vr/stereoview.h>
+#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; i<vr::k_unMaxTrackedDeviceCount; ++i)
                if(vr_sys->IsTrackedDeviceConnected(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<OpenVRController *>::iterator i=controllers.begin(); i!=controllers.end(); ++i)
+               {
+                       int cindex = (*i)->get_index();
+                       if(cindex>=0 && event.trackedDeviceIndex==static_cast<unsigned>(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<OpenVRController *>::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<OpenVRController *>::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<OpenVRController *>::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<OpenVRController *>::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
index 119eb16e7e7948d211cea8b7804360c7d40b4a89..7799ad298405914f198d41c7ddd676eae902b59b 100644 (file)
@@ -5,6 +5,7 @@
 #include <msp/vr/system.h>
 #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<GL::Matrix> tracking_matrices;
+       std::vector<OpenVRController *> controllers;
+       std::vector<unsigned> 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
index 73be19c4527a420aacad992148a56ac25573c2cf..f08cd79ea1ef7166506265ff46dbee457d4df616 100644 (file)
@@ -1,5 +1,6 @@
 #include <msp/gl/renderer.h>
 #include "headtrackingcamera.h"
+#include "motioncontroller.h"
 #include "stereocombiner.h"
 #include "stereoview.h"
 
@@ -59,11 +60,27 @@ void StereoView::set_strabismus(const Geometry::Angle<float> &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<MotionController *>::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<MotionController *>::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<float>::zero())
index ec1b246530b1e41090d677c1da7126fe18cb338e..e31fa3d561b9adaa51f81a916b30d5c66f3483e5 100644 (file)
@@ -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<float> strabismus;
+       std::vector<MotionController *> 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<float> &);
 
+       void add_controller(MotionController &);
+       void remove_controller(MotionController &);
+
 private:
        void setup_frame() const;
 public:
index b4105b056a969553f4df57261e0b293b9e305219..500e672f3365518276c51145b5eb9150aa129143 100644 (file)
@@ -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
index b7187b9e00a95670ddfe974690afc0fc70b090cb..e52c05f6da4c7a7855a1c637f7750ec553fa6f71 100644 (file)
@@ -4,6 +4,7 @@
 #include <string>
 #include <msp/gl/camera.h>
 #include <msp/gl/view.h>
+#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() { }
 };