root/OpenSceneGraph/trunk/examples/osgprerendercubemap/osgprerendercubemap.cpp @ 3033

Revision 3033, 17.2 kB (checked in by robert, 11 years ago)

From Ulrich Hertlein & Iskander Prins, added rotation of skydown on
fixed handling of updating one image per frame.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/GLExtensions>
2#include <osg/Group>
3#include <osg/Geode>
4#include <osg/Matrix>
5#include <osg/Quat>
6#include <osg/StateSet>
7#include <osg/TextureCubeMap>
8#include <osg/TexGen>
9#include <osg/TexMat>
10#include <osg/TexEnvCombine>
11#include <osg/ShapeDrawable>
12#include <osg/PositionAttitudeTransform>
13
14#include <osg/MatrixTransform>
15
16#include <osgUtil/RenderToTextureStage>
17#include <osgUtil/Optimizer>
18#include <osgUtil/TransformCallback>
19
20#include <osgDB/ReadFile>
21#include <osgDB/Registry>
22
23#include <osgGA/TrackballManipulator>
24#include <osgGA/FlightManipulator>
25#include <osgGA/DriveManipulator>
26
27#include <osgProducer/Viewer>
28
29#include <iostream>
30#include <string>
31#include <vector>
32
33#define UPDATE_ONE_IMAGE_PER_FRAME 1
34
35
36class PrerenderAppCallback : public osg::NodeCallback
37{
38    public:
39   
40        PrerenderAppCallback(osg::Node* subgraph):
41            _subgraph(subgraph) {}
42
43        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
44        {
45            // traverse the subgraph to update any nodes.
46            if (_subgraph.valid()) _subgraph->accept(*nv);
47       
48            // must traverse the Node's subgraph           
49            traverse(node,nv);
50        }
51       
52        osg::ref_ptr<osg::Node>     _subgraph;
53};
54
55
56class PrerenderCullCallback : public osg::NodeCallback
57{
58    public:
59   
60        PrerenderCullCallback(osg::Node* subgraph, osg::TextureCubeMap* cubemap, osg::TexMat* texmat):
61            _subgraph(subgraph),
62            _cubemap(cubemap),
63            _texmat(texmat)
64            {
65                _updateCubemapFace = 0;
66                _clearColor = osg::Vec4(1,1,1,1);
67                _localState[0] = new osg::StateSet;
68                _localState[1] = new osg::StateSet;
69                _localState[2] = new osg::StateSet;
70                _localState[3] = new osg::StateSet;
71                _localState[4] = new osg::StateSet;
72                _localState[5] = new osg::StateSet;
73            }
74
75        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
76        {
77            // Reset this counter to zero, otherwise it wont do the while loop down below.
78            // And the cubemap will never update the newer frames.
79            if (_updateCubemapFace > 5)
80                _updateCubemapFace = 0;
81
82            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
83            if (cv && _cubemap.valid() && _subgraph.valid())
84            {
85                const osg::Vec4 clearColArray[] =
86                {
87                    osg::Vec4(0, 0, 1, 1), // +X
88                    osg::Vec4(1, 0.7f, 0, 1), // -X
89                    osg::Vec4(0, 1, 1, 1), // +Y
90                    osg::Vec4(1, 1, 0, 1), // -Y
91                    osg::Vec4(1, 0, 0, 1), // +Z
92                    osg::Vec4(0, 1, 0, 1)  // -Z
93                };
94
95                osg::Quat q;
96                cv->getModelViewMatrix().get(q);
97                const osg::Matrix C = osg::Matrix::rotate( q.inverse() );
98                _texmat->setMatrix(C);
99
100#if UPDATE_ONE_IMAGE_PER_FRAME
101                if ((_updateCubemapFace >= 0) && (_updateCubemapFace <= 5))
102                {
103                    _clearColor = clearColArray[_updateCubemapFace];
104                    doPreRender(*node, *cv, _updateCubemapFace++);
105                }
106#else
107                while (_updateCubemapFace<6)
108                {
109                    _clearColor = clearColArray[_updateCubemapFace];
110                    doPreRender(*node, *cv, _updateCubemapFace++);
111                }   
112#endif
113            }
114               
115            // must traverse the subgraph           
116            traverse(node,nv);
117        }
118           
119        void doPreRender(osg::Node& node, osgUtil::CullVisitor& cv, const int nFace);
120       
121        struct ImageData
122        {
123            ImageData(const osg::Vec3& dir, const osg::Vec3& up) : _dir(dir), _up(up) {}
124            osg::Vec3 _dir;
125            osg::Vec3 _up;
126        };
127
128        osg::ref_ptr<osg::Node> _subgraph;
129        osg::ref_ptr<osg::TextureCubeMap> _cubemap;
130        osg::ref_ptr<osg::StateSet> _localState[6];
131        osg::ref_ptr<osg::TexMat> _texmat;
132        osg::Vec4 _clearColor;
133        int _updateCubemapFace;
134};
135
136
137void PrerenderCullCallback::doPreRender(osg::Node& /*node*/, osgUtil::CullVisitor& cv, const int nFace)
138{
139    const ImageData id[] =
140    {
141        ImageData( osg::Vec3( 1,  0,  0), osg::Vec3( 0, -1,  0) ), // +X
142        ImageData( osg::Vec3(-1,  0,  0), osg::Vec3( 0, -1,  0) ), // -X
143        ImageData( osg::Vec3( 0,  1,  0), osg::Vec3( 0,  0,  1) ), // +Y
144        ImageData( osg::Vec3( 0, -1,  0), osg::Vec3( 0,  0, -1) ), // -Y
145        ImageData( osg::Vec3( 0,  0,  1), osg::Vec3( 0, -1,  0) ), // +Z
146        ImageData( osg::Vec3( 0,  0, -1), osg::Vec3( 0, -1,  0) )  // -Z
147    };
148
149    osg::Image* image = _cubemap->getImage((osg::TextureCubeMap::Face)nFace);
150    osg::Vec3 dir = id[nFace]._dir;
151    osg::Vec3 up  = id[nFace]._up;
152
153    const osg::BoundingSphere& bs = _subgraph->getBound();
154    if (!bs.valid())
155    {
156        osg::notify(osg::WARN) << "bb invalid"<<_subgraph.get()<<std::endl;
157        return;
158    }
159
160    // create the render to texture stage.
161    osg::ref_ptr<osgUtil::RenderToTextureStage> rtts = new osgUtil::RenderToTextureStage;
162
163    // set up lighting.
164    // currently ignore lights in the scene graph itself..
165    // will do later.
166    osgUtil::RenderStage* previous_stage = cv.getCurrentRenderBin()->_stage;
167
168    // set up the background color and clear mask.
169    rtts->setClearColor(_clearColor);
170
171    // ABJ: use default (color+depth)
172    rtts->setClearMask(previous_stage->getClearMask());
173
174    // set up to charge the same RenderStageLighting is the parent previous stage.
175    rtts->setRenderStageLighting(previous_stage->getRenderStageLighting());
176
177    // record the render bin, to be restored after creation
178    // of the render to text
179    osgUtil::RenderBin* previousRenderBin = cv.getCurrentRenderBin();
180
181    // set the current renderbin to be the newly created stage.
182    cv.setCurrentRenderBin(rtts.get());
183
184
185    float znear = 1.0f*bs.radius();
186    float zfar  = 3.0f*bs.radius();
187       
188    znear *= 0.9f;
189    zfar *= 1.1f;
190
191    // set up projection.
192    const double fovy = 90.0;
193    const double aspectRatio = 1.0;
194    osg::RefMatrix* projection = new osg::RefMatrix;
195    projection->makePerspective(fovy, aspectRatio, znear, zfar);
196
197    cv.pushProjectionMatrix(projection);
198
199    osg::RefMatrix* matrix = new osg::RefMatrix;
200    osg::Vec3 eye    = bs.center(); eye.z() = 0.0f;
201    osg::Vec3 center = eye + dir;
202    matrix->makeLookAt(eye, center, up);
203
204    cv.pushModelViewMatrix(matrix);
205
206    cv.pushStateSet(_localState[nFace].get());
207
208    {
209        // traverse the subgraph
210        _subgraph->accept(cv);
211    }
212
213    cv.popStateSet();
214
215    // restore the previous model view matrix.
216    cv.popModelViewMatrix();
217
218    // restore the previous model view matrix.
219    cv.popProjectionMatrix();
220
221    // restore the previous renderbin.
222    cv.setCurrentRenderBin(previousRenderBin);
223
224    if (rtts->_renderGraphList.size()==0 && rtts->_bins.size()==0)
225    {
226        // getting to this point means that all the subgraph has been
227        // culled by small feature culling or is beyond LOD ranges.
228        return;
229    }
230
231    int height = 512;
232    int width  = 512;
233
234    const osg::Viewport& viewport = *cv.getViewport();
235
236    // offset the impostor viewport from the center of the main window
237    // viewport as often the edges of the viewport might be obscured by
238    // other windows, which can cause image/reading writing problems.
239    int center_x = viewport.x()+viewport.width()/2;
240    int center_y = viewport.y()+viewport.height()/2;
241
242    osg::Viewport* new_viewport = new osg::Viewport;
243    new_viewport->setViewport(center_x-width/2,center_y-height/2,width,height);
244    rtts->setViewport(new_viewport);
245   
246    _localState[nFace]->setAttribute(new_viewport);
247
248    // and the render to texture stage to the current stages
249    // dependancy list.
250    cv.getCurrentRenderBin()->_stage->addToDependencyList(rtts.get());
251
252    // if one exist attach image to the RenderToTextureStage.
253    // if (image.valid()) rtts->setImage(_image.get());
254    if (image) rtts->setImage(image);
255}
256
257
258osg::Drawable* makeGeometry()
259{
260    const float radius = 20;
261    return new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),radius));
262}
263
264osg::Node* createPreRenderSubGraph(osg::Node* subgraph)
265{
266    if (!subgraph) return 0;
267 
268    // create the quad to visualize.
269    osg::Drawable* geom = makeGeometry();
270    geom->setSupportsDisplayList(false);
271
272    // new we need to add the texture to the Drawable, we do so by creating a
273    // StateSet to contain the Texture StateAttribute.
274    osg::StateSet* stateset = new osg::StateSet;
275
276    osg::TextureCubeMap* cubemap = new osg::TextureCubeMap;
277
278    // set up the textures
279    osg::Image* imagePosX = new osg::Image;
280    osg::Image* imageNegX = new osg::Image;
281    osg::Image* imagePosY = new osg::Image;
282    osg::Image* imageNegY = new osg::Image;
283    osg::Image* imagePosZ = new osg::Image;
284    osg::Image* imageNegZ = new osg::Image;
285
286    imagePosX->setInternalTextureFormat(GL_RGB);
287    imageNegX->setInternalTextureFormat(GL_RGB);
288    imagePosY->setInternalTextureFormat(GL_RGB);
289    imageNegY->setInternalTextureFormat(GL_RGB);
290    imagePosZ->setInternalTextureFormat(GL_RGB);
291    imageNegZ->setInternalTextureFormat(GL_RGB);
292
293    cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, imagePosX);
294    cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, imageNegX);
295    cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, imagePosY);
296    cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, imageNegY);
297    cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, imagePosZ);
298    cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, imageNegZ);
299
300    cubemap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
301    cubemap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
302    cubemap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
303    cubemap->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
304    cubemap->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
305    stateset->setTextureAttributeAndModes(0, cubemap, osg::StateAttribute::ON);
306
307    osg::TexGen *texgen = new osg::TexGen;
308    texgen->setMode(osg::TexGen::REFLECTION_MAP);
309    stateset->setTextureAttributeAndModes(0, texgen, osg::StateAttribute::ON);
310
311    osg::TexMat* texmat = new osg::TexMat;
312    stateset->setTextureAttribute(0, texmat);
313
314    stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
315
316    geom->setStateSet(stateset);
317
318    osg::Geode* geode = new osg::Geode;
319    geode->addDrawable(geom);
320
321    // Geodes can't have cull callback so create extra Group to attach cullcallback.
322    osg::Group* parent = new osg::Group;
323   
324    parent->setUpdateCallback(new PrerenderAppCallback(subgraph));
325   
326    parent->setCullCallback(new PrerenderCullCallback(subgraph, cubemap, texmat));
327 
328    parent->addChild(geode);
329   
330    return parent;
331}
332
333
334struct DrawableCullCallback : public osg::Drawable::CullCallback
335{
336    DrawableCullCallback(osg::TexMat* texmat) : _texmat(texmat)
337    {}
338
339    virtual bool cull(osg::NodeVisitor* nv, osg::Drawable* /*drawable*/, osg::State* /*state*/) const
340    {
341        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
342        if (cv)
343        {
344            osg::Quat q;
345            cv->getModelViewMatrix().get(q);
346            const osg::Matrix C = osg::Matrix::rotate( q.inverse() );
347            _texmat->setMatrix(C);
348        }
349        return false;
350    }
351
352    mutable osg::ref_ptr<osg::TexMat> _texmat;
353};
354
355osg::Node* createReferenceSphere()
356{
357    const float radius = 10;
358    osg::Drawable* sphere = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),radius));
359
360    osg::StateSet* stateset = new osg::StateSet;
361    sphere->setStateSet(stateset);
362
363    osg::TextureCubeMap* cubemap = new osg::TextureCubeMap;
364    #define CUBEMAP_FILENAME(face) "Cubemap_axis/" #face ".png"
365
366    osg::Image* imagePosX = osgDB::readImageFile(CUBEMAP_FILENAME(posx));
367    osg::Image* imageNegX = osgDB::readImageFile(CUBEMAP_FILENAME(negx));
368    osg::Image* imagePosY = osgDB::readImageFile(CUBEMAP_FILENAME(posy));
369    osg::Image* imageNegY = osgDB::readImageFile(CUBEMAP_FILENAME(negy));
370    osg::Image* imagePosZ = osgDB::readImageFile(CUBEMAP_FILENAME(posz));
371    osg::Image* imageNegZ = osgDB::readImageFile(CUBEMAP_FILENAME(negz));
372
373    if (imagePosX && imageNegX && imagePosY && imageNegY && imagePosZ && imageNegZ)
374    {
375        cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, imagePosX);
376        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, imageNegX);
377        cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, imagePosY);
378        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, imageNegY);
379        cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, imagePosZ);
380        cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, imageNegZ);
381
382        cubemap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
383        cubemap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
384        cubemap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
385        cubemap->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
386        cubemap->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
387        stateset->setTextureAttributeAndModes(0, cubemap, osg::StateAttribute::ON);
388    }
389
390    osg::TexGen *texgen = new osg::TexGen;
391    texgen->setMode(osg::TexGen::REFLECTION_MAP);
392    stateset->setTextureAttributeAndModes(0, texgen, osg::StateAttribute::ON);
393
394    osg::TexMat* texmat = new osg::TexMat;
395    stateset->setTextureAttribute(0, texmat);
396
397    stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
398
399
400    sphere->setCullCallback(new DrawableCullCallback(texmat));
401
402    osg::Geode* geode = new osg::Geode();
403    geode->addDrawable(sphere);
404
405    return geode;
406}
407
408int main( int argc, char **argv )
409{
410    // use an ArgumentParser object to manage the program arguments.
411    osg::ArgumentParser arguments(&argc,argv);
412
413    // set up the usage document, in case we need to print out how to use this program.
414    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates pre rendering of scene to a texture, and then apply this texture to geometry.");
415    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
416    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
417   
418    // construct the viewer.
419    osgProducer::Viewer viewer(arguments);
420
421    // set up the value with sensible default event handlers.
422    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
423
424    // get details on keyboard and mouse bindings used by the viewer.
425    viewer.getUsage(*arguments.getApplicationUsage());
426
427    // if user request help write it out to cout.
428    if (arguments.read("-h") || arguments.read("--help"))
429    {
430        arguments.getApplicationUsage()->write(std::cout);
431        return 1;
432    }
433
434    // any option left unread are converted into errors to write out later.
435    arguments.reportRemainingOptionsAsUnrecognized();
436
437    // report any errors if they have occured when parsing the program aguments.
438    if (arguments.errors())
439    {
440        arguments.writeErrorMessages(std::cout);
441        return 1;
442    }
443/*   
444    if (arguments.argc()<=1)
445    {
446        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
447        return 1;
448    }
449*/
450   
451    osg::Group* rootNode = new osg::Group();
452
453#if 1
454    osg::Node* sky = osgDB::readNodeFile("skydome.osg");
455
456    // Proof of concept to see if the skydome really rotates and that the cubemap gets updated.
457    // create a transform to spin the model.
458   
459    osg::MatrixTransform* loadedModelTransform = new osg::MatrixTransform;
460    loadedModelTransform->addChild(sky);
461
462    osg::NodeCallback* nc = new osgUtil::TransformCallback(loadedModelTransform->getBound().center(),osg::Vec3(0.0f,0.0f,1.0f),osg::inDegrees(5.0f));
463    loadedModelTransform->setUpdateCallback(nc);
464
465    // osg::Group* rootNode = new osg::Group();
466    // rootNode->addChild(loadedModelTransform);
467    // rootNode->addChild(createPreRenderSubGraph(loadedModelTransform));
468
469
470    if (loadedModelTransform)
471    {   
472        rootNode->addChild(createPreRenderSubGraph(loadedModelTransform));
473    }
474#endif
475
476#if 1
477    osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform;
478    pat->setPosition(osg::Vec3(0,0,50));
479    pat->addChild(createReferenceSphere());
480    rootNode->addChild(pat);
481#endif
482
483    // load the nodes from the commandline arguments.
484    osg::Node* loadedModel = osgDB::readNodeFiles(arguments);
485    if (loadedModel)
486        rootNode->addChild(loadedModel);
487
488    // add model to the viewer.
489    viewer.setSceneData( rootNode );
490
491    // create the windows and run the threads.
492    viewer.realize();
493
494    while( !viewer.done() )
495    {
496        // wait for all cull and draw threads to complete.
497        viewer.sync();
498
499        // update the scene by traversing it with the the update visitor which will
500        // call all node update callbacks and animations.
501        viewer.update();
502         
503        // fire off the cull and draw traversals of the scene.
504        viewer.frame();
505       
506    }
507   
508    // wait for all cull and draw threads to complete before exit.
509    viewer.sync();
510
511    return 0;
512}
Note: See TracBrowser for help on using the browser.