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

Revision 8342, 20.5 kB (checked in by robert, 7 years ago)

Added automatic selection of the pixel type according to the window type

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