root/OpenSceneGraph/trunk/examples/osgshadow/osgshadow.cpp @ 6035

Revision 6035, 17.0 kB (checked in by robert, 8 years ago)

Added command line help

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/ArgumentParser>
2
3#include <osg/LightModel>
4#include <osg/Depth>
5#include <osg/BlendFunc>
6#include <osg/Camera>
7#include <osg/Stencil>
8#include <osg/StencilTwoSided>
9#include <osg/CullFace>
10#include <osg/Geometry>
11
12#include <osgGA/TrackballManipulator>
13#include <osgGA/FlightManipulator>
14#include <osgGA/DriveManipulator>
15#include <osgGA/KeySwitchMatrixManipulator>
16#include <osgGA/StateSetManipulator>
17#include <osgGA/AnimationPathManipulator>
18#include <osgGA/TerrainManipulator>
19#include <osgGA/AnimationPathManipulator>
20
21#include <osgViewer/Viewer>
22#include <osgViewer/StatsHandler>
23
24#include <osgShadow/OccluderGeometry>
25
26#include <osgDB/ReadFile>
27#include <osgDB/WriteFile>
28
29#include <iostream>
30
31class ComputeBoundingBoxVisitor : public osg::NodeVisitor
32{
33public:
34    ComputeBoundingBoxVisitor():
35        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
36    {
37    }
38   
39    virtual void reset()
40    {
41        _matrixStack.clear();
42        _bb.init();
43    }
44   
45    osg::BoundingBox& getBoundingBox() { return _bb; }
46
47    void getPolytope(osg::Polytope& polytope, float margin=0.1) const
48    {
49        float delta = _bb.radius()*margin;
50        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
51        polytope.add( osg::Plane(0.0, 0.0, -1.0, (_bb.zMax()+delta)) );
52
53        polytope.add( osg::Plane(1.0, 0.0, 0.0, -(_bb.xMin()-delta)) );
54        polytope.add( osg::Plane(-1.0, 0.0, 0.0, (_bb.xMax()+delta)) );
55
56        polytope.add( osg::Plane(0.0, 1.0, 0.0, -(_bb.yMin()-delta)) );
57        polytope.add( osg::Plane(0.0, -1.0, 0.0, (_bb.yMax()+delta)) );
58    }
59       
60    void getBase(osg::Polytope& polytope, float margin=0.1) const
61    {
62        float delta = _bb.radius()*margin;
63        polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) );
64    }
65   
66    void apply(osg::Node& node)
67    {
68        traverse(node);
69    }
70   
71    void apply(osg::Transform& transform)
72    {
73        osg::Matrix matrix;
74        if (!_matrixStack.empty()) matrix = _matrixStack.back();
75       
76        transform.computeLocalToWorldMatrix(matrix,this);
77       
78        pushMatrix(matrix);
79       
80        traverse(transform);
81       
82        popMatrix();
83    }
84   
85    void apply(osg::Geode& geode)
86    {
87        for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
88        {
89            apply(geode.getDrawable(i));
90        }
91    }
92   
93    void pushMatrix(osg::Matrix& matrix)
94    {
95        _matrixStack.push_back(matrix);
96    }
97   
98    void popMatrix()
99    {
100        _matrixStack.pop_back();
101    }
102
103    void apply(osg::Drawable* drawable)
104    {
105        if (_matrixStack.empty()) _bb.expandBy(drawable->getBound());
106        else
107        {
108            osg::Matrix& matrix = _matrixStack.back();
109            const osg::BoundingBox& dbb = drawable->getBound();
110            if (dbb.valid())
111            {
112                _bb.expandBy(dbb.corner(0) * matrix);
113                _bb.expandBy(dbb.corner(1) * matrix);
114                _bb.expandBy(dbb.corner(2) * matrix);
115                _bb.expandBy(dbb.corner(3) * matrix);
116                _bb.expandBy(dbb.corner(4) * matrix);
117                _bb.expandBy(dbb.corner(5) * matrix);
118                _bb.expandBy(dbb.corner(6) * matrix);
119                _bb.expandBy(dbb.corner(7) * matrix);
120            }
121        }
122    }
123   
124protected:
125   
126    typedef std::vector<osg::Matrix> MatrixStack;
127
128    MatrixStack         _matrixStack;
129    osg::BoundingBox    _bb;
130};
131
132
133
134int main(int argc, char** argv)
135{
136    // use an ArgumentParser object to manage the program arguments.
137    osg::ArgumentParser arguments(&argc, argv);
138
139    // set up the usage document, in case we need to print out how to use this program.
140    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is the example which demonstrates using of GL_ARB_shadow extension implemented in osg::Texture class");
141    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName());
142    arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
143    arguments.getApplicationUsage()->addCommandLineOption("--positionalLight", "Use a positional light.");
144    arguments.getApplicationUsage()->addCommandLineOption("--directionalLight", "Use a direction light.");
145    arguments.getApplicationUsage()->addCommandLineOption("--addOccluderToScene", "Add the occluders geometry.");
146    arguments.getApplicationUsage()->addCommandLineOption("--noUpdate", "Disable the updating the of light source.");
147    arguments.getApplicationUsage()->addCommandLineOption("--base", "Add a base geometry to test shadows.");
148    arguments.getApplicationUsage()->addCommandLineOption("--noShadow", "Disable the shadows.");
149    arguments.getApplicationUsage()->addCommandLineOption("--two-sided", "Use two-sided stencil extension for shadow volumes.");
150    arguments.getApplicationUsage()->addCommandLineOption("--two-pass", "Use two-pass stencil for shadow volumes.");
151
152    // construct the viewer.
153    osgViewer::Viewer viewer;
154
155    // if user request help write it out to cout.
156    if (arguments.read("-h") || arguments.read("--help"))
157    {
158        arguments.getApplicationUsage()->write(std::cout);
159        return 1;
160    }
161
162    bool postionalLight = false;
163    while (arguments.read("--positionalLight")) postionalLight = true;
164    while (arguments.read("--directionalLight")) postionalLight = false;
165
166    bool addOccluderToScene = false;
167    while (arguments.read("--addOccluderToScene")) addOccluderToScene = true;
168
169    bool updateLightPosition = true;
170    while (arguments.read("--noUpdate")) updateLightPosition = false;
171
172    bool createBase = false;
173    while (arguments.read("--base")) createBase = true;
174
175    bool doShadow = true;
176    while (arguments.read("--noShadow")) doShadow = false;
177   
178    osgShadow::ShadowVolumeGeometry::DrawMode drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
179    while (arguments.read("--two-sided")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
180    while (arguments.read("--two-pass")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_PASS;
181   
182
183    // set up the camera manipulators.
184    {
185        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
186
187        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
188        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
189        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
190        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
191
192        std::string pathfile;
193        char keyForAnimationPath = '5';
194        while (arguments.read("-p",pathfile))
195        {
196            osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
197            if (apm || !apm->valid())
198            {
199                unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
200                keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
201                keyswitchManipulator->selectMatrixManipulator(num);
202                ++keyForAnimationPath;
203            }
204        }
205
206        viewer.setCameraManipulator( keyswitchManipulator.get() );
207    }
208
209    // add stats
210    viewer.addEventHandler( new osgViewer::StatsHandler() );
211
212    // any option left unread are converted into errors to write out later.
213    arguments.reportRemainingOptionsAsUnrecognized();
214
215    // report any errors if they have occured when parsing the program aguments.
216    if (arguments.errors())
217    {
218      arguments.writeErrorMessages(std::cout);
219      return 1;
220    }
221
222
223    osg::ref_ptr<osg::Node> model = osgDB::readNodeFiles(arguments);
224    if (!model)
225    {
226        osg::notify(osg::NOTICE)<<"No model loaded, please specify a model to load."<<std::endl;
227        return 1;
228    }
229
230    // get the bounds of the model.   
231    ComputeBoundingBoxVisitor cbbv;
232    model->accept(cbbv);
233    osg::BoundingBox bb = cbbv.getBoundingBox();
234
235    if (createBase)
236    {
237        osg::ref_ptr<osg::Group> newGroup = new osg::Group;
238        newGroup->addChild(model.get());
239       
240        osg::Geode* geode = new osg::Geode;
241       
242        osg::Vec3 widthVec(bb.radius(), 0.0f, 0.0f);
243        osg::Vec3 depthVec(0.0f, bb.radius(), 0.0f);
244        osg::Vec3 centerBase( (bb.xMin()+bb.xMax())*0.5f, (bb.yMin()+bb.yMax())*0.5f, bb.zMin()-bb.radius()*0.1f );
245       
246        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*1.5f-depthVec*1.5f,
247                                                             widthVec*3.0f, depthVec*3.0f) );
248        newGroup->addChild(geode);
249       
250        model = newGroup.get();
251    }
252
253    // get the bounds of the model.
254    cbbv.reset();
255    model->accept(cbbv);
256    bb = cbbv.getBoundingBox();
257   
258    osg::ref_ptr<osg::Group> group = new osg::Group;
259
260    // set up the occluder
261    osg::ref_ptr<osgShadow::OccluderGeometry> occluder = new osgShadow::OccluderGeometry;
262    occluder->computeOccluderGeometry(model.get());
263//    cbbv.getPolytope(occluder->getBoundingPolytope(),0.001);
264    cbbv.getBase(occluder->getBoundingPolytope(),0.001);
265
266    if (addOccluderToScene)
267    {
268        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
269        geode->addDrawable(occluder.get());
270        group->addChild(geode.get());
271    }
272   
273    osg::ref_ptr<osgShadow::ShadowVolumeGeometry> shadowVolume = new osgShadow::ShadowVolumeGeometry;
274
275    // shadowVolume->setUseDisplayList(!updateLightPosition);
276    shadowVolume->setUseDisplayList(false);
277
278    osg::Vec4 lightpos;
279   
280    if (postionalLight)
281    {
282        lightpos.set(bb.center().x(), bb.center().y(), bb.zMax() + bb.radius()  ,1.0f);
283    }
284    else
285    {
286        lightpos.set(0.5f,0.25f,0.8f,0.0f);
287    }
288
289
290
291    osg::ref_ptr<osg::Light> light = new osg::Light;
292
293    if (!doShadow)
294    {
295        group->addChild(model.get());
296
297        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
298        occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
299        geode->addDrawable(shadowVolume.get());
300        group->addChild(geode.get());
301
302        osg::StateSet* ss = geode->getOrCreateStateSet();
303        ss->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
304
305    }
306    else
307    {
308        osg::Vec4 ambient(0.2,0.2,0.2,1.0);
309        osg::Vec4 diffuse(0.8,0.8,0.8,1.0);
310        osg::Vec4 zero_colour(0.0,0.0,0.0,1.0);
311   
312        // first group, render the depth buffer + ambient light contribution
313        {
314
315            osg::Group* first_model_group = new osg::Group;
316            first_model_group->addChild(model.get());
317            group->addChild(first_model_group);
318
319            osg::StateSet* ss1 = first_model_group->getOrCreateStateSet();
320
321            osg::LightModel* lm1 = new osg::LightModel;
322            lm1->setAmbientIntensity(ambient);
323            ss1->setAttribute(lm1);
324
325            osg::Light* light1 = new osg::Light;
326            light1->setAmbient(ambient);
327            light1->setDiffuse(zero_colour);
328            light1->setPosition(lightpos);
329            ss1->setAttributeAndModes(light1, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
330            ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
331       
332        }   
333   
334        // second group
335        {
336            // use a camera here just to implement a seperate post rendering stage.
337            osg::Camera* camera = new osg::Camera;
338            camera->setRenderOrder(osg::Camera::POST_RENDER);
339            camera->setClearMask(GL_STENCIL_BUFFER_BIT);
340            group->addChild(camera);
341
342            osg::StateSet* ss_camera = camera->getOrCreateStateSet();
343
344            osg::Depth* depth = new osg::Depth;
345            depth->setWriteMask(false);
346            depth->setFunction(osg::Depth::LEQUAL);
347            ss_camera->setAttribute(depth);
348
349            {
350                osg::ref_ptr<osg::Geode> geode = new osg::Geode;
351                occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
352                shadowVolume->setDrawMode(drawMode);
353               
354
355                if (drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED)
356                {
357                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_SIDED seleteced"<<std::endl;
358
359                    osg::StencilTwoSided* stencil = new osg::StencilTwoSided;
360                    stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS,0,~0u);
361                    stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::DECR_WRAP);
362                    stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS,0,~0u);
363                    stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::INCR_WRAP);
364
365
366                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
367
368                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
369                    ss_sv1->setRenderBinDetails(0, "RenderBin");
370                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
371                    ss_sv1->setAttribute(colourMask);
372                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
373                   
374                    geode->addDrawable(shadowVolume.get());
375                   
376                    camera->addChild(geode.get());
377
378                }
379                else
380                {
381                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES seleteced"<<std::endl;
382
383                    osg::Stencil* stencil = new osg::Stencil;
384                    stencil->setFunction(osg::Stencil::ALWAYS,0,~0u);
385                    stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
386
387                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
388
389                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
390                    ss_sv1->setRenderBinDetails(0, "RenderBin");
391                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
392                    ss_sv1->setAttribute(colourMask);
393                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::ON);
394                   
395                    geode->addDrawable(shadowVolume.get());
396                   
397                    camera->addChild(geode.get());
398                }
399
400            }
401
402
403            // render scene graph adding contribution of light
404            {
405                osg::Group* second_model_group = new osg::Group;
406                second_model_group->addChild(model.get());
407
408
409                osg::StateSet* ss1 = second_model_group->getOrCreateStateSet();
410                ss1->setRenderBinDetails(5, "RenderBin");
411
412                osg::LightModel* lm1 = new osg::LightModel;
413                lm1->setAmbientIntensity(zero_colour);
414                ss1->setAttribute(lm1);
415
416
417                osg::LightSource* lightsource = new osg::LightSource;
418                lightsource->setLight(light.get());
419                light->setAmbient(zero_colour);
420                light->setDiffuse(diffuse);
421                light->setPosition(lightpos);
422                second_model_group->addChild(lightsource);
423
424                ss1->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
425
426                // set up the stencil ops so that only operator on this mirrors stencil value.
427                osg::Stencil* stencil = new osg::Stencil;
428                stencil->setFunction(osg::Stencil::EQUAL,0,~0u);
429                stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
430                ss1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
431
432                osg::BlendFunc* blend = new osg::BlendFunc;
433                blend->setFunction(osg::BlendFunc::ONE, osg::BlendFunc::ONE);
434                ss1->setAttributeAndModes(blend, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
435                ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
436
437                camera->addChild(second_model_group);
438            }
439                   
440        }   
441
442    }
443
444    // hint to tell viewer to request stencil buffer when setting up windows
445    osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
446
447    viewer.setSceneData(group.get());
448
449    // create the windows and run the threads.
450    viewer.realize();
451
452    osgDB::writeNodeFile(*group,"test.osg");
453
454    while (!viewer.done())
455    {
456        if (updateLightPosition)
457        {
458            float t = viewer.getFrameStamp()->getReferenceTime();
459            if (postionalLight)
460            {
461                lightpos.set(bb.center().x()+sinf(t)*bb.radius(), bb.center().y() + cosf(t)*bb.radius(), bb.zMax() + bb.radius()  ,1.0f);
462            }
463            else
464            {
465                lightpos.set(sinf(t),cosf(t),0.8f,0.0f);
466            }
467            light->setPosition(lightpos);
468            occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
469        }
470
471        viewer.frame();
472    }
473   
474    return 0;
475}
Note: See TracBrowser for help on using the browser.