OpenFrames
OpenVRDevice.hpp
Go to the documentation of this file.
1 /***********************************
2  Copyright 2018 Ravishankar Mathur
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  ***********************************/
16 
21 #ifndef _OF_OPENVRDEVICE_
22 #define _OF_OPENVRDEVICE_
23 
24 #include <OpenFrames/Export.h>
25 #include <OpenFrames/View.hpp>
26 #include <osg/ref_ptr>
27 #include <osg/Referenced>
28 #include <osg/Quat>
29 #include <osg/LineWidth>
30 #include <osg/MatrixTransform>
31 #include <osg/Texture>
32 #include <osg/Vec3d>
33 #include <osgGA/Device>
34 #include <osgViewer/ViewerEventHandlers>
35 #include <algorithm>
36 
47 namespace vr
48 {
49  class IVRSystem;
50  class IVRRenderModels;
51  struct VREvent_t;
52  struct VRControllerState001_t;
53  typedef VRControllerState001_t VRControllerState_t;
54 }
59 namespace OpenFrames
60 {
61  struct VRTextureBuffer; // Used by OpenVRSwapBuffers below
62 
70  class OpenVREvent : public osgGA::GUIEventAdapter
71  {
72  public:
73  OpenVREvent();
74  ~OpenVREvent();
75 
76  void operator=(const OpenVREvent &other);
77 
78  vr::VREvent_t *_ovrEvent; // The actual OpenVR event
79  };
80 
87  class OF_EXPORT OpenVRDevice : public osg::Referenced
88  {
89  public:
92  OpenVRDevice(double worldUnitsPerMeter, double userHeight);
93 
98  bool initVR();
99  void shutdownVR();
100 
102  void getRecommendedTextureSize(int& w, int& h) const
103  {
104  w = _width; h = _height;
105  }
106 
108  inline bool isInitialized() const { return _isInitialized; }
109 
111  osg::MatrixTransform* getDeviceRenderModels() const { return _deviceModels; }
112 
119  class OF_EXPORT LaserModel : public osg::Referenced
120  {
121  public:
122  LaserModel();
123 
125  osg::MatrixTransform* getTransform() const { return _laserTransform; }
126 
129  void showLaser(bool show);
130  bool isLaserShown() const { return (_laserTransform->getNodeMask() == ~0x0); }
131 
137  void setLaserHideDelay(const double& delay) { _hideDelay = delay; }
138  double getLaserHideDelay() const { return _hideDelay; }
139 
141  void setColor(const osg::Vec4& color);
142  const osg::Vec4& getColor() const { return _colors->back(); }
143  void setDefaultColor(const osg::Vec4& color) { _defaultColor = color; }
144  const osg::Vec4& getDefaultColor() const { return _defaultColor; }
145 
147  void setLength(const double& length);
148  double getLength() const { return -(*_vertices)[1].z(); }
149  void setDefaultLength(const double& length) { _defaultLength = (length >= 0.0) ? length : _defaultLength; }
150  double getDefaultLength() const { return _defaultLength; }
151 
153  void setWidth(const float& width);
154  float getWidth() const { return _lineWidth->getWidth(); }
155  void setDefaultWidth(const float& width) { _defaultWidth = (width > 0.0) ? width : _defaultWidth; }
156  float getDefaultWidth() const { return _defaultWidth; }
157 
159  const osg::Timer_t& getUpdateTime() const { return _lastUpdateTime; }
160 
161  // Set all laser parameters to their defaults
162  void restoreDefaults();
163 
164  protected:
165  virtual ~LaserModel();
166 
167  osg::ref_ptr<osg::MatrixTransform> _laserTransform;
168  osg::Timer_t _lastUpdateTime; // Time (in seconds since osg::Timer epoch) of last laser update
169  osg::ref_ptr<osg::Geometry> _geom;
170  osg::ref_ptr<osg::Vec3Array> _vertices;
171  osg::ref_ptr<osg::Vec4Array> _colors;
172  osg::ref_ptr<osg::LineWidth> _lineWidth;
173  double _hideDelay;
174  double _defaultLength;
175  osg::Vec4 _defaultColor;
176  float _defaultWidth;
177  };
178 
181  {
182  NONE = 0,
183  HMD = 1,
184  BASESTATION = 2,
185  CONTROLLER = 3
186  };
187 
194  struct OF_EXPORT DeviceModel
195  {
196  DeviceModel() : _valid(false), _class(NONE), _controllerState(nullptr)
197  {
198  _modelTransform = new osg::MatrixTransform;
199  _modelTransform->setNodeMask(0x0);
200  }
201  ~DeviceModel();
202 
203  osg::ref_ptr<osg::MatrixTransform> _modelTransform; // In world units
204  osg::Matrixd _rawDeviceToWorld; // In OpenVR units [meters]
205  bool _valid; // Whether device is actively being tracked by OpenVR
206  DeviceClass _class;
207 
208  // These variables only apply if the device class is CONTROLLER, otherwise they are NULL
209  // TODO: subclass DeviceModel with appropriate device classes
210  osg::ref_ptr<LaserModel> _laser; // The controller laser
211  vr::VRControllerState_t *_controllerState; // Most recent controller state (buttons, axes, etc.)
212  };
213 
215  unsigned int getNumDeviceModels() const { return _deviceIDToModel.size(); }
216  const DeviceModel* getDeviceModel(unsigned int id) const
217  {
218  if(id < _deviceIDToModel.size()) return &(_deviceIDToModel[id]);
219  else return NULL;
220  }
221 
223  void updateDeviceModels();
224 
226  const osg::Matrixd& getHMDPoseMatrix() const
227  { return _deviceIDToModel[0]._modelTransform->getMatrix(); }
228 
230  void updateProjectionMatrices();
231  osg::Matrixd& getRightEyeProjectionMatrix() { return _rightEyeProj; }
232  osg::Matrixd& getLeftEyeProjectionMatrix() { return _leftEyeProj; }
233  osg::Matrixd& getCenterProjectionMatrix() { return _centerProj; }
234 
236  osg::Vec3d& getRightEyeViewOffset() { return _rightEyeViewOffset; }
237  osg::Vec3d& getLeftEyeViewOffset() { return _leftEyeViewOffset; }
238  osg::Vec3d& getCenterViewOffset() { return _centerViewOffset; }
239 
242  void waitGetPoses();
243  void computeDeviceTransforms(); // Compute Local<->World device transforms
244 
246  void setWorldUnitsPerMeter(const double& worldUnitsPerMeter);
247  double getWorldUnitsPerMeter() const { return _worldUnitsPerMeter; }
248  void setWorldUnitsPerMeterLimits(const double& minWorldUnitsPerMeter,
249  const double& maxWorldUnitsPerMeter)
250  {
251  _minWorldUnitsPerMeter = std::max(0.0, minWorldUnitsPerMeter);
252  _maxWorldUnitsPerMeter = std::max(_minWorldUnitsPerMeter, maxWorldUnitsPerMeter);
253 
254  // Ensure current WorldUnits/Meter is within bounds
255  double newWUM = std::max(_minWorldUnitsPerMeter, std::min(_worldUnitsPerMeter, _maxWorldUnitsPerMeter));
256  setWorldUnitsPerMeter(newWUM);
257  }
258  void getWorldUnitsPerMeterLimits(double &minWorldUnitsPerMeter, double &maxWorldUnitsPerMeter) const
259  {
260  minWorldUnitsPerMeter = _minWorldUnitsPerMeter;
261  maxWorldUnitsPerMeter = _maxWorldUnitsPerMeter;
262  }
263 
265  void setUserHeight(double userHeight) { _userHeight = userHeight; }
266  double getUserHeight() const { return _userHeight; }
267 
269  osg::MatrixTransform* getGroundPlane() { return _roomGround; }
270 
272  void submitFrame(GLuint rightEyeTexName, GLuint leftEyeTexName);
273 
275  bool pollNextEvent(OpenVREvent *event);
276 
278  vr::IVRSystem* getVRSystem() const { return _vrSystem; }
279 
280  protected:
281  virtual ~OpenVRDevice();
282 
284  void updateViewOffsets();
285 
287  void createDeviceRenderModels();
288 
290  void setupRenderModelForTrackedDevice(uint32_t deviceID);
291 
292  double _worldUnitsPerMeter; // Distance units per real-world meter
293  double _minWorldUnitsPerMeter, _maxWorldUnitsPerMeter;
294  double _userHeight; // Height of user's HMD origin in meters
295  int _width, _height; // Per-eye texture dimensions
296 
297  bool _isInitialized; // Whether OpenVR is initialized
298 
299  vr::IVRSystem* _vrSystem; // OpenVR interface
300  vr::IVRRenderModels* _vrRenderModels; // Controller models
301 
303  typedef std::map<std::string, osg::ref_ptr<osg::Geode> > DeviceGeodeMap;
304  DeviceGeodeMap _deviceNameToGeode;
305 
307  typedef std::vector<DeviceModel> DeviceModelVector;
309  DeviceModelVector _deviceIDToModel;
310 
317  struct DeviceModelEventCallback : public osgGA::GUIEventHandler
318  {
320  : _ovrDevice(ovrDevice)
321  {}
322 
323  // Handle the OpenVR event
324  virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor* nv);
325 
326  osg::observer_ptr<OpenVRDevice> _ovrDevice;
327  };
328 
329  // Group that contains all device models
330  osg::ref_ptr<osg::MatrixTransform> _deviceModels;
331 
332  // Transform that contains midpoint between controllers
333  osg::ref_ptr<osg::MatrixTransform> _controllerMidpoint;
334 
335  // Transform that contains local ground plane
336  osg::ref_ptr<osg::MatrixTransform> _roomGround;
337 
338  // Per-eye asymmetric projection matrices
339  osg::Matrixd _rightEyeProj, _leftEyeProj, _centerProj;
340 
341  // Per-eye view offsets, transform from HMD to Eye space
342  osg::Vec3d _rightEyeViewOffset, _leftEyeViewOffset, _centerViewOffset;
343  osg::Vec3d _rightEyeViewOffsetRaw, _leftEyeViewOffsetRaw, _centerViewOffsetRaw;
344  };
345 
352  class OpenVREventDevice : public osgGA::Device
353  {
354  public:
355  OpenVREventDevice(OpenVRDevice *ovrDevice)
356  : _ovrDevice(ovrDevice)
357  {
358  setCapabilities(osgGA::Device::RECEIVE_EVENTS);
359  }
360 
361  // Check for events and store them in event queue
362  virtual bool checkEvents();
363 
364  private:
365  osg::observer_ptr<OpenVRDevice> _ovrDevice;
366  };
367 
376  struct OpenVRSlaveCallback : public osg::View::Slave::UpdateSlaveCallback
377  {
378  enum CameraType
379  {
380  RIGHT_CAMERA,
381  LEFT_CAMERA,
382  MONO_CAMERA
383  };
384 
385  OpenVRSlaveCallback(CameraType cameraType, OpenVRDevice *ovrDevice)
386  : _cameraType(cameraType), _ovrDevice(ovrDevice)
387  { }
388 
389  virtual void updateSlave(osg::View& view, osg::View::Slave& slave);
390 
391  CameraType _cameraType;
392  osg::observer_ptr<OpenVRDevice> _ovrDevice;
393  };
394 
404  class OF_EXPORT OpenVRTrackball : public FollowingTrackball
405  {
406  public:
407  OpenVRTrackball(OpenVRDevice *ovrDevice);
408 
409  virtual const char* className() const { return "OpenVRTrackball"; }
410 
411  // Inherited from FollowingTrackball
412  virtual osg::Matrixd getMatrix() const; // HMD (Center) to World matrix
413  virtual osg::Matrixd getInverseMatrix() const; // World to HMD (Center) matrix
414 
415  // Get Room to Trackball matrix
416  // Additional transformation matrices for an OpenVRTrackball can be computed as follows:
417  // Room -> ViewFrame: tb->getRoomToTrackballMatrix() * tb->osgGA::TrackballManipulator::getMatrix()
418  // Room -> World: tb->getRoomToTrackballMatrix() * tb->FollowingTrackball::getMatrix()
419  const osg::Matrixd& getRoomToTrackballMatrix() const { return _roomPose; }
420  void setRoomToTrackballMatrix(const osg::Matrixd& roomPose) { _roomPose = roomPose; }
421 
422  // Get/set default WorldUnits/Meter ratio
423  double getDefaultWorldUnitsPerMeterRatio() const { return _defaultWorldUnitsPerMeter; }
424  void setDefaultWorldUnitsPerMeterRatio(const double& wum) { _defaultWorldUnitsPerMeter = wum; }
425 
426  // Specify action when user presses one grip button on the VR controller
427  enum OneButtonMode
428  {
429  ONEBUTTON_TRANSLATE,
430  ONEBUTTON_ROTATE,
431  ONEBUTTON_DISABLE
432  };
433  void setOneButtonMode(OneButtonMode mode) { _oneButtonMode = mode; }
434  OneButtonMode getOneButtonMode() const { return _oneButtonMode; }
435 
436  // Specify action when user presses grip buttons on both VR controllers
437  enum TwoButtonMode
438  {
439  TWOBUTTON_ROTATESCALE,
440  TWOBUTTON_DISABLE
441  };
442  void setTwoButtonMode(TwoButtonMode mode) { _twoButtonMode = mode; }
443  TwoButtonMode getTwoButtonMode() const { return _twoButtonMode; }
444 
445  // Handle event
446  virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us);
447 
448  protected:
449  osg::observer_ptr<OpenVRDevice> _ovrDevice;
450 
451  // Transformation from room space to trackball space
452  osg::Matrixd _roomPose, _savedRoomPose;
453 
454  double _savedWorldUnitsPerMeter; // Last saved WorldUnits/Meter ratio
455  double _defaultWorldUnitsPerMeter; // Default WorldUnits/Meter ratio
456 
457  // Process user's controller motion into view changes
458  void processMotion();
459  void processOneButtonMotion();
460  void processTwoButtonMotion();
461 
462  // Save, restore, and reset VR-related trackball state
463  virtual void saveState();
464  virtual void restoreState();
465  virtual void resetState();
466 
469  {
470  NONE = 0,
471  ONEBUTTON,
472  TWOBUTTON
473  };
474  MotionMode _motionMode;
475 
476  OneButtonMode _oneButtonMode;
477  TwoButtonMode _twoButtonMode;
478 
485  struct MotionData
486  {
487  MotionMode _mode;
488  uint32_t _device1ID;
489  uint32_t _device2ID;
490  osg::Matrixd _device1OrigPoseRaw;
491  osg::Matrixd _device2OrigPoseRaw;
492  double _origWorldUnitsPerMeter;
493  osg::Quat _origRotation;
494  osg::Matrixd _origTrackball;
495  osg::Matrixd _origRoomPose;
496  } _motionData;
497  void saveCurrentMotionData();
498  };
499 
508  struct OpenVRSwapBuffers : public osg::GraphicsContext::SwapCallback
509  {
510  public:
511  OpenVRSwapBuffers(OpenVRDevice *ovrDevice, VRTextureBuffer *texBuffer);
512 
513  // Submit eye textures to OpenVR and do standard swap buffers
514  virtual void swapBuffersImplementation(osg::GraphicsContext *gc);
515 
516  osg::observer_ptr<OpenVRDevice> _ovrDevice;
517  osg::observer_ptr<VRTextureBuffer> _texBuffer;
518  };
519 
526  class OF_EXPORT OpenVRImageHandler : public osgViewer::InteractiveImageHandler
527  {
528  public:
529  OpenVRImageHandler(const OpenVRDevice *ovrDevice, osg::Image* image);
530 
531  META_Object(OpenFrames, OpenVRImageHandler);
532 
533  // Inherited from InteractiveImageHandler
534  // Translate an OpenVR controller event to a Qt widget click
535  virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor* nv);
536 
537  // Set properties of laser when it is pointed at an image
538  void setSelectedWidth(const float& width) { _laserSelectedWidth = (width > 0) ? width : _laserSelectedWidth; }
539  float getSelectedWidth() { return _laserSelectedWidth; }
540 
541  void setSelectedColor(const osg::Vec4& color) { _laserSelectedColor = color; }
542  const osg::Vec4& getSelectedColor() const { return _laserSelectedColor; }
543 
544  // Set how far the trigger must be pulled to signal a mousedown event, in range [0, 1]
545  void setTriggerThreshold(const float& threshold)
546  {
547  if ((threshold >= 0.0) && (threshold <= 1.0)) _triggerThreshold = threshold;
548  }
549  float getTriggerThreshold() const { return _triggerThreshold; }
550 
551  protected:
552  virtual ~OpenVRImageHandler() {}
553 
555  osgViewer::InteractiveImageHandler()
556  {}
557 
558  OpenVRImageHandler(const OpenVRImageHandler &ovrih, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY) :
559  osgViewer::InteractiveImageHandler(ovrih, copyop)
560  {}
561 
563  void processImagePick();
564 
566  float getTriggerValue(const vr::VRControllerState_t *controllerState) const;
567 
569  enum PickMode
570  {
571  NONE = 0,
572  MOUSEACTION // Mouse move or click
573  };
574 
581  struct PickData
582  {
583  PickMode mode;
584  uint32_t deviceID;
585  OpenVRTrackball* trackball;
586  osg::NodePath nodePath;
587  } _pickData;
588  void saveCurrentPickData(PickMode mode, osgViewer::View* view, osg::NodeVisitor* nv, uint32_t device1ID);
589 
590  osg::observer_ptr<const OpenVRDevice> _ovrDevice;
591 
592  float _laserSelectedWidth;
593  osg::Vec4 _laserSelectedColor;
594  float _triggerThreshold;
595  };
596 
597 } // !namespace OpenFrames
598 
599 #endif // !define _OF_OPENVRDEVICE_
void getRecommendedTextureSize(int &w, int &h) const
Definition: OpenVRDevice.hpp:102
MotionMode
Definition: OpenVRDevice.hpp:468
PickMode
Definition: OpenVRDevice.hpp:569
DeviceClass
Definition: OpenVRDevice.hpp:180
Encapsulates textures used for VR offscreen rendering.
Definition: VRUtils.hpp:42
Encapsulates an OpenVR device&#39;s model.
Definition: OpenVRDevice.hpp:194
osg::MatrixTransform * getTransform() const
Get Local->World matrix (transforms points in Laser frame to Controller frame)
Definition: OpenVRDevice.hpp:125
std::map< std::string, osg::ref_ptr< osg::Geode > > DeviceGeodeMap
Definition: OpenVRDevice.hpp:303
Represents data needed to use an OpenVR-supported HMD.
Definition: OpenVRDevice.hpp:87
osg::MatrixTransform * getDeviceRenderModels() const
Definition: OpenVRDevice.hpp:111
Defines transforms that can perform complex view transformations.
Definition: View.hpp:220
This class extends FollowingTrackball to include the OpenVR HMD transform.
Definition: OpenVRDevice.hpp:404
void setUserHeight(double userHeight)
Definition: OpenVRDevice.hpp:265
osg::Vec3d & getRightEyeViewOffset()
Definition: OpenVRDevice.hpp:236
const osg::Timer_t & getUpdateTime() const
Last time any laser property was updated.
Definition: OpenVRDevice.hpp:159
Definition: CoordinateAxes.hpp:29
An OSG-compatible event adapter for OpenVR.
Definition: OpenVRDevice.hpp:70
osg::MatrixTransform * getGroundPlane()
Definition: OpenVRDevice.hpp:269
This class computes per-eye view matrices.
Definition: OpenVRDevice.hpp:376
Encapsulates the laser attached to OpenVR devices (usually controllers).
Definition: OpenVRDevice.hpp:119
Polls for OpenVR events and stores them in its OSG event queue.
Definition: OpenVRDevice.hpp:352
unsigned int getNumDeviceModels() const
Definition: OpenVRDevice.hpp:215
const osg::Matrixd & getHMDPoseMatrix() const
Definition: OpenVRDevice.hpp:226
Event callback that shows/hides a VR controller&#39;s laser when its trigger is pressed.
Definition: OpenVRDevice.hpp:317
std::vector< DeviceModel > DeviceModelVector
TODO: Make this a vector of pointers, then DeviceModel can be subclassed.
Definition: OpenVRDevice.hpp:308
Event handler that enables clicking on an image using VR controllers.
Definition: OpenVRDevice.hpp:526
Data used when computing world transformations during user events.
Definition: OpenVRDevice.hpp:581
vr::IVRSystem * getVRSystem() const
Definition: OpenVRDevice.hpp:278
bool isInitialized() const
Definition: OpenVRDevice.hpp:108
Data used when computing world transformations during user events.
Definition: OpenVRDevice.hpp:485
void setLaserHideDelay(const double &delay)
Definition: OpenVRDevice.hpp:137
This class submits eye textures to OpenVR.
Definition: OpenVRDevice.hpp:508