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 <osgDB/WriteFile>
15
16 #include <osgViewer/Viewer>
17 #include <osgViewer/ViewerEventHandlers>
18
19 #include <string.h>
20
21 namespace osgViewer
22 {
23
24 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
25 //
26 // WindowCaptureCallback
27 //
28
29 // From osgscreencapture example
30 /** Callback which will be added to a viewer's camera to do the actual screen capture. */
31 class WindowCaptureCallback : public osg::Camera::DrawCallback
32 {
33 public:
34
35 enum Mode
36 {
37 READ_PIXELS,
38 SINGLE_PBO,
39 DOUBLE_PBO,
40 TRIPLE_PBO
41 };
42
43 enum FramePosition
44 {
45 START_FRAME,
46 END_FRAME
47 };
48
49 WindowCaptureCallback(int numFrames, Mode mode, FramePosition position, GLenum readBuffer);
50
getFramePosition() const51 FramePosition getFramePosition() const { return _position; }
52
53 void setCaptureOperation(ScreenCaptureHandler::CaptureOperation* operation);
getCaptureOperation()54 ScreenCaptureHandler::CaptureOperation* getCaptureOperation() { return _contextDataMap.begin()->second->_captureOperation.get(); }
55
setFramesToCapture(int numFrames)56 void setFramesToCapture(int numFrames) { _numFrames = numFrames; }
getFramesToCapture() const57 int getFramesToCapture() const { return _numFrames; }
58
59 virtual void operator () (osg::RenderInfo& renderInfo) const;
60
61 struct OSGVIEWER_EXPORT ContextData : public osg::Referenced
62 {
63 ContextData(osg::GraphicsContext* gc, Mode mode, GLenum readBuffer);
64
65 void getSize(osg::GraphicsContext* gc, int& width, int& height);
66
67 void updateTimings(osg::Timer_t tick_start,
68 osg::Timer_t tick_afterReadPixels,
69 osg::Timer_t tick_afterMemCpy,
70 osg::Timer_t tick_afterCaptureOperation,
71 unsigned int dataSize);
72
73 void read();
74 void readPixels();
75 void singlePBO(osg::GLExtensions* ext);
76 void multiPBO(osg::GLExtensions* ext);
77
78 typedef std::vector< osg::ref_ptr<osg::Image> > ImageBuffer;
79 typedef std::vector< GLuint > PBOBuffer;
80
81 osg::GraphicsContext* _gc;
82 unsigned int _index;
83 Mode _mode;
84 GLenum _readBuffer;
85
86 GLenum _pixelFormat;
87 GLenum _type;
88 int _width;
89 int _height;
90
91 unsigned int _currentImageIndex;
92 ImageBuffer _imageBuffer;
93
94 unsigned int _currentPboIndex;
95 PBOBuffer _pboBuffer;
96
97 unsigned int _reportTimingFrequency;
98 unsigned int _numTimeValuesRecorded;
99 double _timeForReadPixels;
100 double _timeForMemCpy;
101 double _timeForCaptureOperation;
102 double _timeForFullCopy;
103 double _timeForFullCopyAndOperation;
104 osg::Timer_t _previousFrameTick;
105
106 osg::ref_ptr<ScreenCaptureHandler::CaptureOperation> _captureOperation;
107 };
108
109 typedef std::map<osg::GraphicsContext*, osg::ref_ptr<ContextData> > ContextDataMap;
110
111 ContextData* createContextData(osg::GraphicsContext* gc) const;
112 ContextData* getContextData(osg::GraphicsContext* gc) const;
113
114 Mode _mode;
115 FramePosition _position;
116 GLenum _readBuffer;
117 mutable OpenThreads::Mutex _mutex;
118 mutable ContextDataMap _contextDataMap;
119 mutable int _numFrames;
120
121 osg::ref_ptr<ScreenCaptureHandler::CaptureOperation> _defaultCaptureOperation;
122 };
123
124
ContextData(osg::GraphicsContext * gc,Mode mode,GLenum readBuffer)125 WindowCaptureCallback::ContextData::ContextData(osg::GraphicsContext* gc, Mode mode, GLenum readBuffer)
126 : _gc(gc),
127 _index(_gc->getState()->getContextID()),
128 _mode(mode),
129 _readBuffer(readBuffer),
130 _pixelFormat(GL_RGBA),
131 _type(GL_UNSIGNED_BYTE),
132 _width(0),
133 _height(0),
134 _currentImageIndex(0),
135 _currentPboIndex(0),
136 _reportTimingFrequency(100),
137 _numTimeValuesRecorded(0),
138 _timeForReadPixels(0.0),
139 _timeForMemCpy(0.0),
140 _timeForCaptureOperation(0.0),
141 _timeForFullCopy(0.0),
142 _timeForFullCopyAndOperation(0.0),
143 _previousFrameTick(0)
144 {
145 _previousFrameTick = osg::Timer::instance()->tick();
146
147 osg::NotifySeverity level = osg::INFO;
148
149 if (gc->getTraits())
150 {
151 if (gc->getTraits()->alpha)
152 {
153 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Selected GL_RGBA read back format"<<std::endl;
154 _pixelFormat = GL_RGBA;
155 }
156 else
157 {
158 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Selected GL_RGB read back format"<<std::endl;
159 _pixelFormat = GL_RGB;
160 }
161 }
162
163 getSize(gc, _width, _height);
164
165 //OSG_NOTICE<<"Window size "<<_width<<", "<<_height<<std::endl;
166
167 // single buffered image
168 _imageBuffer.push_back(new osg::Image);
169
170 // double buffer PBO.
171 switch(_mode)
172 {
173 case(READ_PIXELS):
174 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, without PixelBufferObject."<<std::endl;
175 break;
176 case(SINGLE_PBO):
177 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a single PixelBufferObject."<<std::endl;
178 _pboBuffer.push_back(0);
179 break;
180 case(DOUBLE_PBO):
181 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a double buffer PixelBufferObject."<<std::endl;
182 _pboBuffer.push_back(0);
183 _pboBuffer.push_back(0);
184 break;
185 case(TRIPLE_PBO):
186 OSG_NOTIFY(level)<<"ScreenCaptureHandler: Reading window using glReadPixels, with a triple buffer PixelBufferObject."<<std::endl;
187 _pboBuffer.push_back(0);
188 _pboBuffer.push_back(0);
189 _pboBuffer.push_back(0);
190 break;
191 default:
192 break;
193 }
194 }
195
getSize(osg::GraphicsContext * gc,int & width,int & height)196 void WindowCaptureCallback::ContextData::getSize(osg::GraphicsContext* gc, int& width, int& height)
197 {
198 if (gc->getTraits())
199 {
200 width = gc->getTraits()->width;
201 height = gc->getTraits()->height;
202 }
203 }
204
updateTimings(osg::Timer_t tick_start,osg::Timer_t tick_afterReadPixels,osg::Timer_t tick_afterMemCpy,osg::Timer_t tick_afterCaptureOperation,unsigned int)205 void WindowCaptureCallback::ContextData::updateTimings(osg::Timer_t tick_start,
206 osg::Timer_t tick_afterReadPixels,
207 osg::Timer_t tick_afterMemCpy,
208 osg::Timer_t tick_afterCaptureOperation,
209 unsigned int /*dataSize*/)
210 {
211 _timeForReadPixels = osg::Timer::instance()->delta_s(tick_start, tick_afterReadPixels);
212 _timeForMemCpy = osg::Timer::instance()->delta_s(tick_afterReadPixels, tick_afterMemCpy);
213 _timeForCaptureOperation = osg::Timer::instance()->delta_s(tick_afterMemCpy, tick_afterCaptureOperation);
214
215 _timeForFullCopy = osg::Timer::instance()->delta_s(tick_start, tick_afterMemCpy);
216 _timeForFullCopyAndOperation = osg::Timer::instance()->delta_s(tick_start, tick_afterCaptureOperation);
217 }
218
read()219 void WindowCaptureCallback::ContextData::read()
220 {
221 osg::GLExtensions* ext = osg::GLExtensions::Get(_gc->getState()->getContextID(),true);
222
223 if (ext->isPBOSupported && !_pboBuffer.empty())
224 {
225 if (_pboBuffer.size()==1)
226 {
227 singlePBO(ext);
228 }
229 else
230 {
231 multiPBO(ext);
232 }
233 }
234 else
235 {
236 readPixels();
237 }
238 }
239
240
readPixels()241 void WindowCaptureCallback::ContextData::readPixels()
242 {
243 unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
244 unsigned int nextPboIndex = _pboBuffer.empty() ? 0 : (_currentPboIndex+1)%_pboBuffer.size();
245
246 int width=0, height=0;
247 getSize(_gc, width, height);
248 if (width!=_width || _height!=height)
249 {
250 //OSG_NOTICE<<" Window resized "<<width<<", "<<height<<std::endl;
251 _width = width;
252 _height = height;
253 }
254
255 osg::Image* image = _imageBuffer[_currentImageIndex].get();
256
257 osg::Timer_t tick_start = osg::Timer::instance()->tick();
258
259 #if 1
260 image->readPixels(0,0,_width,_height,
261 _pixelFormat,_type);
262 #endif
263
264 osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
265
266 if (_captureOperation.valid())
267 {
268 (*_captureOperation)(*image, _index);
269 }
270
271 osg::Timer_t tick_afterCaptureOperation = osg::Timer::instance()->tick();
272 updateTimings(tick_start, tick_afterReadPixels, tick_afterReadPixels, tick_afterCaptureOperation, image->getTotalSizeInBytes());
273
274 _currentImageIndex = nextImageIndex;
275 _currentPboIndex = nextPboIndex;
276 }
277
singlePBO(osg::GLExtensions * ext)278 void WindowCaptureCallback::ContextData::singlePBO(osg::GLExtensions* ext)
279 {
280 unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
281
282 int width=0, height=0;
283 getSize(_gc, width, height);
284 if (width!=_width || _height!=height)
285 {
286 //OSG_NOTICE<<" Window resized "<<width<<", "<<height<<std::endl;
287 _width = width;
288 _height = height;
289 }
290
291 GLuint& pbo = _pboBuffer[0];
292
293 osg::Image* image = _imageBuffer[_currentImageIndex].get();
294 if (image->s() != _width ||
295 image->t() != _height)
296 {
297 //OSG_NOTICE<<"ScreenCaptureHandler: Allocating image "<<std::endl;
298 image->allocateImage(_width, _height, 1, _pixelFormat, _type);
299
300 if (pbo!=0)
301 {
302 //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<pbo<<std::endl;
303 ext->glDeleteBuffers (1, &pbo);
304 pbo = 0;
305 }
306 }
307
308
309 if (pbo==0)
310 {
311 ext->glGenBuffers(1, &pbo);
312 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
313 ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
314
315 //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<pbo<<std::endl;
316 }
317 else
318 {
319 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
320 }
321
322 osg::Timer_t tick_start = osg::Timer::instance()->tick();
323
324 #if 1
325 glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
326 #endif
327
328 osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
329
330 GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
331 GL_READ_ONLY_ARB);
332 if(src)
333 {
334 memcpy(image->data(), src, image->getTotalSizeInBytes());
335 ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
336 }
337
338 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
339
340 osg::Timer_t tick_afterMemCpy = osg::Timer::instance()->tick();
341
342 if (_captureOperation.valid())
343 {
344 (*_captureOperation)(*image, _index);
345 }
346
347 osg::Timer_t tick_afterCaptureOperation = osg::Timer::instance()->tick();
348 updateTimings(tick_start, tick_afterReadPixels, tick_afterMemCpy, tick_afterCaptureOperation, image->getTotalSizeInBytes());
349
350 _currentImageIndex = nextImageIndex;
351 }
352
multiPBO(osg::GLExtensions * ext)353 void WindowCaptureCallback::ContextData::multiPBO(osg::GLExtensions* ext)
354 {
355 unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
356 unsigned int nextPboIndex = (_currentPboIndex+1)%_pboBuffer.size();
357
358 int width=0, height=0;
359 getSize(_gc, width, height);
360 if (width!=_width || _height!=height)
361 {
362 //OSG_NOTICE<<" Window resized "<<width<<", "<<height<<std::endl;
363 _width = width;
364 _height = height;
365 }
366
367 GLuint& copy_pbo = _pboBuffer[_currentPboIndex];
368 GLuint& read_pbo = _pboBuffer[nextPboIndex];
369
370 osg::Image* image = _imageBuffer[_currentImageIndex].get();
371 if (image->s() != _width ||
372 image->t() != _height)
373 {
374 //OSG_NOTICE<<"ScreenCaptureHandler: Allocating image "<<std::endl;
375 image->allocateImage(_width, _height, 1, _pixelFormat, _type);
376
377 if (read_pbo!=0)
378 {
379 //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<read_pbo<<std::endl;
380 ext->glDeleteBuffers (1, &read_pbo);
381 read_pbo = 0;
382 }
383
384 if (copy_pbo!=0)
385 {
386 //OSG_NOTICE<<"ScreenCaptureHandler: deleting pbo "<<copy_pbo<<std::endl;
387 ext->glDeleteBuffers (1, ©_pbo);
388 copy_pbo = 0;
389 }
390 }
391
392
393 bool doCopy = copy_pbo!=0;
394 if (copy_pbo==0)
395 {
396 ext->glGenBuffers(1, ©_pbo);
397 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
398 ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
399
400 //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<read_pbo<<std::endl;
401 }
402
403 if (read_pbo==0)
404 {
405 ext->glGenBuffers(1, &read_pbo);
406 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
407 ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
408
409 //OSG_NOTICE<<"ScreenCaptureHandler: Generating pbo "<<read_pbo<<std::endl;
410 }
411 else
412 {
413 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
414 }
415
416 osg::Timer_t tick_start = osg::Timer::instance()->tick();
417
418 #if 1
419 glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
420 #endif
421
422 osg::Timer_t tick_afterReadPixels = osg::Timer::instance()->tick();
423
424 if (doCopy)
425 {
426
427 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
428
429 GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
430 GL_READ_ONLY_ARB);
431 if(src)
432 {
433 memcpy(image->data(), src, image->getTotalSizeInBytes());
434 ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
435 }
436
437 if (_captureOperation.valid())
438 {
439 (*_captureOperation)(*image, _index);
440 }
441 }
442
443 ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
444
445 osg::Timer_t tick_afterMemCpy = osg::Timer::instance()->tick();
446
447 updateTimings(tick_start, tick_afterReadPixels, tick_afterMemCpy, tick_afterMemCpy, image->getTotalSizeInBytes());
448
449 _currentImageIndex = nextImageIndex;
450 _currentPboIndex = nextPboIndex;
451 }
452
WindowCaptureCallback(int numFrames,Mode mode,FramePosition position,GLenum readBuffer)453 WindowCaptureCallback::WindowCaptureCallback(int numFrames, Mode mode, FramePosition position, GLenum readBuffer)
454 : _mode(mode),
455 _position(position),
456 _readBuffer(readBuffer),
457 _numFrames(numFrames)
458 {
459 }
460
createContextData(osg::GraphicsContext * gc) const461 WindowCaptureCallback::ContextData* WindowCaptureCallback::createContextData(osg::GraphicsContext* gc) const
462 {
463 WindowCaptureCallback::ContextData* cd = new WindowCaptureCallback::ContextData(gc, _mode, _readBuffer);
464 cd->_captureOperation = _defaultCaptureOperation;
465 return cd;
466 }
467
getContextData(osg::GraphicsContext * gc) const468 WindowCaptureCallback::ContextData* WindowCaptureCallback::getContextData(osg::GraphicsContext* gc) const
469 {
470 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
471 osg::ref_ptr<ContextData>& data = _contextDataMap[gc];
472 if (!data) data = createContextData(gc);
473
474 return data.get();
475 }
476
setCaptureOperation(ScreenCaptureHandler::CaptureOperation * operation)477 void WindowCaptureCallback::setCaptureOperation(ScreenCaptureHandler::CaptureOperation* operation)
478 {
479 _defaultCaptureOperation = operation;
480
481 // Set the capture operation for each ContextData.
482 for (ContextDataMap::iterator it = _contextDataMap.begin(); it != _contextDataMap.end(); ++it)
483 {
484 it->second->_captureOperation = operation;
485 }
486 }
487
488
operator ()(osg::RenderInfo & renderInfo) const489 void WindowCaptureCallback::operator () (osg::RenderInfo& renderInfo) const
490 {
491 #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
492 glReadBuffer(_readBuffer);
493 #endif
494
495 osg::GraphicsContext* gc = renderInfo.getState()->getGraphicsContext();
496 osg::ref_ptr<ContextData> cd = getContextData(gc);
497 cd->read();
498
499 // If _numFrames is > 0 it means capture that number of frames.
500 if (_numFrames > 0)
501 {
502 --_numFrames;
503 if (_numFrames == 0)
504 {
505 // the callback must remove itself when it's done.
506 if (_position == START_FRAME)
507 renderInfo.getCurrentCamera()->setInitialDrawCallback(0);
508 if (_position == END_FRAME)
509 renderInfo.getCurrentCamera()->setFinalDrawCallback(0);
510 }
511 }
512
513 int prec = osg::notify(osg::INFO).precision(5);
514 OSG_INFO << "ScreenCaptureHandler: "
515 << "copy=" << (cd->_timeForFullCopy*1000.0f) << "ms, "
516 << "operation=" << (cd->_timeForCaptureOperation*1000.0f) << "ms, "
517 << "total=" << (cd->_timeForFullCopyAndOperation*1000.0f) << std::endl;
518 osg::notify(osg::INFO).precision(prec);
519
520 cd->_timeForFullCopy = 0;
521 }
522
523
524
525 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
526 //
527 // ScreenCaptureHandler::WriteToFile
528 //
WriteToFile(const std::string & filename,const std::string & extension,SavePolicy savePolicy)529 ScreenCaptureHandler::WriteToFile::WriteToFile(const std::string& filename,
530 const std::string& extension,
531 SavePolicy savePolicy)
532 : _filename(filename), _extension(extension), _savePolicy(savePolicy)
533 {
534 }
535
operator ()(const osg::Image & image,const unsigned int context_id)536 void ScreenCaptureHandler::WriteToFile::operator () (const osg::Image& image, const unsigned int context_id)
537 {
538 if (_savePolicy == SEQUENTIAL_NUMBER)
539 {
540 if (_contextSaveCounter.size() <= context_id)
541 {
542 unsigned int oldSize = _contextSaveCounter.size();
543 _contextSaveCounter.resize(context_id + 1);
544 // Initialize all new values to 0 since context ids may not be consecutive.
545 for (unsigned int i = oldSize; i <= context_id; i++)
546 _contextSaveCounter[i] = 0;
547 }
548 }
549
550 std::stringstream filename;
551 filename << _filename << "_" << context_id;
552
553 if (_savePolicy == SEQUENTIAL_NUMBER)
554 filename << "_" << _contextSaveCounter[context_id];
555
556 filename << "." << _extension;
557
558 osgDB::writeImageFile(image, filename.str());
559
560 OSG_INFO<<"ScreenCaptureHandler: Taking a screenshot, saved as '"<<filename.str()<<"'"<<std::endl;
561
562 if (_savePolicy == SEQUENTIAL_NUMBER)
563 {
564 _contextSaveCounter[context_id]++;
565 }
566 }
567
568
569 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
570 //
571 // ScreenCaptureHandler
572 //
ScreenCaptureHandler(CaptureOperation * defaultOperation,int numFrames)573 ScreenCaptureHandler::ScreenCaptureHandler(CaptureOperation* defaultOperation,
574 int numFrames)
575 : _startCapture(false),
576 _stopCapture(false),
577 _keyEventTakeScreenShot('c'),
578 _keyEventToggleContinuousCapture('C'),
579 _callback(new WindowCaptureCallback( numFrames,
580 WindowCaptureCallback::READ_PIXELS,
581 // WindowCaptureCallback::SINGLE_PBO,
582 // WindowCaptureCallback::DOUBLE_PBO,
583 // WindowCaptureCallback::TRIPLE_PBO,
584 WindowCaptureCallback::END_FRAME, GL_BACK))
585 {
586 if (defaultOperation)
587 setCaptureOperation(defaultOperation);
588 else
589 setCaptureOperation(new WriteToFile("screen_shot", "jpg"));
590 }
591
setCaptureOperation(CaptureOperation * operation)592 void ScreenCaptureHandler::setCaptureOperation(CaptureOperation* operation)
593 {
594 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
595 callback->setCaptureOperation(operation);
596 }
597
getCaptureOperation() const598 ScreenCaptureHandler::CaptureOperation* ScreenCaptureHandler::getCaptureOperation() const
599 {
600 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
601 return callback->getCaptureOperation();
602 }
603
addCallbackToViewer(osgViewer::ViewerBase & viewer)604 void ScreenCaptureHandler::addCallbackToViewer(osgViewer::ViewerBase& viewer)
605 {
606 osg::Camera* camera = findAppropriateCameraForCallback(viewer);
607 if (!camera) return;
608
609 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
610 if (callback && callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
611 {
612 camera->setInitialDrawCallback(_callback.get());
613 }
614 else
615 {
616 camera->setFinalDrawCallback(_callback.get());
617 }
618 }
619
removeCallbackFromViewer(osgViewer::ViewerBase & viewer)620 void ScreenCaptureHandler::removeCallbackFromViewer(osgViewer::ViewerBase& viewer)
621 {
622 osg::Camera* camera = findAppropriateCameraForCallback(viewer);
623 if (!camera) return;
624
625 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
626 if (callback && callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
627 {
628 camera->setInitialDrawCallback(0);
629 }
630 else
631 {
632 camera->setFinalDrawCallback(0);
633 }
634 }
635
findAppropriateCameraForCallback(osgViewer::ViewerBase & viewer)636 osg::Camera* ScreenCaptureHandler::findAppropriateCameraForCallback(osgViewer::ViewerBase& viewer)
637 {
638 // Select either the first or the last active camera, depending on the
639 // frame position set in the callback.
640 // One case where testing the node mask is important is when the stats
641 // handler has been initialized, but stats are not displayed. In that
642 // case, there is a post render camera on the viewer, but its node mask
643 // is zero, so the callback added to that camera would never be called.
644 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
645
646 if (callback->getFramePosition() == WindowCaptureCallback::START_FRAME)
647 {
648 osgViewer::ViewerBase::Contexts contexts;
649 viewer.getContexts(contexts);
650 for(osgViewer::ViewerBase::Contexts::iterator itr = contexts.begin();
651 itr != contexts.end();
652 ++itr)
653 {
654 osg::GraphicsContext* context = *itr;
655 osg::GraphicsContext::Cameras& cameras = context->getCameras();
656 osg::Camera* firstCamera = 0;
657 for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
658 cam_itr != cameras.end();
659 ++cam_itr)
660 {
661 if (firstCamera)
662 {
663 if ((*cam_itr)->getRenderOrder() < firstCamera->getRenderOrder())
664 {
665 if ((*cam_itr)->getNodeMask() != 0x0)
666 firstCamera = (*cam_itr);
667 }
668 if ((*cam_itr)->getRenderOrder() == firstCamera->getRenderOrder() &&
669 (*cam_itr)->getRenderOrderNum() < firstCamera->getRenderOrderNum())
670 {
671 if ((*cam_itr)->getNodeMask() != 0x0)
672 firstCamera = (*cam_itr);
673 }
674 }
675 else
676 {
677 if ((*cam_itr)->getNodeMask() != 0x0)
678 firstCamera = *cam_itr;
679 }
680 }
681
682 if (firstCamera)
683 {
684 //OSG_NOTICE<<"ScreenCaptureHandler: First camera "<<firstCamera<<std::endl;
685
686 return firstCamera;
687 }
688 else
689 {
690 OSG_NOTICE<<"ScreenCaptureHandler: No camera found"<<std::endl;
691 }
692 }
693 }
694 else
695 {
696 osgViewer::ViewerBase::Contexts contexts;
697 viewer.getContexts(contexts);
698 for(osgViewer::ViewerBase::Contexts::iterator itr = contexts.begin();
699 itr != contexts.end();
700 ++itr)
701 {
702 osg::GraphicsContext* context = *itr;
703 osg::GraphicsContext::Cameras& cameras = context->getCameras();
704 osg::Camera* lastCamera = 0;
705 for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
706 cam_itr != cameras.end();
707 ++cam_itr)
708 {
709 if (lastCamera)
710 {
711 if ((*cam_itr)->getRenderOrder() > lastCamera->getRenderOrder())
712 {
713 if ((*cam_itr)->getNodeMask() != 0x0)
714 lastCamera = (*cam_itr);
715 }
716 if ((*cam_itr)->getRenderOrder() == lastCamera->getRenderOrder() &&
717 (*cam_itr)->getRenderOrderNum() >= lastCamera->getRenderOrderNum())
718 {
719 if ((*cam_itr)->getNodeMask() != 0x0)
720 lastCamera = (*cam_itr);
721 }
722 }
723 else
724 {
725 if ((*cam_itr)->getNodeMask() != 0x0)
726 lastCamera = *cam_itr;
727 }
728 }
729
730 if (lastCamera)
731 {
732 //OSG_NOTICE<<"ScreenCaptureHandler: Last camera "<<lastCamera<<std::endl;
733
734 return lastCamera;
735 }
736 else
737 {
738 OSG_NOTICE<<"ScreenCaptureHandler: No camera found"<<std::endl;
739 }
740 }
741 }
742
743 return 0;
744 }
745
746 // aa will point to an osgViewer::View, so we will take a screenshot
747 // of that view's graphics contexts.
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)748 bool ScreenCaptureHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
749 {
750 osgViewer::ViewerBase* viewer = dynamic_cast<osgViewer::View*>(&aa)->getViewerBase();
751 if (!viewer) return false;
752
753 switch(ea.getEventType())
754 {
755 case (osgGA::GUIEventAdapter::FRAME):
756 {
757 // Booleans aren't the best way of doing this, but I want to do
758 // the actual adding here because I don't want to require
759 // startCapture() take a viewer as argument, which could not be
760 // the right one.
761 if (_startCapture)
762 {
763 // Start capturing with the currently set number of frames.
764 // If set to -1 it will capture continuously, if set to >0
765 // it will capture that number of frames.
766 _startCapture = false;
767 addCallbackToViewer(*viewer);
768 }
769 else if (_stopCapture)
770 {
771 _stopCapture = false;
772 removeCallbackFromViewer(*viewer);
773 }
774 break;
775 }
776
777 case(osgGA::GUIEventAdapter::KEYUP):
778 {
779 if (ea.getKey() == _keyEventTakeScreenShot)
780 {
781 // Check that we will capture at least one frame.
782 // Just check for ==0, because >0 is means we're already
783 // capturing and <0 means it will capture all frames.
784 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
785 if (callback->getFramesToCapture() == 0)
786 {
787 setFramesToCapture(1);
788 }
789 addCallbackToViewer(*viewer);
790 return true;
791 }
792
793 if (ea.getKey() == _keyEventToggleContinuousCapture)
794 {
795 if (getFramesToCapture() < 0)
796 {
797 setFramesToCapture(0);
798 removeCallbackFromViewer(*viewer);
799 }
800 else
801 {
802 setFramesToCapture(-1);
803 addCallbackToViewer(*viewer);
804 }
805 return true;
806 }
807 break;
808 }
809 default:
810 break;
811 }
812
813 return false;
814 }
815
816 /** Capture the given viewer's views on the next frame. */
captureNextFrame(osgViewer::ViewerBase & viewer)817 void ScreenCaptureHandler::captureNextFrame(osgViewer::ViewerBase& viewer)
818 {
819 addCallbackToViewer(viewer);
820 }
821
822 /** Set the number of frames to capture. */
setFramesToCapture(int numFrames)823 void ScreenCaptureHandler::setFramesToCapture(int numFrames)
824 {
825 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
826 callback->setFramesToCapture(numFrames);
827 }
828
829 /** Get the number of frames to capture. */
getFramesToCapture() const830 int ScreenCaptureHandler::getFramesToCapture() const
831 {
832 WindowCaptureCallback* callback = static_cast<WindowCaptureCallback*>(_callback.get());
833 return callback->getFramesToCapture();
834 }
835
836 /** Start capturing at the end of the next frame. */
startCapture()837 void ScreenCaptureHandler::startCapture()
838 {
839 if (getFramesToCapture() != 0)
840 _startCapture = true;
841 }
842
843 /** Stop capturing. */
stopCapture()844 void ScreenCaptureHandler::stopCapture()
845 {
846 _stopCapture = true;
847 }
848
849 /** Get the keyboard and mouse usage of this manipulator.*/
getUsage(osg::ApplicationUsage & usage) const850 void ScreenCaptureHandler::getUsage(osg::ApplicationUsage& usage) const
851 {
852 usage.addKeyboardMouseBinding(_keyEventTakeScreenShot,"Take screenshot.");
853 usage.addKeyboardMouseBinding(_keyEventToggleContinuousCapture,"Toggle continuous screen capture.");
854 }
855
856 }
857