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

Revision 8341, 20.3 kB (checked in by robert, 6 years ago)

Added option for doing triple buffering, and set the default read format
to GL_RGBA

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