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

Revision 13574, 13.7 kB (checked in by robert, 13 hours ago)

From Jason Beverage, "It looks like the Callback header got accidentally removed from the CMakeLists.txt in the submission yesterday for the geometry instancing example."

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