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

Revision 6916, 13.5 kB (checked in by robert, 7 years ago)

From Martin Lavery and Robert Osfield, added fallbacks for when now command line args are provided.

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