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