root/OpenSceneGraph/trunk/examples/osgscreencapture/osgscreencapture.cpp @ 8339

Revision 8339, 19.8 kB (checked in by robert, 6 years ago)

Added option for setting whether the front or back buffer should be read using
--front and --back command line options.

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This application is open source and may be redistributed and/or modified   
4 * freely and without restriction, both in commericial and non commericial applications,
5 * as long as this copyright notice is maintained.
6 *
7 * This application is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10*/
11
12#include <osgDB/ReadFile>
13#include <osgDB/WriteFile>
14
15#include <osgUtil/Optimizer>
16#include <osg/CoordinateSystemNode>
17
18#include <osg/Switch>
19#include <osgText/Text>
20
21#include <osgViewer/Viewer>
22#include <osgViewer/ViewerEventHandlers>
23
24#include <osgGA/TrackballManipulator>
25#include <osgGA/FlightManipulator>
26#include <osgGA/DriveManipulator>
27#include <osgGA/KeySwitchMatrixManipulator>
28#include <osgGA/StateSetManipulator>
29#include <osgGA/AnimationPathManipulator>
30#include <osgGA/TerrainManipulator>
31
32#include <iostream>
33#include <sstream>
34
35class WindowCaptureCallback : public osg::Camera::DrawCallback
36{
37    public:
38   
39        enum Mode
40        {
41            READ_PIXELS,
42            SINGLE_PBO,
43            DOUBLE_PBO
44        };
45   
46        enum FramePosition
47        {
48            START_FRAME,
49            END_FRAME
50        };
51   
52        struct ContextData : public osg::Referenced
53        {
54       
55            ContextData(osg::GraphicsContext* gc, Mode mode, GLenum readBuffer, const std::string& name):
56                _gc(gc),
57                _mode(mode),
58                _readBuffer(readBuffer),
59                _fileName(name),
60                _pixelFormat(GL_BGR),
61                _type(GL_UNSIGNED_BYTE),
62                _width(0),
63                _height(0),
64                _currentImageIndex(0),
65                _currentPboIndex(0)
66            {
67                getSize(gc, _width, _height);
68               
69                std::cout<<"Window size "<<_width<<", "<<_height<<std::endl;
70           
71                // single buffered image
72                _imageBuffer.push_back(new osg::Image);
73               
74                // double buffer PBO.
75                switch(_mode)
76                {
77                    case(READ_PIXELS):
78                        osg::notify(osg::NOTICE)<<"Reading window usig glReadPixels, with out PixelBufferObject."<<std::endl;
79                        break;
80                    case(SINGLE_PBO):
81                        osg::notify(osg::NOTICE)<<"Reading window usig glReadPixels, with a single PixelBufferObject."<<std::endl;
82                        _pboBuffer.push_back(0);
83                        break;
84                    case(DOUBLE_PBO):
85                        osg::notify(osg::NOTICE)<<"Reading window usig glReadPixels, with a double buffer PixelBufferObject."<<std::endl;
86                        _pboBuffer.push_back(0);
87                        _pboBuffer.push_back(0);
88                        break;
89                    default:
90                        break;                               
91                }
92            }
93           
94            void getSize(osg::GraphicsContext* gc, int& width, int& height)
95            {
96                if (gc->getTraits())
97                {
98                    width = gc->getTraits()->width;
99                    height = gc->getTraits()->height;
100                }
101            }
102           
103            void read()
104            {
105                osg::BufferObject::Extensions* ext = osg::BufferObject::getExtensions(_gc->getState()->getContextID(),true);
106
107                if (ext->isPBOSupported() && !_pboBuffer.empty())
108                {
109                    if (_pboBuffer.size()==1)
110                    {
111                        singlePBO(ext);
112                    }
113                    else
114                    {
115                        multiPBO(ext);
116                    }
117                }
118                else
119                {
120                    readPixels();
121                }
122            }
123           
124            void readPixels();
125
126            void singlePBO(osg::BufferObject::Extensions* ext);
127
128            void multiPBO(osg::BufferObject::Extensions* ext);
129       
130            typedef std::vector< osg::ref_ptr<osg::Image> >             ImageBuffer;
131            typedef std::vector< GLuint > PBOBuffer;
132       
133            osg::GraphicsContext*   _gc;
134            Mode                    _mode;
135            GLenum                  _readBuffer;
136            std::string             _fileName;
137           
138            GLenum                  _pixelFormat;
139            GLenum                  _type;
140            int                     _width;
141            int                     _height;
142           
143            unsigned int            _currentImageIndex;
144            ImageBuffer             _imageBuffer;
145           
146            unsigned int            _currentPboIndex;
147            PBOBuffer               _pboBuffer;
148        };
149   
150        WindowCaptureCallback(Mode mode, FramePosition position, GLenum readBuffer):
151            _mode(mode),
152            _position(position),
153            _readBuffer(readBuffer)
154        {
155        }
156
157        FramePosition getFramePosition() const { return _position; }
158
159        ContextData* createContextData(osg::GraphicsContext* gc) const
160        {
161            std::stringstream filename;
162            filename << "test_"<<_contextDataMap.size()<<".jpg";
163            return new ContextData(gc, _mode, _readBuffer, filename.str());
164        }
165       
166        ContextData* getContextData(osg::GraphicsContext* gc) const
167        {
168            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
169            osg::ref_ptr<ContextData>& data = _contextDataMap[gc];
170            if (!data) data = createContextData(gc);
171           
172            return data.get();
173        }
174
175        virtual void operator () (osg::RenderInfo& renderInfo) const
176        {
177            glReadBuffer(_readBuffer);
178
179            osg::GraphicsContext* gc = renderInfo.getState()->getGraphicsContext();
180            osg::ref_ptr<ContextData> cd = getContextData(gc);
181            cd->read();
182        }
183       
184        typedef std::map<osg::GraphicsContext*, osg::ref_ptr<ContextData> > ContextDataMap;
185
186        Mode                        _mode;       
187        FramePosition               _position;
188        GLenum                      _readBuffer;
189        mutable OpenThreads::Mutex  _mutex;
190        mutable ContextDataMap      _contextDataMap;
191       
192};
193
194void WindowCaptureCallback::ContextData::readPixels()
195{
196    // std::cout<<"readPixels("<<_fileName<<" image "<<_currentImageIndex<<" "<<_currentPboIndex<<std::endl;
197
198    unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
199    unsigned int nextPboIndex = _pboBuffer.empty() ? 0 : (_currentPboIndex+1)%_pboBuffer.size();
200
201    int width=0, height=0;
202    getSize(_gc, width, height);
203    if (width!=_width || _height!=height)
204    {
205        std::cout<<"   Window resized "<<width<<", "<<height<<std::endl;
206        _width = width;
207        _height = height;
208    }
209
210    osg::Image* image = _imageBuffer[_currentImageIndex].get();
211
212#if 1
213    image->readPixels(0,0,_width,_height,
214                      _pixelFormat,_type);
215#endif
216
217    if (!_fileName.empty())
218    {
219        // osgDB::writeImageFile(*image, _fileName);
220    }
221
222    _currentImageIndex = nextImageIndex;
223    _currentPboIndex = nextPboIndex;
224}
225
226void WindowCaptureCallback::ContextData::singlePBO(osg::BufferObject::Extensions* ext)
227{
228    // std::cout<<"singelPBO(  "<<_fileName<<" image "<<_currentImageIndex<<" "<<_currentPboIndex<<std::endl;
229
230    unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
231
232    int width=0, height=0;
233    getSize(_gc, width, height);
234    if (width!=_width || _height!=height)
235    {
236        std::cout<<"   Window resized "<<width<<", "<<height<<std::endl;
237        _width = width;
238        _height = height;
239    }
240
241    GLuint& pbo = _pboBuffer[0];
242   
243    osg::Image* image = _imageBuffer[_currentImageIndex].get();
244    if (image->s() != _width ||
245        image->t() != _height)
246    {
247        osg::notify(osg::NOTICE)<<"Allocating image "<<std::endl;
248        image->allocateImage(_width, _height, 1, _pixelFormat, _type);
249       
250        if (pbo!=0)
251        {
252            osg::notify(osg::NOTICE)<<"deleting pbo "<<pbo<<std::endl;
253            ext->glDeleteBuffers (1, &pbo);
254            pbo = 0;
255        }
256    }
257   
258   
259    if (pbo==0)
260    {
261        ext->glGenBuffers(1, &pbo);
262        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
263        ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
264
265        osg::notify(osg::NOTICE)<<"Generating pbo "<<pbo<<std::endl;
266    }
267    else
268    {
269        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, pbo);
270    }
271
272#if 1
273    glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
274#endif
275
276    GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
277                                              GL_READ_ONLY_ARB);
278
279    if(src)
280    {
281        memcpy(image->data(), src, image->getTotalSizeInBytes());
282
283        ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
284    }
285
286    if (!_fileName.empty())
287    {
288        // osgDB::writeImageFile(*image, _fileName);
289    }
290
291    ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
292
293    _currentImageIndex = nextImageIndex;
294}
295
296void WindowCaptureCallback::ContextData::multiPBO(osg::BufferObject::Extensions* ext)
297{
298    // std::cout<<"multiPBO(  "<<_fileName<<" image "<<_currentImageIndex<<" "<<_currentPboIndex<<std::endl;
299    unsigned int nextImageIndex = (_currentImageIndex+1)%_imageBuffer.size();
300    unsigned int nextPboIndex = (_currentPboIndex+1)%_pboBuffer.size();
301
302    int width=0, height=0;
303    getSize(_gc, width, height);
304    if (width!=_width || _height!=height)
305    {
306        std::cout<<"   Window resized "<<width<<", "<<height<<std::endl;
307        _width = width;
308        _height = height;
309    }
310
311    GLuint& copy_pbo = _pboBuffer[_currentPboIndex];
312    GLuint& read_pbo = _pboBuffer[nextPboIndex];
313   
314    osg::Image* image = _imageBuffer[_currentImageIndex].get();
315    if (image->s() != _width ||
316        image->t() != _height)
317    {
318        osg::notify(osg::NOTICE)<<"Allocating image "<<std::endl;
319        image->allocateImage(_width, _height, 1, _pixelFormat, _type);
320       
321        if (read_pbo!=0)
322        {
323            osg::notify(osg::NOTICE)<<"deleting pbo "<<read_pbo<<std::endl;
324            ext->glDeleteBuffers (1, &read_pbo);
325            read_pbo = 0;
326        }
327
328        if (copy_pbo!=0)
329        {
330            osg::notify(osg::NOTICE)<<"deleting pbo "<<copy_pbo<<std::endl;
331            ext->glDeleteBuffers (1, &copy_pbo);
332            copy_pbo = 0;
333        }
334    }
335   
336   
337    bool doCopy = copy_pbo!=0;
338    if (copy_pbo==0)
339    {
340        ext->glGenBuffers(1, &copy_pbo);
341        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
342        ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
343
344        osg::notify(osg::NOTICE)<<"Generating pbo "<<read_pbo<<std::endl;
345    }
346
347    if (read_pbo==0)
348    {
349        ext->glGenBuffers(1, &read_pbo);
350        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
351        ext->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, image->getTotalSizeInBytes(), 0, GL_STREAM_READ);
352
353        osg::notify(osg::NOTICE)<<"Generating pbo "<<read_pbo<<std::endl;
354    }
355    else
356    {
357        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, read_pbo);
358    }
359
360#if 1
361    glReadPixels(0, 0, _width, _height, _pixelFormat, _type, 0);
362#endif
363
364    if (doCopy)
365    {
366
367        ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, copy_pbo);
368
369        GLubyte* src = (GLubyte*)ext->glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB,
370                                                  GL_READ_ONLY_ARB);
371
372        if(src)
373        {
374            memcpy(image->data(), src, image->getTotalSizeInBytes());
375
376            ext->glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);
377        }
378
379        if (!_fileName.empty())
380        {
381            // osgDB::writeImageFile(*image, _fileName);
382        }
383    }
384   
385    ext->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
386
387    _currentImageIndex = nextImageIndex;
388    _currentPboIndex = nextPboIndex;
389}
390
391void addCallbackToViewer(osgViewer::ViewerBase& viewer, WindowCaptureCallback* callback)
392{
393   
394    if (callback->getFramePosition()==WindowCaptureCallback::START_FRAME)
395    {
396        osgViewer::ViewerBase::Windows windows;
397        viewer.getWindows(windows);
398        for(osgViewer::ViewerBase::Windows::iterator itr = windows.begin();
399            itr != windows.end();
400            ++itr)
401        {
402            osgViewer::GraphicsWindow* window = *itr;
403            osg::GraphicsContext::Cameras& cameras = window->getCameras();
404            osg::Camera* firstCamera = 0;
405            for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
406                cam_itr != cameras.end();
407                ++cam_itr)
408            {
409                if (firstCamera)
410                {
411                    if ((*cam_itr)->getRenderOrder() < firstCamera->getRenderOrder())
412                    {
413                        firstCamera = (*cam_itr);
414                    }
415                    if ((*cam_itr)->getRenderOrder() == firstCamera->getRenderOrder() &&
416                        (*cam_itr)->getRenderOrderNum() < firstCamera->getRenderOrderNum())
417                    {
418                        firstCamera = (*cam_itr);
419                    }
420                }
421                else
422                {
423                    firstCamera = *cam_itr;
424                }
425            }
426
427            if (firstCamera)
428            {
429                osg::notify(osg::NOTICE)<<"First camera "<<firstCamera<<std::endl;
430
431                firstCamera->setFinalDrawCallback(callback);
432            }
433            else
434            {
435                osg::notify(osg::NOTICE)<<"No camera found"<<std::endl;
436            }
437        }
438    }
439    else
440    {   
441        osgViewer::ViewerBase::Windows windows;
442        viewer.getWindows(windows);
443        for(osgViewer::ViewerBase::Windows::iterator itr = windows.begin();
444            itr != windows.end();
445            ++itr)
446        {
447            osgViewer::GraphicsWindow* window = *itr;
448            osg::GraphicsContext::Cameras& cameras = window->getCameras();
449            osg::Camera* lastCamera = 0;
450            for(osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin();
451                cam_itr != cameras.end();
452                ++cam_itr)
453            {
454                if (lastCamera)
455                {
456                    if ((*cam_itr)->getRenderOrder() > lastCamera->getRenderOrder())
457                    {
458                        lastCamera = (*cam_itr);
459                    }
460                    if ((*cam_itr)->getRenderOrder() == lastCamera->getRenderOrder() &&
461                        (*cam_itr)->getRenderOrderNum() >= lastCamera->getRenderOrderNum())
462                    {
463                        lastCamera = (*cam_itr);
464                    }
465                }
466                else
467                {
468                    lastCamera = *cam_itr;
469                }
470            }
471
472            if (lastCamera)
473            {
474                osg::notify(osg::NOTICE)<<"Last camera "<<lastCamera<<std::endl;
475
476                lastCamera->setFinalDrawCallback(callback);
477            }
478            else
479            {
480                osg::notify(osg::NOTICE)<<"No camera found"<<std::endl;
481            }
482        }
483    }
484}
485
486int main(int argc, char** argv)
487{
488    // use an ArgumentParser object to manage the program arguments.
489    osg::ArgumentParser arguments(&argc,argv);
490
491    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
492    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
493    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
494
495    osgViewer::Viewer viewer(arguments);
496
497    unsigned int helpType = 0;
498    if ((helpType = arguments.readHelpType()))
499    {
500        arguments.getApplicationUsage()->write(std::cout, helpType);
501        return 1;
502    }
503   
504    // report any errors if they have occurred when parsing the program arguments.
505    if (arguments.errors())
506    {
507        arguments.writeErrorMessages(std::cout);
508        return 1;
509    }
510   
511    if (arguments.argc()<=1)
512    {
513        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
514        return 1;
515    }
516
517    // set up the camera manipulators.
518    {
519        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
520
521        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
522        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
523        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
524        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
525
526        std::string pathfile;
527        char keyForAnimationPath = '5';
528        while (arguments.read("-p",pathfile))
529        {
530            osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
531            if (apm || !apm->valid())
532            {
533                unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
534                keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
535                keyswitchManipulator->selectMatrixManipulator(num);
536                ++keyForAnimationPath;
537            }
538        }
539
540        viewer.setCameraManipulator( keyswitchManipulator.get() );
541    }
542
543    // add the state manipulator
544    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
545   
546    // add the thread model handler
547    viewer.addEventHandler(new osgViewer::ThreadingHandler);
548
549    // add the window size toggle handler
550    viewer.addEventHandler(new osgViewer::WindowSizeHandler);
551       
552    // add the stats handler
553    viewer.addEventHandler(new osgViewer::StatsHandler);
554
555    // add the help handler
556    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
557
558    // add the record camera path handler
559    viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
560
561    // add the LOD Scale handler
562    viewer.addEventHandler(new osgViewer::LODScaleHandler);
563
564    GLenum readBuffer = GL_BACK;
565    WindowCaptureCallback::FramePosition position = WindowCaptureCallback::END_FRAME;
566    WindowCaptureCallback::Mode mode = WindowCaptureCallback::DOUBLE_PBO;
567
568    while (arguments.read("--start-frame")) { position = WindowCaptureCallback::START_FRAME; readBuffer = GL_FRONT; }
569    while (arguments.read("--end-frame")) position = WindowCaptureCallback::END_FRAME;
570
571    while (arguments.read("--front")) readBuffer = GL_FRONT;
572    while (arguments.read("--back")) readBuffer = GL_BACK;
573
574    while (arguments.read("--no-pbo")) mode = WindowCaptureCallback::READ_PIXELS;
575    while (arguments.read("--single-pbo")) mode = WindowCaptureCallback::SINGLE_PBO;
576    while (arguments.read("--double-pbo")) mode = WindowCaptureCallback::DOUBLE_PBO;
577
578   
579       
580    // load the data
581    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
582    if (!loadedModel)
583    {
584        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
585        return 1;
586    }
587
588    // any option left unread are converted into errors to write out later.
589    arguments.reportRemainingOptionsAsUnrecognized();
590
591    // report any errors if they have occurred when parsing the program arguments.
592    if (arguments.errors())
593    {
594        arguments.writeErrorMessages(std::cout);
595        return 1;
596    }
597
598
599    // optimize the scene graph, remove redundant nodes and state etc.
600    osgUtil::Optimizer optimizer;
601    optimizer.optimize(loadedModel.get());
602
603    viewer.setSceneData( loadedModel.get() );
604
605    viewer.realize();
606   
607    addCallbackToViewer(viewer, new WindowCaptureCallback(mode, position, readBuffer));
608
609    return viewer.run();
610
611}
Note: See TracBrowser for help on using the browser.