oculus_display.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2012, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <string>
00031 #include <iostream>
00032 
00033 #include <QWidget>
00034 #include <QDesktopWidget>
00035 #include <QApplication>
00036 
00037 #include <OVR.h>
00038 
00039 #include <boost/bind.hpp>
00040 
00041 #include <OGRE/OgreRoot.h>
00042 #include <OGRE/OgreSceneNode.h>
00043 #include <OGRE/OgreRenderWindow.h>
00044 
00045 #include <ros/package.h>
00046 
00047 #include <rviz/properties/bool_property.h>
00048 #include <rviz/properties/status_property.h>
00049 #include <rviz/properties/float_property.h>
00050 #include <rviz/properties/string_property.h>
00051 #include <rviz/properties/tf_frame_property.h>
00052 #include <rviz/properties/vector_property.h>
00053 
00054 #include <rviz/window_manager_interface.h>
00055 #include <rviz/view_manager.h>
00056 #include <rviz/render_panel.h>
00057 #include <rviz/display_context.h>
00058 #include <rviz/ogre_helpers/render_widget.h>
00059 #include <rviz/ogre_helpers/render_system.h>
00060 #include <rviz/frame_manager.h>
00061 
00062 #include "oculus_rviz_plugins/oculus_display.h"
00063 #include "oculus_rviz_plugins/ogre_oculus.h"
00064 
00065 namespace oculus_rviz_plugins
00066 {
00067 
00068 OculusDisplay::OculusDisplay()
00069 : render_widget_(0)
00070 , scene_node_(0)
00071 {
00072   std::string rviz_path = ros::package::getPath(ROS_PACKAGE_NAME);
00073   Ogre::ResourceGroupManager::getSingleton().addResourceLocation( rviz_path + "/ogre_media", "FileSystem", ROS_PACKAGE_NAME );
00074   Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup(ROS_PACKAGE_NAME);
00075 
00076   connect( QApplication::desktop(), SIGNAL( screenCountChanged ( int ) ), this, SLOT( onScreenCountChanged(int)) );
00077 }
00078 
00079 OculusDisplay::~OculusDisplay()
00080 {
00081   oculus_.reset();
00082   delete render_widget_;
00083 }
00084 
00085 
00086 void OculusDisplay::onInitialize()
00087 {
00088   fullscreen_property_ = new rviz::BoolProperty( "Render to Oculus", false,
00089     "If checked, will render fullscreen on your secondary screen. Otherwise, shows a window.",
00090     this, SLOT(onFullScreenChanged()));
00091 
00092   prediction_dt_property_ = new rviz::FloatProperty( "Motion prediction (ms)", 30.0,
00093     "Time in ms to predict head motion. Decreases overall latency and motion sickness.",
00094     this, SLOT(onPredictionDtChanged()) );
00095 
00096   near_clip_property_ = new rviz::FloatProperty( "Near Clip Distance", 0.02,
00097     "Minimum rendering distance for Oculus camera.",
00098     this );
00099 
00100   horizontal_property_ = new rviz::BoolProperty( "Fixed Horizon", true,
00101     "If checked, will ignore the pitch component of the RViz camera.", this);
00102 
00103   follow_cam_property_ = new rviz::BoolProperty( "Follow RViz Camera", true,
00104     "If checked, will set the Oculus camera to the same position as the main view camera.",
00105     this, SLOT( onFollowCamChanged() ) );
00106 
00107   tf_frame_property_ = new rviz::TfFrameProperty( "Target Frame", "<Fixed Frame>",
00108     "Tf frame that the Oculus camera should follow.", this, context_->getFrameManager(), true );
00109 
00110   offset_property_ = new rviz::VectorProperty( "Offset", Ogre::Vector3(0,0,0),
00111     "Additional offset of the Oculus camera from the followed RViz camera or target frame.", this );
00112 
00113   pub_tf_property_ = new rviz::BoolProperty( "Publish tf", true,
00114     "If checked, will publish the pose of the Oculus camera as a tf frame.",
00115     this, SLOT( onPubTfChanged() ) );
00116 
00117   pub_tf_frame_property_ = new rviz::StringProperty( "Tf Frame", "oculus",
00118     "Name of the published tf frame.", this );
00119 
00120   render_widget_ = new rviz::RenderWidget( rviz::RenderSystem::get() );
00121   render_widget_->setVisible(false);
00122   render_widget_->setWindowTitle( "Oculus View" );
00123 
00124   render_widget_->setParent( context_->getWindowManager()->getParentWindow() );
00125   render_widget_->setWindowFlags( Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint );
00126 
00127   Ogre::RenderWindow *window = render_widget_->getRenderWindow();
00128   window->setVisible(false);
00129   window->setAutoUpdated(false);
00130   window->addListener(this);
00131 
00132   scene_node_ = scene_manager_->getRootSceneNode()->createChildSceneNode();
00133 }
00134 
00135 
00136 void OculusDisplay::onFollowCamChanged()
00137 {
00138   tf_frame_property_->setHidden( follow_cam_property_->getBool() );
00139 }
00140 
00141 void OculusDisplay::onPubTfChanged()
00142 {
00143   pub_tf_frame_property_->setHidden( !pub_tf_property_->getBool() );
00144 }
00145 
00146 void OculusDisplay::onPredictionDtChanged( )
00147 {
00148   if ( !oculus_ || !isEnabled() )
00149   {
00150     return;
00151   }
00152 
00153   oculus_->setPredictionDt( prediction_dt_property_->getFloat() * 0.001 );
00154 }
00155 
00156 
00157 void OculusDisplay::onScreenCountChanged( int newCount )
00158 {
00159   if ( newCount == 1 )
00160   {
00161     fullscreen_property_->setBool(false);
00162     fullscreen_property_->setHidden(true);
00163     setStatus( rviz::StatusProperty::Error, "Screen", "No secondary screen detected. Cannot render to Oculus device.");
00164   }
00165   else
00166   {
00167     fullscreen_property_->setHidden(false);
00168     setStatus( rviz::StatusProperty::Ok, "Screen", "Using screen #2.");
00169   }
00170 }
00171 
00172 
00173 void OculusDisplay::onFullScreenChanged()
00174 {
00175   if ( !oculus_ || !isEnabled() )
00176   {
00177     return;
00178   }
00179 
00180   if ( fullscreen_property_->getBool() && QApplication::desktop()->numScreens() > 1 )
00181   {
00182     QRect screen_res = QApplication::desktop()->screenGeometry(1);
00183     //render_widget->setWindowFlags();
00184     render_widget_->setGeometry( screen_res );
00185     //render_widget->show();
00186     render_widget_->showFullScreen();
00187   }
00188   else
00189   {
00190     int x_res = 1280;
00191     int y_res = 800;
00192     if ( oculus_->getHMDDevice() )
00193     {
00194       OVR::HMDInfo info;
00195       oculus_->getHMDDevice()->GetDeviceInfo( &info );
00196       x_res = info.HResolution;
00197       y_res = info.VResolution;
00198     }
00199     int primary_screen = QApplication::desktop()->primaryScreen();
00200     QRect screen_res = QApplication::desktop()->screenGeometry( primary_screen );
00201     render_widget_->setGeometry( screen_res.x(), screen_res.y(), x_res, y_res );
00202     render_widget_->showNormal();
00203   }
00204 }
00205 
00206 void OculusDisplay::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
00207 {
00208   if ( !oculus_ )
00209   {
00210     return;
00211   }
00212   updateCamera();
00213 }
00214 
00215 void OculusDisplay::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
00216 {
00217   if ( !oculus_ )
00218   {
00219     return;
00220   }
00221   Ogre::RenderWindow *window = render_widget_->getRenderWindow();
00222   window->swapBuffers();
00223 }
00224 
00225 void OculusDisplay::onEnable()
00226 {
00227   if ( oculus_ )
00228   {
00229      return;
00230   }
00231 
00232   oculus_.reset( new Oculus() );
00233   oculus_->setupOculus();
00234 
00235   if ( !oculus_->isOculusReady() )
00236   {
00237     oculus_.reset();
00238     setStatusStd( rviz::StatusProperty::Error, "Oculus", "No Oculus device found!" );
00239     return;
00240   }
00241 
00242   setStatusStd( rviz::StatusProperty::Ok, "Oculus", "Oculus is ready." );
00243 
00244   Ogre::RenderWindow *window = render_widget_->getRenderWindow();
00245   oculus_->setupOgre( scene_manager_, window, scene_node_ );
00246 
00247   render_widget_->setVisible( oculus_->isOculusReady() );
00248 
00249   onScreenCountChanged( QApplication::desktop()->numScreens() );
00250   onFullScreenChanged();
00251   onPredictionDtChanged();
00252 }
00253 
00254 void OculusDisplay::onDisable()
00255 {
00256   clearStatuses();
00257   render_widget_->setVisible(false);
00258   if ( oculus_ )
00259   {
00260     oculus_.reset();
00261   }
00262 }
00263 
00264 void OculusDisplay::update( float wall_dt, float ros_dt )
00265 {
00266   if ( !oculus_ )
00267   {
00268     return;
00269   }
00270 
00271   updateCamera();
00272   Ogre::RenderWindow *window = render_widget_->getRenderWindow();
00273   window->update(false);
00274 
00275   if ( oculus_->isMagCalibrated() )
00276   {
00277     setStatus( rviz::StatusProperty::Ok, "Magnetometer", "Magnetometer calibrated.");
00278   }
00279   else
00280   {
00281     setStatus( rviz::StatusProperty::Warn, "Magnetometer", "Magnetometer not calibrated. Look left/right/up/down to collect enough samples.");
00282   }
00283 }
00284 
00285 void OculusDisplay::updateCamera()
00286 {
00287   if (!oculus_ || !oculus_->isOculusReady())
00288   {
00289     return;
00290   }
00291 
00292   Ogre::Vector3 pos;
00293   Ogre::Quaternion ori;
00294 
00295 
00296   if ( follow_cam_property_->getBool() )
00297   {
00298     const Ogre::Camera *cam = context_->getViewManager()->getCurrent()->getCamera();
00299     pos = cam->getDerivedPosition();
00300     ori = cam->getDerivedOrientation();
00301   }
00302   else
00303   {
00304     context_->getFrameManager()->getTransform( tf_frame_property_->getStdString(),
00305                                                ros::Time(), pos, ori );
00306     Ogre::Quaternion r;
00307     r.FromAngleAxis( Ogre::Radian(M_PI*0.5), Ogre::Vector3::UNIT_X );
00308     ori = ori * r;
00309     r.FromAngleAxis( Ogre::Radian(-M_PI*0.5), Ogre::Vector3::UNIT_Y );
00310     ori = ori * r;
00311   }
00312 
00313   pos += offset_property_->getVector();
00314 
00315   scene_node_->setPosition( pos );
00316 
00317 
00318   if ( horizontal_property_->getBool() )
00319   {
00320     Ogre::Vector3 x_axis = ori * Ogre::Vector3(1,0,0);
00321     float yaw = atan2( x_axis.y, x_axis.x );// - M_PI*0.5;
00322 
00323     // we're working in OpenGL coordinates now
00324     ori.FromAngleAxis( Ogre::Radian(yaw), Ogre::Vector3::UNIT_Z );
00325 
00326     Ogre::Quaternion r;
00327     r.FromAngleAxis( Ogre::Radian(M_PI*0.5), Ogre::Vector3::UNIT_X );
00328     ori = ori * r;
00329   }
00330 
00331   scene_node_->setOrientation( ori );
00332 
00333   Ogre::ColourValue bg_color = context_->getViewManager()->getRenderPanel()->getViewport()->getBackgroundColour();
00334 
00335   for ( int i =0; i<2; i++ )
00336   {
00337     oculus_->getViewport(i)->setBackgroundColour( bg_color );
00338     oculus_->getCamera(i)->setNearClipDistance( near_clip_property_->getFloat() );
00339 
00340     // this is a hack to circumvent a bug in Ogre 1.8
00341     // otherwise one of the viewports will not update it's background color
00342     bg_color.g += 0.0001;
00343   }
00344 
00345   oculus_->updateProjectionMatrices();
00346   oculus_->update();
00347 
00348   if ( pub_tf_property_->getBool() )
00349   {
00350     tf::StampedTransform pose;
00351     pose.frame_id_ = context_->getFixedFrame().toStdString();
00352     pose.child_frame_id_ = pub_tf_frame_property_->getStdString();
00353     pose.stamp_ = ros::Time::now();
00354     ori = ori * oculus_->getOrientation();
00355     Ogre::Quaternion r;
00356     r.FromAngleAxis( Ogre::Radian(M_PI*0.5), Ogre::Vector3::UNIT_Y );
00357     ori = ori * r;
00358     r.FromAngleAxis( Ogre::Radian(-M_PI*0.5), Ogre::Vector3::UNIT_X );
00359     ori = ori * r;
00360     //Ogre::Matrix3
00361     pose.setRotation( tf::Quaternion( ori.x, ori.y, ori.z, ori.w ) );
00362     pose.setOrigin( tf::Vector3( pos.x, pos.y, pos.z ) );
00363     tf_pub_.sendTransform( pose );
00364   }
00365 }
00366 
00367 void OculusDisplay::reset()
00368 {
00369   rviz::Display::reset();
00370   if ( oculus_ )
00371   {
00372     onDisable();
00373     onEnable();
00374   }
00375 }
00376 
00377 } // namespace rviz
00378 
00379 #include <pluginlib/class_list_macros.h>
00380 PLUGINLIB_EXPORT_CLASS( oculus_rviz_plugins::OculusDisplay, rviz::Display )


oculus_rviz_plugins
Author(s): David Gossow
autogenerated on Mon Oct 6 2014 03:00:55