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

Revision 8333, 16.9 kB (checked in by robert, 6 years ago)

Added single buffered and double buffered PBO support, and --no-pbo,
--single-pbo and --double-pbo command line parameters

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