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

Revision 6030, 16.3 kB (checked in by robert, 7 years ago)

Added stats and range of keyboard mouse handling to osgshadow example

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