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

Revision 5786, 14.1 kB (checked in by robert, 8 years ago)

Added support for placing a base in the scene to shadow against

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