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

Revision 6031, 16.4 kB (checked in by robert, 7 years ago)

Made two sided stencil the default

  • 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("--with-base-texture", "Adde base texture to shadowed model.");
144    arguments.getApplicationUsage()->addCommandLineOption("--no-base-texture", "Adde base texture to shadowed model.");
145
146    // construct the viewer.
147    osgViewer::Viewer viewer;
148
149    // if user request help write it out to cout.
150    if (arguments.read("-h") || arguments.read("--help"))
151    {
152        arguments.getApplicationUsage()->write(std::cout);
153        return 1;
154    }
155
156    bool postionalLight = false;
157    while (arguments.read("--positionalLight")) postionalLight = true;
158    while (arguments.read("--directionalLight")) postionalLight = false;
159
160    bool addOccluderToScene = false;
161    while (arguments.read("--addOccluderToScene")) addOccluderToScene = true;
162
163    bool updateLightPosition = true;
164    while (arguments.read("--noUpdate")) updateLightPosition = false;
165
166    bool createBase = false;
167    while (arguments.read("--base")) createBase = true;
168
169    bool doShadow = true;
170    while (arguments.read("--noShadow")) doShadow = false;
171   
172    osgShadow::ShadowVolumeGeometry::DrawMode drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
173    while (arguments.read("--two-sided")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED;
174    while (arguments.read("--two-pass")) drawMode = osgShadow::ShadowVolumeGeometry::STENCIL_TWO_PASS;
175   
176
177    // set up the camera manipulators.
178    {
179        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
180
181        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
182        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
183        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
184        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
185
186        std::string pathfile;
187        char keyForAnimationPath = '5';
188        while (arguments.read("-p",pathfile))
189        {
190            osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
191            if (apm || !apm->valid())
192            {
193                unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
194                keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
195                keyswitchManipulator->selectMatrixManipulator(num);
196                ++keyForAnimationPath;
197            }
198        }
199
200        viewer.setCameraManipulator( keyswitchManipulator.get() );
201    }
202
203    // add stats
204    viewer.addEventHandler( new osgViewer::StatsHandler() );
205
206    // any option left unread are converted into errors to write out later.
207    arguments.reportRemainingOptionsAsUnrecognized();
208
209    // report any errors if they have occured when parsing the program aguments.
210    if (arguments.errors())
211    {
212      arguments.writeErrorMessages(std::cout);
213      return 1;
214    }
215
216
217    osg::ref_ptr<osg::Node> model = osgDB::readNodeFiles(arguments);
218    if (!model)
219    {
220        osg::notify(osg::NOTICE)<<"No model loaded, please specify a model to load."<<std::endl;
221        return 1;
222    }
223
224    // get the bounds of the model.   
225    ComputeBoundingBoxVisitor cbbv;
226    model->accept(cbbv);
227    osg::BoundingBox bb = cbbv.getBoundingBox();
228
229    if (createBase)
230    {
231        osg::ref_ptr<osg::Group> newGroup = new osg::Group;
232        newGroup->addChild(model.get());
233       
234        osg::Geode* geode = new osg::Geode;
235       
236        osg::Vec3 widthVec(bb.radius(), 0.0f, 0.0f);
237        osg::Vec3 depthVec(0.0f, bb.radius(), 0.0f);
238        osg::Vec3 centerBase( (bb.xMin()+bb.xMax())*0.5f, (bb.yMin()+bb.yMax())*0.5f, bb.zMin()-bb.radius()*0.1f );
239       
240        geode->addDrawable( osg::createTexturedQuadGeometry( centerBase-widthVec*1.5f-depthVec*1.5f,
241                                                             widthVec*3.0f, depthVec*3.0f) );
242        newGroup->addChild(geode);
243       
244        model = newGroup.get();
245    }
246
247    // get the bounds of the model.
248    cbbv.reset();
249    model->accept(cbbv);
250    bb = cbbv.getBoundingBox();
251   
252    osg::ref_ptr<osg::Group> group = new osg::Group;
253
254    // set up the occluder
255    osg::ref_ptr<osgShadow::OccluderGeometry> occluder = new osgShadow::OccluderGeometry;
256    occluder->computeOccluderGeometry(model.get());
257//    cbbv.getPolytope(occluder->getBoundingPolytope(),0.001);
258    cbbv.getBase(occluder->getBoundingPolytope(),0.001);
259
260    if (addOccluderToScene)
261    {
262        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
263        geode->addDrawable(occluder.get());
264        group->addChild(geode.get());
265    }
266   
267    osg::ref_ptr<osgShadow::ShadowVolumeGeometry> shadowVolume = new osgShadow::ShadowVolumeGeometry;
268
269    // shadowVolume->setUseDisplayList(!updateLightPosition);
270    shadowVolume->setUseDisplayList(false);
271
272    osg::Vec4 lightpos;
273   
274    if (postionalLight)
275    {
276        lightpos.set(bb.center().x(), bb.center().y(), bb.zMax() + bb.radius()  ,1.0f);
277    }
278    else
279    {
280        lightpos.set(0.5f,0.25f,0.8f,0.0f);
281    }
282
283
284
285    osg::ref_ptr<osg::Light> light = new osg::Light;
286
287    if (!doShadow)
288    {
289        group->addChild(model.get());
290
291        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
292        occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
293        geode->addDrawable(shadowVolume.get());
294        group->addChild(geode.get());
295
296        osg::StateSet* ss = geode->getOrCreateStateSet();
297        ss->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
298
299    }
300    else
301    {
302        osg::Vec4 ambient(0.2,0.2,0.2,1.0);
303        osg::Vec4 diffuse(0.8,0.8,0.8,1.0);
304        osg::Vec4 zero_colour(0.0,0.0,0.0,1.0);
305   
306        // first group, render the depth buffer + ambient light contribution
307        {
308
309            osg::Group* first_model_group = new osg::Group;
310            first_model_group->addChild(model.get());
311            group->addChild(first_model_group);
312
313            osg::StateSet* ss1 = first_model_group->getOrCreateStateSet();
314
315            osg::LightModel* lm1 = new osg::LightModel;
316            lm1->setAmbientIntensity(ambient);
317            ss1->setAttribute(lm1);
318
319            osg::Light* light1 = new osg::Light;
320            light1->setAmbient(ambient);
321            light1->setDiffuse(zero_colour);
322            light1->setPosition(lightpos);
323            ss1->setAttributeAndModes(light1, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
324            ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
325       
326        }   
327   
328        // second group
329        {
330            // use a camera here just to implement a seperate post rendering stage.
331            osg::Camera* camera = new osg::Camera;
332            camera->setRenderOrder(osg::Camera::POST_RENDER);
333            camera->setClearMask(GL_STENCIL_BUFFER_BIT);
334            group->addChild(camera);
335
336            osg::StateSet* ss_camera = camera->getOrCreateStateSet();
337
338            osg::Depth* depth = new osg::Depth;
339            depth->setWriteMask(false);
340            depth->setFunction(osg::Depth::LEQUAL);
341            ss_camera->setAttribute(depth);
342
343            {
344                osg::ref_ptr<osg::Geode> geode = new osg::Geode;
345                occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
346                shadowVolume->setDrawMode(drawMode);
347               
348
349                if (drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED)
350                {
351                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_SIDED seleteced"<<std::endl;
352
353                    osg::StencilTwoSided* stencil = new osg::StencilTwoSided;
354                    stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS,0,~0u);
355                    stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::DECR_WRAP);
356                    stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS,0,~0u);
357                    stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::INCR_WRAP);
358
359
360                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
361
362                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
363                    ss_sv1->setRenderBinDetails(0, "RenderBin");
364                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
365                    ss_sv1->setAttribute(colourMask);
366                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
367                   
368                    geode->addDrawable(shadowVolume.get());
369                   
370                    camera->addChild(geode.get());
371
372                }
373                else
374                {
375                    osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES seleteced"<<std::endl;
376
377                    osg::Stencil* stencil = new osg::Stencil;
378                    stencil->setFunction(osg::Stencil::ALWAYS,0,~0u);
379                    stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
380
381                    osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false);
382
383                    osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
384                    ss_sv1->setRenderBinDetails(0, "RenderBin");
385                    ss_sv1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
386                    ss_sv1->setAttribute(colourMask);
387                    ss_sv1->setMode(GL_CULL_FACE,osg::StateAttribute::ON);
388                   
389                    geode->addDrawable(shadowVolume.get());
390                   
391                    camera->addChild(geode.get());
392                }
393
394            }
395
396
397            // render scene graph adding contribution of light
398            {
399                osg::Group* second_model_group = new osg::Group;
400                second_model_group->addChild(model.get());
401
402
403                osg::StateSet* ss1 = second_model_group->getOrCreateStateSet();
404                ss1->setRenderBinDetails(5, "RenderBin");
405
406                osg::LightModel* lm1 = new osg::LightModel;
407                lm1->setAmbientIntensity(zero_colour);
408                ss1->setAttribute(lm1);
409
410
411                osg::LightSource* lightsource = new osg::LightSource;
412                lightsource->setLight(light.get());
413                light->setAmbient(zero_colour);
414                light->setDiffuse(diffuse);
415                light->setPosition(lightpos);
416                second_model_group->addChild(lightsource);
417
418                ss1->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
419
420                // set up the stencil ops so that only operator on this mirrors stencil value.
421                osg::Stencil* stencil = new osg::Stencil;
422                stencil->setFunction(osg::Stencil::EQUAL,0,~0u);
423                stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
424                ss1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
425
426                osg::BlendFunc* blend = new osg::BlendFunc;
427                blend->setFunction(osg::BlendFunc::ONE, osg::BlendFunc::ONE);
428                ss1->setAttributeAndModes(blend, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
429                ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
430
431                camera->addChild(second_model_group);
432            }
433                   
434        }   
435
436    }
437
438    // hint to tell viewer to request stencil buffer when setting up windows
439    osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
440
441    viewer.setSceneData(group.get());
442
443    // create the windows and run the threads.
444    viewer.realize();
445
446    osgDB::writeNodeFile(*group,"test.osg");
447
448    while (!viewer.done())
449    {
450        if (updateLightPosition)
451        {
452            float t = viewer.getFrameStamp()->getReferenceTime();
453            if (postionalLight)
454            {
455                lightpos.set(bb.center().x()+sinf(t)*bb.radius(), bb.center().y() + cosf(t)*bb.radius(), bb.zMax() + bb.radius()  ,1.0f);
456            }
457            else
458            {
459                lightpos.set(sinf(t),cosf(t),0.8f,0.0f);
460            }
461            light->setPosition(lightpos);
462            occluder->computeShadowVolumeGeometry(lightpos, *shadowVolume);
463        }
464
465        viewer.frame();
466    }
467   
468    return 0;
469}
Note: See TracBrowser for help on using the browser.