Files
OpenSceneGraph/src/osgViewer/ViewerEventHandlers.cpp

519 lines
19 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <stdlib.h>
#include <float.h>
#include <fstream>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
namespace osgViewer
{
/*
** WindowSizeHandler
*/
WindowSizeHandler::WindowSizeHandler() :
_keyEventToggleFullscreen('f'),
_toggleFullscreen(true),
_keyEventWindowedResolutionUp('>'),
_keyEventWindowedResolutionDown('<'),
_changeWindowedResolution(true),
_currentResolutionIndex(-1)
{
_resolutionList.push_back(osg::Vec2(640, 480));
_resolutionList.push_back(osg::Vec2(800, 600));
_resolutionList.push_back(osg::Vec2(1024, 768));
_resolutionList.push_back(osg::Vec2(1152, 864));
_resolutionList.push_back(osg::Vec2(1280, 720));
_resolutionList.push_back(osg::Vec2(1280, 768));
_resolutionList.push_back(osg::Vec2(1280, 1024));
_resolutionList.push_back(osg::Vec2(1440, 900));
_resolutionList.push_back(osg::Vec2(1400, 1050));
_resolutionList.push_back(osg::Vec2(1600, 900));
_resolutionList.push_back(osg::Vec2(1600, 1024));
_resolutionList.push_back(osg::Vec2(1600, 1200));
_resolutionList.push_back(osg::Vec2(1680, 1050));
_resolutionList.push_back(osg::Vec2(1920, 1080));
_resolutionList.push_back(osg::Vec2(1920, 1200));
_resolutionList.push_back(osg::Vec2(2048, 1536));
_resolutionList.push_back(osg::Vec2(2560, 2048));
_resolutionList.push_back(osg::Vec2(3200, 2400));
_resolutionList.push_back(osg::Vec2(3840, 2400));
}
void WindowSizeHandler::getUsage(osg::ApplicationUsage &usage) const
{
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventToggleFullscreen), "Toggle full screen.");
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventWindowedResolutionUp), "Increase the screen resolution (in windowed mode).");
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventWindowedResolutionDown), "Decrease the screen resolution (in windowed mode).");
}
bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
if (!view) return false;
osgViewer::ViewerBase* viewer = view->getViewerBase();
if (viewer == NULL)
{
return false;
}
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
{
if (_toggleFullscreen == true && ea.getKey() == _keyEventToggleFullscreen)
{
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
itr != windows.end();
++itr)
{
toggleFullscreen(*itr);
}
return true;
}
else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionUp)
{
// Increase resolution
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
itr != windows.end();
++itr)
{
changeWindowedResolution(*itr, true);
}
return true;
}
else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionDown)
{
// Decrease resolution
osgViewer::Viewer::Windows windows;
viewer->getWindows(windows);
for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
itr != windows.end();
++itr)
{
changeWindowedResolution(*itr, false);
}
return true;
}
break;
}
default:
break;
}
return false;
}
void WindowSizeHandler::toggleFullscreen(osgViewer::GraphicsWindow *window)
{
osg::GraphicsContext::WindowingSystemInterface *wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (wsi == NULL)
{
osg::notify(osg::NOTICE) << "Error, no WindowSystemInterface available, cannot toggle window fullscreen." << std::endl;
return;
}
unsigned int screenWidth;
unsigned int screenHeight;
wsi->getScreenResolution(*(window->getTraits()), screenWidth, screenHeight);
int x;
int y;
int width;
int height;
window->getWindowRectangle(x, y, width, height);
bool isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight;
if (isFullScreen)
{
osg::Vec2 resolution;
if (_currentResolutionIndex == -1)
{
_currentResolutionIndex = getNearestResolution(screenWidth, screenHeight, screenWidth / 2, screenHeight / 2);
}
resolution = _resolutionList[_currentResolutionIndex];
window->setWindowDecoration(true);
window->setWindowRectangle((screenWidth - (int)resolution.x()) / 2, (screenHeight - (int)resolution.y()) / 2, (int)resolution.x(), (int)resolution.y());
osg::notify(osg::INFO) << "Screen resolution = " << (int)resolution.x() << "x" << (int)resolution.y() << std::endl;
}
else
{
window->setWindowDecoration(false);
window->setWindowRectangle(0, 0, screenWidth, screenHeight);
}
window->grabFocusIfPointerInWindow();
}
void WindowSizeHandler::changeWindowedResolution(osgViewer::GraphicsWindow *window, bool increase)
{
osg::GraphicsContext::WindowingSystemInterface *wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (wsi == NULL)
{
osg::notify(osg::NOTICE) << "Error, no WindowSystemInterface available, cannot toggle window fullscreen." << std::endl;
return;
}
unsigned int screenWidth;
unsigned int screenHeight;
wsi->getScreenResolution(*(window->getTraits()), screenWidth, screenHeight);
int x;
int y;
int width;
int height;
window->getWindowRectangle(x, y, width, height);
bool isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight;
if (window->getWindowDecoration() == true || isFullScreen == false)
{
osg::Vec2 resolution;
if (_currentResolutionIndex == -1)
{
_currentResolutionIndex = getNearestResolution(screenWidth, screenHeight, width, height);
}
if (increase == true)
{
// Find the next resolution
for (int i = _currentResolutionIndex + 1; i < (int)_resolutionList.size(); ++i)
{
if ((unsigned int)_resolutionList[i].x() <= screenWidth && (unsigned int)_resolutionList[i].y() <= screenHeight)
{
_currentResolutionIndex = i;
break;
}
}
}
else
{
// Find the previous resolution
for (int i = _currentResolutionIndex - 1; i >= 0; --i)
{
if ((unsigned int)_resolutionList[i].x() <= screenWidth && (unsigned int)_resolutionList[i].y() <= screenHeight)
{
_currentResolutionIndex = i;
break;
}
}
}
resolution = _resolutionList[_currentResolutionIndex];
window->setWindowDecoration(true);
window->setWindowRectangle((screenWidth - (int)resolution.x()) / 2, (screenHeight - (int)resolution.y()) / 2, (int)resolution.x(), (int)resolution.y());
osg::notify(osg::INFO) << "Screen resolution = " << (int)resolution.x() << "x" << (int)resolution.y() << std::endl;
window->grabFocusIfPointerInWindow();
}
}
unsigned int WindowSizeHandler::getNearestResolution(int screenWidth, int screenHeight, int width, int height) const
{
unsigned int position = 0;
unsigned int result = 0;
int delta = INT_MAX;
for (std::vector<osg::Vec2>::const_iterator it = _resolutionList.begin();
it != _resolutionList.end();
++it, ++position)
{
if ((int)it->x() <= screenWidth && (int)it->y() <= screenHeight)
{
int tmp = static_cast<int>(osg::absolute((width * height) - (it->x() * it->y())));
if (tmp < delta)
{
delta = tmp;
result = position;
}
}
}
return (result);
}
/*
** ThreadingHandler
*/
ThreadingHandler::ThreadingHandler() :
_keyEventChangeThreadingModel('m'),
_changeThreadingModel(true),
_keyEventChangeEndBarrierPosition('e'),
_changeEndBarrierPosition(true)
{
_tickOrLastKeyPress = osg::Timer::instance()->tick();
}
void ThreadingHandler::getUsage(osg::ApplicationUsage &usage) const
{
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventChangeThreadingModel), "Toggle threading model.");
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventChangeEndBarrierPosition), "Toggle the placement of the end of frame barrier.");
}
bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
if (!view) return false;
osgViewer::ViewerBase* viewerBase = view->getViewerBase();
osgViewer::Viewer* viewer = dynamic_cast<Viewer*>(viewerBase);
if (viewerBase == NULL)
{
return false;
}
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
{
double delta = osg::Timer::instance()->delta_s(_tickOrLastKeyPress, osg::Timer::instance()->tick());
if (_changeThreadingModel == true && ea.getKey() == _keyEventChangeThreadingModel && delta > 1.0)
{
_tickOrLastKeyPress = osg::Timer::instance()->tick();
switch(viewerBase->getThreadingModel())
{
case(osgViewer::ViewerBase::SingleThreaded):
viewerBase->setThreadingModel(osgViewer::ViewerBase::CullDrawThreadPerContext);
osg::notify(osg::NOTICE)<<"Threading model 'CullDrawThreadPerContext' selected."<<std::endl;
break;
case(osgViewer::ViewerBase::CullDrawThreadPerContext):
viewerBase->setThreadingModel(osgViewer::ViewerBase::DrawThreadPerContext);
osg::notify(osg::NOTICE)<<"Threading model 'DrawThreadPerContext' selected."<<std::endl;
break;
case(osgViewer::ViewerBase::DrawThreadPerContext):
viewerBase->setThreadingModel(osgViewer::ViewerBase::CullThreadPerCameraDrawThreadPerContext);
osg::notify(osg::NOTICE)<<"Threading model 'CullThreadPerCameraDrawThreadPerContext' selected."<<std::endl;
break;
case(osgViewer::ViewerBase::CullThreadPerCameraDrawThreadPerContext):
viewerBase->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
osg::notify(osg::NOTICE)<<"Threading model 'SingleThreaded' selected."<<std::endl;
break;
#if 1
case(osgViewer::ViewerBase::AutomaticSelection):
viewerBase->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
osg::notify(osg::NOTICE)<<"Threading model 'AutomaticSelection' selected."<<std::endl;
#else
case(osgViewer::ViewerBase::AutomaticSelection):
viewerBase->setThreadingModel(viewer->suggestBestThreadingModel());
osg::notify(osg::NOTICE)<<"Threading model 'AutomaticSelection' selected."<<std::endl;
#endif
break;
}
return true;
}
if (viewer && _changeEndBarrierPosition == true && ea.getKey() == _keyEventChangeEndBarrierPosition)
{
switch(viewer->getEndBarrierPosition())
{
case(osgViewer::Viewer::BeforeSwapBuffers):
viewer->setEndBarrierPosition(osgViewer::Viewer::AfterSwapBuffers);
osg::notify(osg::NOTICE)<<"Threading model 'AfterSwapBuffers' selected."<<std::endl;
break;
case(osgViewer::Viewer::AfterSwapBuffers):
viewer->setEndBarrierPosition(osgViewer::Viewer::BeforeSwapBuffers);
osg::notify(osg::NOTICE)<<"Threading model 'BeforeSwapBuffers' selected."<<std::endl;
break;
}
return true;
}
break;
}
default:
break;
}
return false;
}
RecordCameraPathHandler::RecordCameraPathHandler(const std::string& filename):
_filename(filename),
_keyEventToggleRecord('z'),
_keyEventTogglePlayback('Z'),
_currentlyRecording(false),
_currentlyPlaying(false),
_delta(0.0f),
_animStartTime(0),
_lastFrameTime(osg::Timer::instance()->tick())
{
_animPath = new osg::AnimationPath();
const char* str = getenv("OSG_RECORD_CAMERA_PATH_FPS");
if (str) _interval = 1.0f / atof(str);
else _interval = 1.0f / 25.0f;
}
void RecordCameraPathHandler::getUsage(osg::ApplicationUsage &usage) const
{
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventToggleRecord), "Toggle camera path recording.");
usage.addKeyboardMouseBinding(reinterpret_cast<const char*>(&_keyEventTogglePlayback), "Toggle camera path playback.");
}
bool RecordCameraPathHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
{
osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
if (view == NULL)
{
return false;
}
if(ea.getEventType()==osgGA::GUIEventAdapter::FRAME)
{
// Calculate our current delta (difference) in time between the last frame and
// current frame, regardless of whether we actually store a ControlPoint...
osg::Timer_t time = osg::Timer::instance()->tick();
double delta = osg::Timer::instance()->delta_s(_lastFrameTime, time);
_lastFrameTime = time;
// If our internal _delta is finally large enough to warrant a ControlPoint
// insertion, do so now. Be sure and reset the internal _delta, so we can start
// calculating when the next insert should happen.
if (_currentlyRecording && _delta >= _interval)
{
const osg::Matrixd& m = view->getCamera()->getInverseViewMatrix();
_animPath->insert(osg::Timer::instance()->delta_s(_animStartTime, time), osg::AnimationPath::ControlPoint(m.getTrans(), m.getRotate()));
_delta = 0.0f;
}
else _delta += delta;
return true;
}
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
{
// The user has requested to toggle recording.
if (ea.getKey() ==_keyEventToggleRecord)
{
// The user has requested to BEGIN recording.
if (!_currentlyRecording)
{
_currentlyRecording = true;
_animStartTime = osg::Timer::instance()->tick();
_animPath->clear();
osg::notify(osg::NOTICE)<<"Recording camera path."<<std::endl;
}
// THe user has requested to STOP recording, write the file!
else
{
_currentlyRecording = false;
_delta = 0.0f;
// In the future this will need to be written continuously, rather
// than all at once.
std::ofstream out(_filename.c_str());
osg::notify(osg::NOTICE)<<"Writing camera file: "<<_filename<<std::endl;
_animPath->write(out);
out.close();
}
return true;
}
// The user has requested to toggle playback. You'll notice in the code below that
// we take over the current manipulator; it was originally recommended that we
// check for a KeySwitchManipulator, create one if not present, and then add this
// to either the newly created one or the existing one. However, the code do that was
// EXTREMELY dirty, so I opted for a simpler solution. At a later date, someone may
// want to implement the original recommendation (which is in a mailing list reply
// from June 1st by Robert in a thread called "osgviewer Camera Animation (preliminary)".
else if (ea.getKey() == _keyEventTogglePlayback)
{
if (_currentlyRecording)
{
_currentlyRecording = false;
_delta = 0.0f;
// In the future this will need to be written continuously, rather
// than all at once.
std::ofstream out(_filename.c_str());
osg::notify(osg::NOTICE)<<"Writing camera file: "<<_filename<<std::endl;
_animPath->write(out);
out.close();
}
// The user has requested to BEGIN playback.
if (!_currentlyPlaying)
{
_animPathManipulator = new osgGA::AnimationPathManipulator(_animPath.get());
_animPathManipulator->home(ea,aa);
// If we successfully found our _filename file, set it and keep a copy
// around of the original MatrixManipulator to restore later.
if (_animPathManipulator.valid() && _animPathManipulator->valid())
{
_oldManipulator = view->getCameraManipulator();
view->setCameraManipulator(_animPathManipulator.get());
_currentlyPlaying = true;
}
}
// The user has requested to STOP playback.
else
{
// Restore the old manipulator if necessary and stop playback.
if(_oldManipulator.valid()) view->setCameraManipulator(_oldManipulator.get());
_currentlyPlaying = false;
_oldManipulator = 0;
}
return true;
}
break;
}
default:
break;
}
return false;
}
}