root/OpenSceneGraph/trunk/examples/osgreflect/osgreflect.cpp @ 5381

Revision 5381, 15.2 kB (checked in by robert, 8 years ago)

Added viewer.cleanup_frame() to all examples.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This application is open source and may be redistributed and/or modified   
4 * freely and without restriction, both in commericial and non commericial applications,
5 * as long as this copyright notice is maintained.
6 *
7 * This application is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10*/
11
12#include <osg/Node>
13#include <osg/Geometry>
14#include <osg/Geode>
15#include <osg/Notify>
16#include <osg/MatrixTransform>
17#include <osg/Texture2D>
18#include <osg/BlendFunc>
19#include <osg/Stencil>
20#include <osg/ColorMask>
21#include <osg/Depth>
22#include <osg/ClipNode>
23#include <osg/AnimationPath>
24
25#include <osgDB/ReadFile>
26#include <osgUtil/Optimizer>
27#include <osgProducer/Viewer>
28
29//
30// A simple demo demonstrating planar reflections using multiple renderings
31// of a subgraph, overriding of state attribures and use of the stencil buffer.
32//
33// The multipass system implemented here is a variation if Mark Kilgard's
34// paper "Improving Shadows and Reflections via the Stencil Buffer" which
35// can be found on the developer parts of the NVidia web site.
36//
37// The variations comes from the fact that the mirrors stencil values
38// are done on the first pass, rather than the second as in Mark's paper.
39// The second pass is now Mark's first pass - drawing the unreflected scene,
40// but also unsets the stencil buffer.  This variation stops the unreflected
41// world poking through the mirror to be seen in the final rendering and
42// also obscures the world correctly when on the reverse side of the mirror.
43// Although there is still some unresolved issue with the clip plane needing
44// to be flipped when looking at the reverse side of the mirror.  Niether
45// of these issues are mentioned in the Mark's paper, but trip us up when
46// we apply them.
47
48
49osg::StateSet* createMirrorTexturedState(const std::string& filename)
50{
51    osg::StateSet* dstate = new osg::StateSet;
52    dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF|osg::StateAttribute::PROTECTED);
53   
54    // set up the texture.
55    osg::Image* image = osgDB::readImageFile(filename.c_str());
56    if (image)
57    {
58        osg::Texture2D* texture = new osg::Texture2D;
59        texture->setImage(image);
60        dstate->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON|osg::StateAttribute::PROTECTED);
61    }
62   
63    return dstate;
64}
65
66
67osg::Drawable* createMirrorSurface(float xMin,float xMax,float yMin,float yMax,float z)
68{
69   
70    // set up the drawstate.
71
72    // set up the Geometry.
73    osg::Geometry* geom = new osg::Geometry;
74
75    osg::Vec3Array* coords = new osg::Vec3Array(4);
76    (*coords)[0].set(xMin,yMax,z);
77    (*coords)[1].set(xMin,yMin,z);
78    (*coords)[2].set(xMax,yMin,z);
79    (*coords)[3].set(xMax,yMax,z);
80    geom->setVertexArray(coords);
81
82    osg::Vec3Array* norms = new osg::Vec3Array(1);
83    (*norms)[0].set(0.0f,0.0f,1.0f);
84    geom->setNormalArray(norms);
85    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
86
87    osg::Vec2Array* tcoords = new osg::Vec2Array(4);
88    (*tcoords)[0].set(0.0f,1.0f);
89    (*tcoords)[1].set(0.0f,0.0f);
90    (*tcoords)[2].set(1.0f,0.0f);
91    (*tcoords)[3].set(1.0f,1.0f);
92    geom->setTexCoordArray(0,tcoords);
93   
94    osg::Vec4Array* colours = new osg::Vec4Array(1);
95    (*colours)[0].set(1.0f,1.0f,1.0,1.0f);
96    geom->setColorArray(colours);
97    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
98
99    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
100
101    return geom;
102}
103
104osg::Node* createMirroredScene(osg::Node* model)
105{
106
107    // calculate where to place the mirror according to the
108    // loaded models bounding sphere.
109    const osg::BoundingSphere& bs = model->getBound();
110
111    float width_factor = 1.5;
112    float height_factor = 0.3;
113   
114    float xMin = bs.center().x()-bs.radius()*width_factor;
115    float xMax = bs.center().x()+bs.radius()*width_factor;
116    float yMin = bs.center().y()-bs.radius()*width_factor;
117    float yMax = bs.center().y()+bs.radius()*width_factor;
118   
119    float z = bs.center().z()-bs.radius()*height_factor;
120   
121   
122    // create a textured, transparent node at the appropriate place.
123    osg::Drawable* mirror = createMirrorSurface(xMin,xMax,yMin,yMax,z);
124   
125
126    osg::MatrixTransform* rootNode = new osg::MatrixTransform;
127    rootNode->setMatrix(osg::Matrix::rotate(osg::inDegrees(45.0f),1.0f,0.0f,0.0f));
128       
129    // make sure that the global color mask exists.
130    osg::ColorMask* rootColorMask = new osg::ColorMask;
131    rootColorMask->setMask(true,true,true,true);       
132   
133    // set up depth to be inherited by the rest of the scene unless
134    // overrideen. this is overridden in bin 3.
135    osg::Depth* rootDepth = new osg::Depth;
136    rootDepth->setFunction(osg::Depth::LESS);
137    rootDepth->setRange(0.0,1.0);
138
139    osg::StateSet* rootStateSet = new osg::StateSet();       
140    rootStateSet->setAttribute(rootColorMask);
141    rootStateSet->setAttribute(rootDepth);
142
143    rootNode->setStateSet(rootStateSet);
144
145
146    // bin1  - set up the stencil values and depth for mirror.
147    {
148   
149        // set up the stencil ops so that the stencil buffer get set at
150        // the mirror plane
151        osg::Stencil* stencil = new osg::Stencil;
152        stencil->setFunction(osg::Stencil::ALWAYS,1,~0u);
153        stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::REPLACE);
154       
155        // switch off the writing to the color bit planes.
156        osg::ColorMask* colorMask = new osg::ColorMask;
157        colorMask->setMask(false,false,false,false);
158       
159        osg::StateSet* statesetBin1 = new osg::StateSet();       
160        statesetBin1->setRenderBinDetails(1,"RenderBin");
161        statesetBin1->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
162        statesetBin1->setAttributeAndModes(stencil,osg::StateAttribute::ON);
163        statesetBin1->setAttribute(colorMask);
164       
165        // set up the mirror geode.
166        osg::Geode* geode = new osg::Geode;
167        geode->addDrawable(mirror);
168        geode->setStateSet(statesetBin1);
169       
170        rootNode->addChild(geode);
171       
172    }
173
174    // bin one - draw scene without mirror or reflection, unset
175    // stencil values where scene is infront of mirror and hence
176    // occludes the mirror.
177    {       
178        osg::Stencil* stencil = new osg::Stencil;
179        stencil->setFunction(osg::Stencil::ALWAYS,0,~0u);
180        stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::REPLACE);
181
182        osg::StateSet* statesetBin2 = new osg::StateSet();       
183        statesetBin2->setRenderBinDetails(2,"RenderBin");
184        statesetBin2->setAttributeAndModes(stencil,osg::StateAttribute::ON);
185       
186
187        osg::Group* groupBin2 = new osg::Group();
188        groupBin2->setStateSet(statesetBin2);
189        groupBin2->addChild(model);
190       
191        rootNode->addChild(groupBin2);
192    }
193       
194    // bin3  - set up the depth to the furthest depth value
195    {
196   
197        // set up the stencil ops so that only operator on this mirrors stencil value.
198        osg::Stencil* stencil = new osg::Stencil;
199        stencil->setFunction(osg::Stencil::EQUAL,1,~0u);
200        stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
201       
202        // switch off the writing to the color bit planes.
203        osg::ColorMask* colorMask = new osg::ColorMask;
204        colorMask->setMask(false,false,false,false);
205
206        // set up depth so all writing to depth goes to maximum depth.
207        osg::Depth* depth = new osg::Depth;
208        depth->setFunction(osg::Depth::ALWAYS);
209        depth->setRange(1.0,1.0);
210
211        osg::StateSet* statesetBin3 = new osg::StateSet();
212        statesetBin3->setRenderBinDetails(3,"RenderBin");
213        statesetBin3->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
214        statesetBin3->setAttributeAndModes(stencil,osg::StateAttribute::ON);
215        statesetBin3->setAttribute(colorMask);
216        statesetBin3->setAttribute(depth);
217       
218        // set up the mirror geode.
219        osg::Geode* geode = new osg::Geode;
220        geode->addDrawable(mirror);
221        geode->setStateSet(statesetBin3);
222       
223        rootNode->addChild(geode);
224       
225    }
226
227    // bin4  - draw the reflection.
228    {
229   
230        // now create the 'reflection' of the loaded model by applying
231        // create a Transform which flips the loaded model about the z axis
232        // relative to the mirror node, the loadedModel is added to the
233        // Transform so now appears twice in the scene, but is shared so there
234        // is negligable memory overhead.  Also use an osg::StateSet
235        // attached to the Transform to override the face culling on the subgraph
236        // to prevert an 'inside' out view of the reflected model.
237        // set up the stencil ops so that only operator on this mirrors stencil value.
238
239
240
241        // this clip plane removes any of the scene which when mirror would
242        // poke through the mirror.  However, this clip plane should really
243        // flip sides once the eye point goes to the back of the mirror...
244        osg::ClipPlane* clipplane = new osg::ClipPlane;
245        clipplane->setClipPlane(0.0,0.0,-1.0,z);
246        clipplane->setClipPlaneNum(0);
247
248        osg::ClipNode* clipNode = new osg::ClipNode;
249        clipNode->addClipPlane(clipplane);
250
251
252        osg::StateSet* dstate = clipNode->getOrCreateStateSet();
253        dstate->setRenderBinDetails(4,"RenderBin");
254        dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OVERRIDE|osg::StateAttribute::OFF);
255
256        osg::Stencil* stencil = new osg::Stencil;
257        stencil->setFunction(osg::Stencil::EQUAL,1,~0u);
258        stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP);
259        dstate->setAttributeAndModes(stencil,osg::StateAttribute::ON);
260
261        osg::MatrixTransform* reverseMatrix = new osg::MatrixTransform;
262        reverseMatrix->setStateSet(dstate);
263        reverseMatrix->preMult(osg::Matrix::translate(0.0f,0.0f,-z)*
264                     osg::Matrix::scale(1.0f,1.0f,-1.0f)*
265                     osg::Matrix::translate(0.0f,0.0f,z));
266
267        reverseMatrix->addChild(model);
268
269        clipNode->addChild(reverseMatrix);
270
271        rootNode->addChild(clipNode);
272   
273    }
274
275
276    // bin5  - draw the textured mirror and blend it with the reflection.
277    {
278   
279        // set up depth so all writing to depth goes to maximum depth.
280        osg::Depth* depth = new osg::Depth;
281        depth->setFunction(osg::Depth::ALWAYS);
282
283        osg::Stencil* stencil = new osg::Stencil;
284        stencil->setFunction(osg::Stencil::EQUAL,1,~0u);
285        stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::ZERO);
286
287        // set up additive blending.
288        osg::BlendFunc* trans = new osg::BlendFunc;
289        trans->setFunction(osg::BlendFunc::ONE,osg::BlendFunc::ONE);
290
291        osg::StateSet* statesetBin5 = createMirrorTexturedState("Images/tank.rgb");
292
293        statesetBin5->setRenderBinDetails(5,"RenderBin");
294        statesetBin5->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
295        statesetBin5->setAttributeAndModes(stencil,osg::StateAttribute::ON);
296        statesetBin5->setAttributeAndModes(trans,osg::StateAttribute::ON);
297        statesetBin5->setAttribute(depth);
298       
299        // set up the mirror geode.
300        osg::Geode* geode = new osg::Geode;
301        geode->addDrawable(mirror);
302        geode->setStateSet(statesetBin5);
303       
304        rootNode->addChild(geode);
305
306    }
307   
308    return rootNode;
309}
310
311
312/////////////////////////////////////////////////////////////////////////////////////////////////////////
313//
314// create the viewer
315// load a model
316// decoate the model so it renders using a multipass stencil buffer technique for planar reflections.
317// release the viewer
318// run main loop.
319//
320int main( int argc, char **argv )
321{
322
323    // use an ArgumentParser object to manage the program arguments.
324    osg::ArgumentParser arguments(&argc,argv);
325   
326    // set up the usage document, in case we need to print out how to use this program.
327    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates the use multi-pass rendering, stencil buffer to create a planer reflection effect.");
328    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
329    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
330   
331
332    // construct the viewer.
333    osgProducer::Viewer viewer(arguments);
334
335    // set up the value with sensible default event handlers.
336    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
337
338    // get details on keyboard and mouse bindings used by the viewer.
339    viewer.getUsage(*arguments.getApplicationUsage());
340
341    // if user request help write it out to cout.
342    if (arguments.read("-h") || arguments.read("--help"))
343    {
344        arguments.getApplicationUsage()->write(std::cout);
345        return 1;
346    }
347
348    // any option left unread are converted into errors to write out later.
349    arguments.reportRemainingOptionsAsUnrecognized();
350
351    // report any errors if they have occured when parsing the program aguments.
352    if (arguments.errors())
353    {
354        arguments.writeErrorMessages(std::cout);
355        return 1;
356    }
357   
358    if (arguments.argc()<=1)
359    {
360        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
361        return 1;
362    }
363
364
365    // read the scene from the list of file specified commandline args.
366    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
367
368    // if no model has been successfully loaded report failure.
369    if (!loadedModel)
370    {
371        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
372        return 1;
373    }
374
375
376    // optimize the scene graph, remove rendundent nodes and state etc.
377    osgUtil::Optimizer optimizer;
378    optimizer.optimize(loadedModel.get());
379
380    // add a transform with a callback to animate the loaded model.
381    osg::ref_ptr<osg::MatrixTransform> loadedModelTransform = new osg::MatrixTransform;
382    loadedModelTransform->addChild(loadedModel.get());
383
384    osg::ref_ptr<osg::NodeCallback> nc = new osg::AnimationPathCallback(loadedModelTransform->getBound().center(),osg::Vec3(0.0f,0.0f,1.0f),osg::inDegrees(45.0f));
385    loadedModelTransform->setUpdateCallback(nc.get());
386
387
388    // finally decorate the loaded model so that it has the required multipass/bin scene graph to do the reflection effect.
389    osg::ref_ptr<osg::Node> rootNode = createMirroredScene(loadedModelTransform.get());
390
391
392    // set the scene to render
393    viewer.setSceneData(rootNode.get());
394
395    // create the windows and run the threads.
396    viewer.realize();
397
398    while( !viewer.done() )
399    {
400        // wait for all cull and draw threads to complete.
401        viewer.sync();
402
403        // update the scene by traversing it with the the update visitor which will
404        // call all node update callbacks and animations.
405        viewer.update();
406         
407        // fire off the cull and draw traversals of the scene.
408        viewer.frame();
409       
410    }
411   
412    // wait for all cull and draw threads to complete.
413    viewer.sync();
414
415    // run a clean up frame to delete all OpenGL objects.
416    viewer.cleanup_frame();
417
418    // wait for all the clean up frame to complete.
419    viewer.sync();
420   
421    return 0;
422
423}
Note: See TracBrowser for help on using the browser.