Hi Robert,
Most recent changes to CameraPathEventHandler change path writing method. Now
control points are written on the fly. But default stream precision is not
adjusted as it used to and remains set to 6 digits (at least with VS 2008) so
larger coordinates like positions on Earth Ellipsoid loose lots of fidelity.
This patch fixes this issue.
Cheers,
Wojtek
/* -*-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 <limits.h>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <osgDB/FileNameUtils>
#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),
_autoinc( -1 ),
_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();
double animationPathTime =
osg::Timer::instance()->delta_s(_animStartTime, time);
_animPath->insert(animationPathTime,
osg::AnimationPath::ControlPoint(m.getTrans(), m.getRotate()));
_delta = 0.0f;
if (_fout)
{
_animPath->write(_animPath->getTimeControlPointMap().find(animationPathTime),
_fout);
_fout.flush();
}
}
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();
if (!_filename.empty())
{
std::stringstream ss;
ss << osgDB::getNameLessExtension(_filename);
if ( _autoinc != -1 )
{
ss << "_"<<std::setfill( '0' ) << std::setw( 2 ) <<
_autoinc;
_autoinc++;
}
ss << "."<<osgDB::getFileExtension(_filename);
osg::notify(osg::NOTICE) << "Recording camera path to
file " << ss.str() << std::endl;
_fout.open( ss.str().c_str() );
// make sure doubles are not trucated by default stream
precision = 6
_fout.precision( 15 );
}
else
{
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;
if (_fout) _fout.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;
}
LODScaleHandler::LODScaleHandler():
_keyEventIncreaseLODScale('*'),
_keyEventDecreaseLODScale('/')
{
}
bool LODScaleHandler::handle(const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa)
{
osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
osg::Camera* camera = view ? view->getCamera() : 0;
if (!camera) return false;
if (ea.getHandled()) return false;
switch(ea.getEventType())
{
case(osgGA::GUIEventAdapter::KEYUP):
{
if (ea.getKey() == _keyEventIncreaseLODScale)
{
camera->setLODScale(camera->getLODScale()*1.1);
osg::notify(osg::NOTICE)<<"LODScale =
"<<camera->getLODScale()<<std::endl;
return true;
}
else if (ea.getKey() == _keyEventDecreaseLODScale)
{
camera->setLODScale(camera->getLODScale()/1.1);
osg::notify(osg::NOTICE)<<"LODScale =
"<<camera->getLODScale()<<std::endl;
return true;
}
break;
}
default:
break;
}
return false;
}
void LODScaleHandler::getUsage(osg::ApplicationUsage& usage) const
{
{
std::ostringstream ostr;
ostr<<char(_keyEventIncreaseLODScale);
usage.addKeyboardMouseBinding(ostr.str(),"Increase LODScale.");
}
{
std::ostringstream ostr;
ostr<<char(_keyEventDecreaseLODScale);
usage.addKeyboardMouseBinding(ostr.str(),"Decrease LODScale.");
}
}
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org