/* -*-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 #include #include #include 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(&_keyEventToggleFullscreen), "Toggle full screen."); usage.addKeyboardMouseBinding(reinterpret_cast(&_keyEventWindowedResolutionUp), "Increase the screen resolution (in windowed mode)."); usage.addKeyboardMouseBinding(reinterpret_cast(&_keyEventWindowedResolutionDown), "Decrease the screen resolution (in windowed mode)."); } bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) { osgViewer::Viewer *viewer = dynamic_cast(&aa); 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::const_iterator it = _resolutionList.begin(); it != _resolutionList.end(); ++it, ++position) { if ((int)it->x() <= screenWidth && (int)it->y() <= screenHeight) { int tmp = static_cast(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(&_keyEventChangeThreadingModel), "Toggle threading model."); usage.addKeyboardMouseBinding(reinterpret_cast(&_keyEventChangeEndBarrierPosition), "Toggle the placement of the end of frame barrier."); } bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) { osgViewer::Viewer *viewer = dynamic_cast(&aa); if (viewer == 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(viewer->getThreadingModel()) { case(osgViewer::Viewer::SingleThreaded): viewer->setThreadingModel(osgViewer::Viewer::CullDrawThreadPerContext); osg::notify(osg::NOTICE)<<"Threading model 'CullDrawThreadPerContext' selected."<setThreadingModel(osgViewer::Viewer::DrawThreadPerContext); osg::notify(osg::NOTICE)<<"Threading model 'DrawThreadPerContext' selected."<setThreadingModel(osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext); osg::notify(osg::NOTICE)<<"Threading model 'CullThreadPerCameraDrawThreadPerContext' selected."<setThreadingModel(osgViewer::Viewer::SingleThreaded); osg::notify(osg::NOTICE)<<"Threading model 'SingleThreaded' selected."<setThreadingModel(viewer->suggestBestThreadingModel()); osg::notify(osg::NOTICE)<<"Threading model 'AutomaticSelection' selected."<getEndBarrierPosition()) { case(osgViewer::Viewer::BeforeSwapBuffers): viewer->setEndBarrierPosition(osgViewer::Viewer::AfterSwapBuffers); osg::notify(osg::NOTICE)<<"Threading model 'AfterSwapBuffers' selected."<setEndBarrierPosition(osgViewer::Viewer::BeforeSwapBuffers); osg::notify(osg::NOTICE)<<"Threading model 'BeforeSwapBuffers' selected."<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(&_keyEventToggleRecord), "Toggle camera path recording."); usage.addKeyboardMouseBinding(reinterpret_cast(&_keyEventTogglePlayback), "Toggle camera path playback."); } bool RecordCameraPathHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) { osgViewer::Viewer* viewer = dynamic_cast(&aa); if (viewer == 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 = viewer->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."<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 recomendation (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<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 succesfully found our _filename file, set it and keep a copy // around of the original MatrixManipulator to restore later. if (_animPathManipulator.valid() && _animPathManipulator->valid()) { _oldManipulator = viewer->getCameraManipulator(); viewer->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()) viewer->setCameraManipulator(_oldManipulator.get()); _currentlyPlaying = false; _oldManipulator = 0; } return true; } break; } default: break; } return false; } }