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

Revision 3755, 15.1 kB (checked in by robert, 9 years ago)

Property API clean to smooth the task of generating wrappers.

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