diff --git a/applications/osgviewer/osgviewer.cpp b/applications/osgviewer/osgviewer.cpp index e70980267..f11c904a6 100644 --- a/applications/osgviewer/osgviewer.cpp +++ b/applications/osgviewer/osgviewer.cpp @@ -117,6 +117,9 @@ int main(int argc, char** argv) // add the help handler viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + // add the camera path handler + viewer.addEventHandler(new osgViewer::AnimationPathHandler); + while (arguments.read("--SingleThreaded")) viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); while (arguments.read("--CullDrawThreadPerContext")) viewer.setThreadingModel(osgViewer::Viewer::CullDrawThreadPerContext); while (arguments.read("--DrawThreadPerContext")) viewer.setThreadingModel(osgViewer::Viewer::DrawThreadPerContext); diff --git a/include/osgViewer/ViewerEventHandlers b/include/osgViewer/ViewerEventHandlers index 0bbcc2564..05884f2a3 100644 --- a/include/osgViewer/ViewerEventHandlers +++ b/include/osgViewer/ViewerEventHandlers @@ -14,6 +14,7 @@ #ifndef OSGVIEWER_VIEWEREVENTHANDLER #define OSGVIEWER_VIEWEREVENTHANDLER 1 +#include #include #include @@ -102,6 +103,36 @@ protected: bool _done; }; +/** +Handler allowing the user to record the animation "path" of a camera. In it's current +implementation, this handler cannot guarantee the final view matrix is correct; it is +conceivable that the matrix may be one frame off. Eh--not a big deal! :) +TODO: Find the good filename. +TODO: Write as we go, not when it's all done. +TODO: Create osgviewer on-screen indication that animation is taking place. +TODO: Explore multi-cameras thing. +TODO: Investigate crash if non-focused camera? +*/ +class AnimationPathHandler : public osgGA::GUIEventHandler +{ +public: + + AnimationPathHandler(); + + virtual void getUsage(osg::ApplicationUsage &usage) const; + + bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa); + +protected: + + bool _currentlyRecording; + double _interval; + double _delta; + osg::Timer_t _animStartTime; + osg::Timer_t _lastFrameTime; + osg::ref_ptr _animPath; +}; + } #endif diff --git a/src/osgViewer/ViewerEventHandlers.cpp b/src/osgViewer/ViewerEventHandlers.cpp index 1ff47e4d6..0ca59df48 100644 --- a/src/osgViewer/ViewerEventHandlers.cpp +++ b/src/osgViewer/ViewerEventHandlers.cpp @@ -11,6 +11,7 @@ * OpenSceneGraph Public License for more details. */ +#include #include #include @@ -342,4 +343,97 @@ bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIAction return false; } +AnimationPathHandler::AnimationPathHandler(): + _currentlyRecording(false), + _delta(0.0f), + _lastFrameTime(osg::Timer::instance()->tick()), + _animStartTime(0) +{ + _animPath = new osg::AnimationPath(); + + const char* str = getenv("OSG_CAMERA_ANIMATION_FPS"); + + if(str) _interval = 1.0f / atof(str); + + else _interval = 1.0f / 25.0f; +} + +void AnimationPathHandler::getUsage(osg::ApplicationUsage &usage) const +{ + usage.addKeyboardMouseBinding("z", "Toggle camera path recording."); +} + +bool AnimationPathHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) +{ + osgViewer::Viewer* viewer = dynamic_cast(&aa); + + if (viewer == NULL) + { + return false; + } + + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYUP): + { + if (ea.getKey() == 'z') + { + // The user has requested to BEGIN recording. + if (!_currentlyRecording) + { + _currentlyRecording = true; + _animStartTime = osg::Timer::instance()->tick(); + + osg::notify(osg::NOTICE)<<"Recording camera path."<write(out); + out.close(); + } + + return true; + } + + break; + } + case(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; + + break; + } + default: + break; + } + + return false; +} + }