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

Revision 8338, 19.7 kB (checked in by robert, 6 years ago)

Added option for controlling whether the front buffer is read at the start of the
frame or the back buffer at the end of the frame.

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