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

Revision 8343, 20.8 kB (checked in by robert, 6 years ago)

Added feedback of pixel format chosen for read back

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