1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12 */
13 
14 #include <stdlib.h>
15 #include <float.h>
16 #include <limits.h>
17 
18 #include <iomanip>
19 #include <sstream>
20 
21 #include <osgDB/FileNameUtils>
22 
23 #include <osg/Version>
24 #include <osg/Geometry>
25 #include <osg/TexMat>
26 #include <osg/Texture2D>
27 #include <osg/TextureRectangle>
28 #include <osg/io_utils>
29 
30 #include <osgViewer/Viewer>
31 #include <osgViewer/ViewerEventHandlers>
32 
33 namespace osgViewer
34 {
35 
36 /*
37 ** WindowSizeHandler
38 */
39 
WindowSizeHandler()40 WindowSizeHandler::WindowSizeHandler() :
41     _keyEventToggleFullscreen('f'),
42     _toggleFullscreen(true),
43     _keyEventWindowedResolutionUp('>'),
44     _keyEventWindowedResolutionDown('<'),
45     _changeWindowedResolution(true),
46     _currentResolutionIndex(-1)
47 {
48     _resolutionList.push_back(osg::Vec2(640, 480));
49     _resolutionList.push_back(osg::Vec2(800, 600));
50     _resolutionList.push_back(osg::Vec2(1024, 768));
51     _resolutionList.push_back(osg::Vec2(1152, 864));
52     _resolutionList.push_back(osg::Vec2(1280, 720));
53     _resolutionList.push_back(osg::Vec2(1280, 768));
54     _resolutionList.push_back(osg::Vec2(1280, 1024));
55     _resolutionList.push_back(osg::Vec2(1440, 900));
56     _resolutionList.push_back(osg::Vec2(1400, 1050));
57     _resolutionList.push_back(osg::Vec2(1600, 900));
58     _resolutionList.push_back(osg::Vec2(1600, 1024));
59     _resolutionList.push_back(osg::Vec2(1600, 1200));
60     _resolutionList.push_back(osg::Vec2(1680, 1050));
61     _resolutionList.push_back(osg::Vec2(1920, 1080));
62     _resolutionList.push_back(osg::Vec2(1920, 1200));
63     _resolutionList.push_back(osg::Vec2(2048, 1536));
64     _resolutionList.push_back(osg::Vec2(2560, 2048));
65     _resolutionList.push_back(osg::Vec2(3200, 2400));
66     _resolutionList.push_back(osg::Vec2(3840, 2400));
67 }
68 
getUsage(osg::ApplicationUsage & usage) const69 void WindowSizeHandler::getUsage(osg::ApplicationUsage &usage) const
70 {
71     usage.addKeyboardMouseBinding(_keyEventToggleFullscreen, "Toggle full screen.");
72     usage.addKeyboardMouseBinding(_keyEventWindowedResolutionUp, "Increase the screen resolution (in windowed mode).");
73     usage.addKeyboardMouseBinding(_keyEventWindowedResolutionDown, "Decrease the screen resolution (in windowed mode).");
74 }
75 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)76 bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
77 {
78     osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
79     if (!view) return false;
80 
81     osgViewer::ViewerBase* viewer = view->getViewerBase();
82 
83     if (viewer == NULL)
84     {
85         return false;
86     }
87 
88     if (ea.getHandled()) return false;
89 
90     switch(ea.getEventType())
91     {
92         case(osgGA::GUIEventAdapter::KEYUP):
93         {
94             if (_toggleFullscreen == true && ea.getKey() == _keyEventToggleFullscreen)
95             {
96 
97                 // sleep to allow any viewer rendering threads to complete before we
98                 // resize the window
99                 OpenThreads::Thread::microSleep(100000);
100 
101                 osgViewer::Viewer::Windows windows;
102                 viewer->getWindows(windows);
103                 for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
104                     itr != windows.end();
105                     ++itr)
106                 {
107                     toggleFullscreen(*itr);
108                 }
109 
110                 aa.requestRedraw();
111                 return true;
112             }
113             else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionUp)
114             {
115                 // sleep to allow any viewer rendering threads to complete before we
116                 // resize the window
117                 OpenThreads::Thread::microSleep(100000);
118 
119                 // Increase resolution
120                 osgViewer::Viewer::Windows    windows;
121                 viewer->getWindows(windows);
122                 for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
123                     itr != windows.end();
124                     ++itr)
125                 {
126                     changeWindowedResolution(*itr, true);
127                 }
128 
129                 aa.requestRedraw();
130                 return true;
131             }
132             else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionDown)
133             {
134                 // sleep to allow any viewer rendering threads to complete before we
135                 // resize the window
136                 OpenThreads::Thread::microSleep(100000);
137 
138                 // Decrease resolution
139                 osgViewer::Viewer::Windows    windows;
140                 viewer->getWindows(windows);
141                 for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
142                     itr != windows.end();
143                     ++itr)
144                 {
145                     changeWindowedResolution(*itr, false);
146                 }
147 
148                 aa.requestRedraw();
149                 return true;
150             }
151             break;
152         }
153     default:
154         break;
155     }
156     return false;
157 }
158 
toggleFullscreen(osgViewer::GraphicsWindow * window)159 void WindowSizeHandler::toggleFullscreen(osgViewer::GraphicsWindow *window)
160 {
161     osg::GraphicsContext::WindowingSystemInterface    *wsi = osg::GraphicsContext::getWindowingSystemInterface();
162 
163     if (wsi == NULL)
164     {
165         OSG_NOTICE << "Error, no WindowSystemInterface available, cannot toggle window fullscreen." << std::endl;
166         return;
167     }
168 
169     unsigned int    screenWidth;
170     unsigned int    screenHeight;
171 
172     wsi->getScreenResolution(*(window->getTraits()), screenWidth, screenHeight);
173 
174     int x;
175     int y;
176     int width;
177     int height;
178 
179     window->getWindowRectangle(x, y, width, height);
180 
181     bool    isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight;
182 
183     if (isFullScreen)
184     {
185         osg::Vec2    resolution;
186 
187         if (_currentResolutionIndex == -1)
188         {
189             _currentResolutionIndex = getNearestResolution(screenWidth, screenHeight, screenWidth / 2, screenHeight / 2);
190         }
191         resolution = _resolutionList[_currentResolutionIndex];
192         window->setWindowDecoration(true);
193         window->setWindowRectangle((screenWidth - (int)resolution.x()) / 2, (screenHeight - (int)resolution.y()) / 2, (int)resolution.x(), (int)resolution.y());
194         OSG_INFO << "Screen resolution = " << (int)resolution.x() << "x" << (int)resolution.y() << std::endl;
195     }
196     else
197     {
198         window->setWindowDecoration(false);
199         window->setWindowRectangle(0, 0, screenWidth, screenHeight);
200     }
201 
202     window->grabFocusIfPointerInWindow();
203 }
204 
changeWindowedResolution(osgViewer::GraphicsWindow * window,bool increase)205 void WindowSizeHandler::changeWindowedResolution(osgViewer::GraphicsWindow *window, bool increase)
206 {
207     osg::GraphicsContext::WindowingSystemInterface    *wsi = osg::GraphicsContext::getWindowingSystemInterface();
208 
209     if (wsi == NULL)
210     {
211         OSG_NOTICE << "Error, no WindowSystemInterface available, cannot toggle window fullscreen." << std::endl;
212         return;
213     }
214 
215     unsigned int    screenWidth;
216     unsigned int    screenHeight;
217 
218     wsi->getScreenResolution(*(window->getTraits()), screenWidth, screenHeight);
219 
220     int x;
221     int y;
222     int width;
223     int height;
224 
225     window->getWindowRectangle(x, y, width, height);
226 
227     bool    isFullScreen = x == 0 && y == 0 && width == (int)screenWidth && height == (int)screenHeight;
228 
229     if (window->getWindowDecoration() == true || isFullScreen == false)
230     {
231         osg::Vec2    resolution;
232 
233         if (_currentResolutionIndex == -1)
234         {
235             _currentResolutionIndex = getNearestResolution(screenWidth, screenHeight, width, height);
236         }
237 
238         if (increase == true)
239         {
240             // Find the next resolution
241             for (int i = _currentResolutionIndex + 1; i < (int)_resolutionList.size(); ++i)
242             {
243                 if ((unsigned int)_resolutionList[i].x() <= screenWidth && (unsigned int)_resolutionList[i].y() <= screenHeight)
244                 {
245                     _currentResolutionIndex = i;
246                     break;
247                 }
248             }
249         }
250         else
251         {
252             // Find the previous resolution
253             for (int i = _currentResolutionIndex - 1; i >= 0; --i)
254             {
255                 if ((unsigned int)_resolutionList[i].x() <= screenWidth && (unsigned int)_resolutionList[i].y() <= screenHeight)
256                 {
257                     _currentResolutionIndex = i;
258                     break;
259                 }
260             }
261         }
262 
263         resolution = _resolutionList[_currentResolutionIndex];
264         window->setWindowDecoration(true);
265         window->setWindowRectangle((screenWidth - (int)resolution.x()) / 2, (screenHeight - (int)resolution.y()) / 2, (int)resolution.x(), (int)resolution.y());
266         OSG_INFO << "Screen resolution = " << (int)resolution.x() << "x" << (int)resolution.y() << std::endl;
267 
268         window->grabFocusIfPointerInWindow();
269     }
270 }
271 
getNearestResolution(int screenWidth,int screenHeight,int width,int height) const272 unsigned int WindowSizeHandler::getNearestResolution(int screenWidth, int screenHeight, int width, int height) const
273 {
274     unsigned int    position = 0;
275     unsigned int    result = 0;
276     int                delta = INT_MAX;
277 
278     for (std::vector<osg::Vec2>::const_iterator it = _resolutionList.begin();
279         it != _resolutionList.end();
280         ++it, ++position)
281     {
282         if ((int)it->x() <= screenWidth && (int)it->y() <= screenHeight)
283         {
284             int tmp = static_cast<int>(osg::absolute((width * height) - (it->x() * it->y())));
285 
286             if (tmp < delta)
287             {
288                 delta = tmp;
289                 result = position;
290             }
291         }
292     }
293     return (result);
294 }
295 
296 /*
297 ** ThreadingHandler
298 */
299 
ThreadingHandler()300 ThreadingHandler::ThreadingHandler() :
301     _keyEventChangeThreadingModel('m'),
302     _changeThreadingModel(true),
303     _keyEventChangeEndBarrierPosition('e'),
304     _changeEndBarrierPosition(true)
305 {
306     _tickOrLastKeyPress = osg::Timer::instance()->tick();
307 }
308 
getUsage(osg::ApplicationUsage & usage) const309 void ThreadingHandler::getUsage(osg::ApplicationUsage &usage) const
310 {
311     usage.addKeyboardMouseBinding(_keyEventChangeThreadingModel, "Toggle threading model.");
312     usage.addKeyboardMouseBinding(_keyEventChangeEndBarrierPosition, "Toggle the placement of the end of frame barrier.");
313 }
314 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)315 bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
316 {
317     osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
318     if (!view) return false;
319 
320     osgViewer::ViewerBase* viewerBase = view->getViewerBase();
321     osgViewer::Viewer* viewer = dynamic_cast<Viewer*>(viewerBase);
322 
323     if (viewerBase == NULL)
324     {
325         return false;
326     }
327 
328     if (ea.getHandled()) return false;
329 
330     switch(ea.getEventType())
331     {
332         case(osgGA::GUIEventAdapter::KEYUP):
333         {
334             double    delta = osg::Timer::instance()->delta_s(_tickOrLastKeyPress, osg::Timer::instance()->tick());
335 
336             if (_changeThreadingModel == true && ea.getKey() == _keyEventChangeThreadingModel && delta > 1.0)
337             {
338 
339                 _tickOrLastKeyPress = osg::Timer::instance()->tick();
340 
341                 switch(viewerBase->getThreadingModel())
342                 {
343                 case(osgViewer::ViewerBase::SingleThreaded):
344                     viewerBase->setThreadingModel(osgViewer::ViewerBase::CullDrawThreadPerContext);
345                     OSG_NOTICE<<"Threading model 'CullDrawThreadPerContext' selected."<<std::endl;
346                     break;
347                 case(osgViewer::ViewerBase::CullDrawThreadPerContext):
348                     viewerBase->setThreadingModel(osgViewer::ViewerBase::DrawThreadPerContext);
349                     OSG_NOTICE<<"Threading model 'DrawThreadPerContext' selected."<<std::endl;
350                     break;
351                 case(osgViewer::ViewerBase::DrawThreadPerContext):
352                     viewerBase->setThreadingModel(osgViewer::ViewerBase::CullThreadPerCameraDrawThreadPerContext);
353                     OSG_NOTICE<<"Threading model 'CullThreadPerCameraDrawThreadPerContext' selected."<<std::endl;
354                     break;
355                 case(osgViewer::ViewerBase::CullThreadPerCameraDrawThreadPerContext):
356                     viewerBase->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
357                     OSG_NOTICE<<"Threading model 'SingleThreaded' selected."<<std::endl;
358                     break;
359 #if 1
360                 case(osgViewer::ViewerBase::AutomaticSelection):
361                     viewerBase->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
362                     OSG_NOTICE<<"Threading model 'SingleThreaded' selected."<<std::endl;
363 #else
364                 case(osgViewer::ViewerBase::AutomaticSelection):
365                     viewerBase->setThreadingModel(viewer->suggestBestThreadingModel());
366                     OSG_NOTICE<<"Threading model 'AutomaticSelection' selected."<<std::endl;
367 #endif
368                     break;
369                 }
370 
371                 aa.requestRedraw();
372                 return true;
373             }
374             if (viewer && _changeEndBarrierPosition == true && ea.getKey() == _keyEventChangeEndBarrierPosition)
375             {
376                 switch(viewer->getEndBarrierPosition())
377                 {
378                 case(osgViewer::Viewer::BeforeSwapBuffers):
379                     viewer->setEndBarrierPosition(osgViewer::Viewer::AfterSwapBuffers);
380                     OSG_NOTICE<<"Threading end of frame barrier position 'AfterSwapBuffers' selected."<<std::endl;
381                     break;
382                 case(osgViewer::Viewer::AfterSwapBuffers):
383                     viewer->setEndBarrierPosition(osgViewer::Viewer::BeforeSwapBuffers);
384                     OSG_NOTICE<<"Threading end of frame barrier position 'BeforeSwapBuffers' selected."<<std::endl;
385                     break;
386                 }
387 
388                 aa.requestRedraw();
389                 return true;
390             }
391             break;
392         }
393     default:
394         break;
395     }
396     return false;
397 }
398 
RecordCameraPathHandler(const std::string & filename,float fps)399 RecordCameraPathHandler::RecordCameraPathHandler(const std::string& filename, float fps):
400     _filename(filename),
401     _autoinc( -1 ),
402     _keyEventToggleRecord('z'),
403     _keyEventTogglePlayback('Z'),
404     _currentlyRecording(false),
405     _currentlyPlaying(false),
406     _delta(0.0f),
407     _animStartTime(0),
408     _lastFrameTime(osg::Timer::instance()->tick())
409 {
410     const char* str = getenv("OSG_RECORD_CAMERA_PATH_FPS");
411     if (str)
412     {
413         _interval = 1.0f / osg::asciiToDouble(str);
414     }
415     else
416     {
417         _interval = 1.0f / fps;
418     }
419 }
420 
getUsage(osg::ApplicationUsage & usage) const421 void RecordCameraPathHandler::getUsage(osg::ApplicationUsage &usage) const
422 {
423     usage.addKeyboardMouseBinding(_keyEventToggleRecord, "Toggle camera path recording.");
424     usage.addKeyboardMouseBinding(_keyEventTogglePlayback, "Toggle camera path playback.");
425 }
426 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)427 bool RecordCameraPathHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
428 {
429     osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
430 
431     if (view == NULL)
432     {
433         return false;
434     }
435 
436     if(ea.getEventType()==osgGA::GUIEventAdapter::FRAME)
437     {
438         // Calculate our current delta (difference) in time between the last frame and
439         // current frame, regardless of whether we actually store a ControlPoint...
440         osg::Timer_t time = osg::Timer::instance()->tick();
441         double delta = osg::Timer::instance()->delta_s(_lastFrameTime, time);
442         _lastFrameTime = time;
443 
444         // If our internal _delta is finally large enough to warrant a ControlPoint
445         // insertion, do so now. Be sure and reset the internal _delta, so we can start
446         // calculating when the next insert should happen.
447         if (_animPath.valid() && _currentlyRecording && _delta >= _interval)
448         {
449             const osg::Matrixd& m = view->getCamera()->getInverseViewMatrix();
450             double animationPathTime = osg::Timer::instance()->delta_s(_animStartTime, time);
451             _animPath->insert(animationPathTime, osg::AnimationPath::ControlPoint(m.getTrans(), m.getRotate()));
452             _delta = 0.0f;
453 
454             if (_fout)
455             {
456                 _animPath->write(_animPath->getTimeControlPointMap().find(animationPathTime), _fout);
457                 _fout.flush();
458             }
459 
460         }
461         else _delta += delta;
462 
463         return true;
464     }
465 
466     if (ea.getHandled()) return false;
467 
468     switch(ea.getEventType())
469     {
470         case(osgGA::GUIEventAdapter::KEYUP):
471         {
472             // The user has requested to toggle recording.
473             if (ea.getKey() ==_keyEventToggleRecord)
474             {
475                 // The user has requested to BEGIN recording.
476                 if (!_currentlyRecording)
477                 {
478                     _currentlyRecording = true;
479                     _animStartTime = osg::Timer::instance()->tick();
480                     _animPath = new osg::AnimationPath();
481 
482                     if (!_filename.empty())
483                     {
484                         std::stringstream ss;
485                         ss << osgDB::getNameLessExtension(_filename);
486                         if ( _autoinc != -1 )
487                         {
488                             ss << "_"<<std::setfill( '0' ) << std::setw( 2 ) << _autoinc;
489                             _autoinc++;
490                         }
491                         ss << "."<<osgDB::getFileExtension(_filename);
492 
493                         OSG_NOTICE << "Recording camera path to file " << ss.str() << std::endl;
494                         _fout.open( ss.str().c_str() );
495 
496                         // make sure doubles are not trucated by default stream precision = 6
497                         _fout.precision( 15 );
498                     }
499                     else
500                     {
501                         OSG_NOTICE<<"Recording camera path."<<std::endl;
502                     }
503                 }
504 
505                 // The user has requested to STOP recording, write the file!
506                 else
507                 {
508                     _currentlyRecording = false;
509                     _delta = 0.0f;
510 
511                     if (_fout) _fout.close();
512                 }
513 
514                 return true;
515             }
516 
517             // The user has requested to toggle playback. You'll notice in the code below that
518             // we take over the current manipulator; it was originally recommended that we
519             // check for a KeySwitchManipulator, create one if not present, and then add this
520             // to either the newly created one or the existing one. However, the code do that was
521             // EXTREMELY dirty, so I opted for a simpler solution. At a later date, someone may
522             // want to implement the original recommendation (which is in a mailing list reply
523             // from June 1st by Robert in a thread called "osgviewer Camera Animation (preliminary)".
524             else if (ea.getKey() == _keyEventTogglePlayback)
525             {
526                 if (_currentlyRecording)
527                 {
528                     _currentlyRecording = false;
529                     _delta = 0.0f;
530 
531                     if (_animPath.valid() && !_animPath->empty())
532                     {
533                         // In the future this will need to be written continuously, rather
534                         // than all at once.
535                         osgDB::ofstream out(_filename.c_str());
536                         OSG_NOTICE<<"Writing camera file: "<<_filename<<std::endl;
537                         _animPath->write(out);
538                         out.close();
539                     }
540                     else
541                     {
542                         OSG_NOTICE<<"No animation path to write out."<<std::endl;
543                     }
544                 }
545 
546                 // The user has requested to BEGIN playback.
547                 if (!_currentlyPlaying)
548                 {
549                      if (_animPath.valid() && !_animPath->empty())
550                      {
551                         _animPathManipulator = new osgGA::AnimationPathManipulator(_animPath.get());
552                         _animPathManipulator->home(ea,aa);
553 
554 
555                         // If we successfully found our _filename file, set it and keep a copy
556                         // around of the original CameraManipulator to restore later.
557                         if (_animPathManipulator.valid() && _animPathManipulator->valid())
558                         {
559                             _oldManipulator = view->getCameraManipulator();
560                             view->setCameraManipulator(_animPathManipulator.get());
561                             _currentlyPlaying = true;
562                         }
563                      }
564                 }
565 
566                 // The user has requested to STOP playback.
567                 else
568                 {
569                     // Restore the old manipulator if necessary and stop playback.
570                     if(_oldManipulator.valid()) view->setCameraManipulator(_oldManipulator.get());
571                     _currentlyPlaying = false;
572                     _oldManipulator = 0;
573                 }
574 
575                 return true;
576             }
577 
578             break;
579         }
580     default:
581         break;
582     }
583 
584     return false;
585 }
586 
LODScaleHandler()587 LODScaleHandler::LODScaleHandler():
588     _keyEventIncreaseLODScale('*'),
589     _keyEventDecreaseLODScale('/')
590 {
591 }
592 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)593 bool LODScaleHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
594 {
595     osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
596     osg::Camera* camera = view ? view->getCamera() : 0;
597     if (!camera) return false;
598 
599     if (ea.getHandled()) return false;
600 
601     switch(ea.getEventType())
602     {
603         case(osgGA::GUIEventAdapter::KEYUP):
604         {
605             if (ea.getKey() == _keyEventIncreaseLODScale)
606             {
607                 camera->setLODScale(camera->getLODScale()*1.1);
608                 OSG_NOTICE<<"LODScale = "<<camera->getLODScale()<<std::endl;
609 
610                 aa.requestRedraw();
611                 return true;
612             }
613 
614             else if (ea.getKey() == _keyEventDecreaseLODScale)
615             {
616                 camera->setLODScale(camera->getLODScale()/1.1);
617                 OSG_NOTICE<<"LODScale = "<<camera->getLODScale()<<std::endl;
618 
619                 aa.requestRedraw();
620                 return true;
621             }
622 
623             break;
624         }
625     default:
626         break;
627     }
628 
629     return false;
630 }
631 
getUsage(osg::ApplicationUsage & usage) const632 void LODScaleHandler::getUsage(osg::ApplicationUsage& usage) const
633 {
634     usage.addKeyboardMouseBinding(_keyEventIncreaseLODScale,"Increase LODScale.");
635     usage.addKeyboardMouseBinding(_keyEventDecreaseLODScale,"Decrease LODScale.");
636 }
637 
ToggleSyncToVBlankHandler()638 ToggleSyncToVBlankHandler::ToggleSyncToVBlankHandler():
639     _keyEventToggleSyncToVBlank('v')
640 {
641 }
642 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)643 bool ToggleSyncToVBlankHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
644 {
645     osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
646     if (!view) return false;
647 
648     osgViewer::ViewerBase* viewer = view->getViewerBase();
649 
650     if (viewer == NULL)
651     {
652         return false;
653     }
654 
655     if (ea.getHandled()) return false;
656 
657     switch(ea.getEventType())
658     {
659         case(osgGA::GUIEventAdapter::KEYUP):
660         {
661             if (ea.getKey() == _keyEventToggleSyncToVBlank)
662             {
663                 // Increase resolution
664                 osgViewer::Viewer::Windows    windows;
665 
666                 viewer->getWindows(windows);
667                 for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
668                     itr != windows.end();
669                     ++itr)
670                 {
671                     (*itr)->setSyncToVBlank( !(*itr)->getSyncToVBlank() );
672                 }
673 
674                 aa.requestRedraw();
675                 return true;
676             }
677 
678             break;
679         }
680     default:
681         break;
682     }
683 
684     return false;
685 }
686 
687 
getUsage(osg::ApplicationUsage & usage) const688 void ToggleSyncToVBlankHandler::getUsage(osg::ApplicationUsage& usage) const
689 {
690     usage.addKeyboardMouseBinding(_keyEventToggleSyncToVBlank,"Toggle SyncToVBlank.");
691 }
692 
693 
InteractiveImageHandler(osg::Image * image)694 InteractiveImageHandler::InteractiveImageHandler(osg::Image* image) :
695     _image(image),
696     _texture(0),
697     _fullscreen(false),
698     _camera(0)
699 {
700 }
701 
InteractiveImageHandler(osg::Image * image,osg::Texture2D * texture,osg::Camera * camera)702 InteractiveImageHandler::InteractiveImageHandler(osg::Image* image, osg::Texture2D* texture, osg::Camera* camera) :
703     _image(image),
704     _texture(texture),
705     _fullscreen(true),
706     _camera(camera)
707 {
708     if (_camera.valid() && _camera->getViewport())
709     {
710         // Send an initial resize event (with the same size) so the image can
711         // resize itself initially.
712         double width = _camera->getViewport()->width();
713         double height = _camera->getViewport()->height();
714 
715         resize(static_cast<int>(width), static_cast<int>(height));
716     }
717 }
718 
mousePosition(osgViewer::View * view,osg::NodeVisitor * nv,const osgGA::GUIEventAdapter & ea,int & x,int & y) const719 bool InteractiveImageHandler::mousePosition(osgViewer::View* view, osg::NodeVisitor* nv, const osgGA::GUIEventAdapter& ea, int& x, int &y) const
720 {
721     if (!view) return false;
722     if (_fullscreen)
723     {
724         x = (int) ea.getX();
725         y = (int) ea.getY();
726         return true;
727     }
728 
729     osgUtil::LineSegmentIntersector::Intersections intersections;
730     bool foundIntersection = (nv==0) ? view->computeIntersections(ea, intersections) :
731                                        view->computeIntersections(ea, nv->getNodePath(), intersections);
732 
733     if (foundIntersection)
734     {
735         osg::Vec2 tc(0.5f,0.5f);
736 
737         // use the nearest intersection
738         const osgUtil::LineSegmentIntersector::Intersection& intersection = *(intersections.begin());
739         osg::Drawable* drawable = intersection.drawable.get();
740         osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
741         osg::Vec3Array* vertices = geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
742         if (vertices)
743         {
744             // get the vertex indices.
745             const osgUtil::LineSegmentIntersector::Intersection::IndexList& indices = intersection.indexList;
746             const osgUtil::LineSegmentIntersector::Intersection::RatioList& ratios = intersection.ratioList;
747 
748             if (indices.size()==3 && ratios.size()==3)
749             {
750                 unsigned int i1 = indices[0];
751                 unsigned int i2 = indices[1];
752                 unsigned int i3 = indices[2];
753 
754                 float r1 = ratios[0];
755                 float r2 = ratios[1];
756                 float r3 = ratios[2];
757 
758                 osg::Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
759                 osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
760                 if (texcoords_Vec2Array)
761                 {
762                     // we have tex coord array so now we can compute the final tex coord at the point of intersection.
763                     osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
764                     osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
765                     osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
766                     tc = tc1*r1 + tc2*r2 + tc3*r3;
767                 }
768             }
769 
770             osg::TexMat* activeTexMat = 0;
771             osg::Texture* activeTexture = 0;
772 
773             if (drawable->getStateSet())
774             {
775                 osg::TexMat* texMat = dynamic_cast<osg::TexMat*>(drawable->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXMAT));
776                 if (texMat) activeTexMat = texMat;
777 
778                 osg::Texture* texture = dynamic_cast<osg::Texture*>(drawable->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXTURE));
779                 if (texture) activeTexture = texture;
780             }
781 
782             if (activeTexMat)
783             {
784                 osg::Vec4 tc_transformed = osg::Vec4(tc.x(), tc.y(), 0.0f,0.0f) * activeTexMat->getMatrix();
785                 tc.x() = tc_transformed.x();
786                 tc.y() = tc_transformed.y();
787             }
788 
789             if (dynamic_cast<osg::TextureRectangle*>(activeTexture))
790             {
791                 x = int( tc.x() );
792                 y = int( tc.y() );
793             }
794             else if (_image.valid())
795             {
796                 x = int( float(_image->s()) * tc.x() );
797                 y = int( float(_image->t()) * tc.y() );
798             }
799 
800             return true;
801         }
802 
803     }
804 
805     return false;
806 }
807 
808 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa,osg::Object *,osg::NodeVisitor * nv)809 bool InteractiveImageHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv)
810 {
811     if (ea.getHandled()) return false;
812 
813     if (!_image) return false;
814 
815     switch(ea.getEventType())
816     {
817         case(osgGA::GUIEventAdapter::MOVE):
818         case(osgGA::GUIEventAdapter::DRAG):
819         case(osgGA::GUIEventAdapter::PUSH):
820         case(osgGA::GUIEventAdapter::RELEASE):
821         {
822             osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
823             int x,y;
824             if (mousePosition(view, nv, ea, x, y))
825             {
826                 return _image->sendPointerEvent(x, y, ea.getButtonMask());
827             }
828             break;
829         }
830         case(osgGA::GUIEventAdapter::KEYDOWN):
831         case(osgGA::GUIEventAdapter::KEYUP):
832         {
833             osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
834             int x,y;
835             bool sendKeyEvent = mousePosition(view, nv, ea, x, y);
836 
837             if (sendKeyEvent)
838             {
839                 return _image->sendKeyEvent(ea.getKey(), ea.getEventType()==osgGA::GUIEventAdapter::KEYDOWN);
840             }
841             break;
842         }
843         case (osgGA::GUIEventAdapter::RESIZE):
844         {
845             if (_fullscreen && _camera.valid())
846             {
847                 _camera->setViewport(0, 0, ea.getWindowWidth(), ea.getWindowHeight());
848 
849                 resize(ea.getWindowWidth(), ea.getWindowHeight());
850                 return true;
851             }
852             break;
853         }
854 
855         default:
856             return false;
857     }
858     return false;
859 }
860 
cull(osg::NodeVisitor * nv,osg::Drawable *,osg::RenderInfo *) const861 bool InteractiveImageHandler::cull(osg::NodeVisitor* nv, osg::Drawable*, osg::RenderInfo*) const
862 {
863     if (_image.valid())
864     {
865         _image->setFrameLastRendered(nv->getFrameStamp());
866     }
867 
868     return false;
869 }
870 
resize(int width,int height)871 void InteractiveImageHandler::resize(int width, int height)
872 {
873     if (_image.valid())
874     {
875         _image->scaleImage(width, height, 1);
876     }
877 
878     // Make sure the texture does not rescale the image because
879     // it thinks it should still be the previous size...
880     if (_texture.valid())
881         _texture->setTextureSize(width, height);
882 }
883 
884 }
885