root/OpenSceneGraph/trunk/examples/osgocclusionquery/osgocclusionquery.cpp @ 7754

Revision 7754, 22.9 kB (checked in by robert, 10 years ago)

From Paul Martz, "The osgocclusionquery example contained some duplicate code that was also in the core OSG occlusion query code. This change removes the need for that."

Line 
1/* OpenSceneGraph example, osganimate.
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// This exampl demonstrates use of OcclusionQueryNode.
20//
21// In general, you use OcclusionQueryNode by simply attaching a subgraph
22// or subgraphs as children, and it performs an OpenGL oclusion query
23// to determine whether to draw the subgraphs or not.
24//
25// You can manually insert OcclusionQueryNodes at strategic locations
26// in your scene graph, or you can write a NodeVisitor to insert them
27// automatically, as this example shows.
28//
29// Run this example with no command line arguments, and it creates
30// a "stock scene" to show how OcclusionQueryNode can be used.
31//
32// Or, run this example with a model on the command line, and the
33// example uses a NodeVisitor to try to find worthwhile locations
34// for OcclusionQueryNodes in your the scene graph.
35
36
37
38#include <osg/NodeVisitor>
39#include <osg/Geode>
40#include <osg/Geometry>
41#include <osg/StateSet>
42#include <osg/StateAttribute>
43#include <osg/PolygonMode>
44#include <osg/ColorMask>
45#include <osg/PolygonOffset>
46#include <osg/Depth>
47
48#include <osgDB/ReadFile>
49#include <osgDB/WriteFile>
50
51#include <osgUtil/Optimizer>
52
53#include <osgViewer/Viewer>
54#include <osgViewer/ViewerEventHandlers>
55
56#include <osgGA/StateSetManipulator>
57
58#include <osg/OcclusionQueryNode>
59
60#include <iostream>
61#include <sstream>
62
63
64// NodeVisitors and utility functions for OcclusionQueryNode
65
66// Use this visitor to insert OcclusionQueryNodes (OQNs) in the
67//   visited subgraph. Only one OQN will test any particular node
68//   (no nesting). See also OcclusionQueryNonFlatVisitor.
69class OcclusionQueryVisitor : public osg::NodeVisitor
70{
71public:
72    OcclusionQueryVisitor();
73    virtual ~OcclusionQueryVisitor();
74
75    // Specify the vertex count threshold for performing occlusion
76    //   query tests. Nodes in the scene graph whose total child geometry
77    //   contains fewer vertices than the specified threshold will
78    //   never be tested, just drawn. (In fact, they will br treated as
79    //   potential occluders and rendered first in front-to-back order.)
80    void setOccluderThreshold( int vertices );
81    int getOccluderThreshold() const;
82
83    virtual void apply( osg::OcclusionQueryNode& oqn );
84    virtual void apply( osg::Group& group );
85    virtual void apply( osg::Geode& geode );
86
87protected:
88    void addOQN( osg::Node& node );
89
90    // When an OQR creates all OQNs and each OQN shares the same OQC,
91    //   these methods are used to uniquely name all OQNs. Handy
92    //   for debugging.
93    std::string getNextOQNName();
94    int getNameIdx() const { return _nameIdx; }
95
96    osg::ref_ptr<osg::StateSet> _state;
97    osg::ref_ptr<osg::StateSet> _debugState;
98
99    unsigned int _nameIdx;
100
101    int _occluderThreshold;
102};
103
104// Find all OQNs in the visited scene graph and set their visibility threshold.
105class VisibilityThresholdVisitor : public osg::NodeVisitor
106{
107public:
108    VisibilityThresholdVisitor( unsigned int threshold=500 )
109      : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
110        _visThreshold( threshold ) {}
111    virtual ~VisibilityThresholdVisitor() {}
112
113    virtual void apply( osg::OcclusionQueryNode& oqn );
114
115protected:
116    unsigned int _visThreshold;
117};
118
119// Find all OQNs in the visited scene graph and set the number of frames
120//   between queries.
121class QueryFrameCountVisitor : public osg::NodeVisitor
122{
123public:
124    QueryFrameCountVisitor( int count=5 )
125      : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
126        _count( count ) {}
127    virtual ~QueryFrameCountVisitor() {}
128
129    virtual void apply( osg::OcclusionQueryNode& oqn );
130
131protected:
132    unsigned int _count;
133};
134
135// Find all OQNs in the visited scene graph and enable or disable queries..
136class EnableQueryVisitor : public osg::NodeVisitor
137{
138public:
139    EnableQueryVisitor( bool enable=true )
140      : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
141        _enabled( enable ) {}
142    virtual ~EnableQueryVisitor() {}
143
144    virtual void apply( osg::OcclusionQueryNode& oqn );
145
146protected:
147    bool _enabled;
148};
149
150// Find all OQNs in the visited scene graph and enable or disable the
151//   debug bounding volume display.
152class DebugDisplayVisitor : public osg::NodeVisitor
153{
154public:
155    DebugDisplayVisitor( bool debug=true )
156      : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
157        _debug( debug ) {}
158    virtual ~DebugDisplayVisitor() {}
159
160    virtual void apply( osg::OcclusionQueryNode& oqn );
161
162protected:
163    bool _debug;
164};
165
166// Remove all OQNs from the visited scene graph.
167class RemoveOcclusionQueryVisitor : public osg::NodeVisitor
168{
169public:
170    RemoveOcclusionQueryVisitor();
171    virtual ~RemoveOcclusionQueryVisitor();
172
173    virtual void apply( osg::OcclusionQueryNode& oqn );
174
175protected:
176};
177
178// Gather statistics about OQN performance in the visited scene graph.
179class StatisticsVisitor : public osg::NodeVisitor
180{
181public:
182    StatisticsVisitor( osg::NodeVisitor::TraversalMode mode=osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN );
183    virtual ~StatisticsVisitor();
184
185    virtual void apply( osg::OcclusionQueryNode& oqn );
186
187    void reset();
188    unsigned int getNumOQNs() const;
189    unsigned int getNumPassed() const;
190
191protected:
192    unsigned int _numOQNs;
193    unsigned int _numPassed;
194};
195
196
197unsigned int countGeometryVertices( osg::Geometry* geom )
198{
199    if (!geom->getVertexArray())
200        return 0;
201
202    // TBD This will eventually iterate over the PrimitiveSets and total the
203    //   number of vertices actually used. But for now, it just returns the
204    //   size of the vertex array.
205
206    return geom->getVertexArray()->getNumElements();
207}
208
209class VertexCounter : public osg::NodeVisitor
210{
211public:
212    VertexCounter( int limit )
213      : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
214        _limit( limit ),
215        _total( 0 ) {}
216    ~VertexCounter() {}
217
218    int getTotal() { return _total; }
219    bool exceeded() const { return _total > _limit; }
220    void reset() { _total = 0; }
221
222    virtual void apply( osg::Node& node )
223    {
224        // Check for early abort. If out total already exceeds the
225        //   max number of vertices, no need to traverse further.
226        if (exceeded())
227            return;
228        traverse( node );
229    }
230
231    virtual void apply( osg::Geode& geode )
232    {
233        // Possible early abort.
234        if (exceeded())
235            return;
236
237        unsigned int i;
238        for( i = 0; i < geode.getNumDrawables(); i++ )
239        {
240            osg::Geometry* geom = dynamic_cast<osg::Geometry *>(geode.getDrawable(i));
241            if( !geom )
242                continue;
243
244            _total += countGeometryVertices( geom );
245
246            if (_total > _limit)
247                break;
248        }
249    }
250
251protected:
252    int _limit;
253    int _total;
254};
255
256
257
258OcclusionQueryVisitor::OcclusionQueryVisitor()
259  : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
260    _nameIdx( 0 ),
261    _occluderThreshold( 5000 )
262{
263    // Create a dummy OcclusionQueryNode just so we can get its state.
264    // We'll then share that state between all OQNs we add to the visited scene graph.
265    osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
266
267    osg::StateSet* ss( NULL );
268    osg::StateSet* ssDebug( NULL );
269    oqn->getQueryStateSets( ss, ssDebug );
270    _state = ss;
271    _debugState = ssDebug;
272}
273
274OcclusionQueryVisitor::~OcclusionQueryVisitor()
275{
276    osg::notify( osg::INFO ) <<
277        "osgOQ: OcclusionQueryVisitor: Added " << getNameIdx() <<
278        " OQNodes." << std::endl;
279}
280
281void
282OcclusionQueryVisitor::setOccluderThreshold( int vertices )
283{
284    _occluderThreshold = vertices;
285}
286int
287OcclusionQueryVisitor::getOccluderThreshold() const
288{
289    return _occluderThreshold;
290}
291
292void
293OcclusionQueryVisitor::apply( osg::OcclusionQueryNode& oqn )
294{
295    // A subgraph is already under osgOQ control.
296    // Don't traverse further.
297    return;
298}
299
300void
301OcclusionQueryVisitor::apply( osg::Group& group )
302{
303    if (group.getNumParents() == 0)
304    {
305        // Can't add an OQN above a root node.
306        traverse( group );
307        return;
308    }
309
310    int preTraverseOQNCount = getNameIdx();
311    traverse( group );
312
313    if (getNameIdx() > preTraverseOQNCount)
314        // A least one OQN was added below the current node.
315        //   Don't add one here to avoid hierarchical nesting.
316        return;
317   
318    // There are no OQNs below this group. If the vertex
319    //   count exceeds the threshold, add an OQN here.
320    addOQN( group );
321}
322
323void
324OcclusionQueryVisitor::apply( osg::Geode& geode )
325{
326    if (geode.getNumParents() == 0)
327    {
328        // Can't add an OQN above a root node.
329        traverse( geode );
330        return;
331    }
332
333    addOQN( geode );
334}
335
336void
337OcclusionQueryVisitor::addOQN( osg::Node& node )
338{
339    VertexCounter vc( _occluderThreshold );
340    node.accept( vc );
341    if (vc.exceeded())
342    {
343        // Insert OQN(s) above this node.
344        unsigned int np = node.getNumParents();
345        while (np--)
346        {
347            osg::Group* parent = dynamic_cast<osg::Group*>( node.getParent( np ) );
348            if (parent != NULL)
349            {
350                osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode();
351                oqn->addChild( &node );
352                parent->replaceChild( &node, oqn.get() );
353
354                oqn->setName( getNextOQNName() );
355                // Set all OQNs to use the same query StateSets (instead of multiple copies
356                //   of the same StateSet) for efficiency.
357                oqn->setQueryStateSets( _state.get(), _debugState.get() );
358            }
359        }
360    }
361}
362
363std::string
364OcclusionQueryVisitor::getNextOQNName()
365{
366    std::ostringstream ostr;
367    ostr << "OQNode_" << _nameIdx++;
368    return ostr.str();
369}
370
371
372
373
374//
375void
376VisibilityThresholdVisitor::apply( osg::OcclusionQueryNode& oqn )
377{
378    oqn.setVisibilityThreshold( _visThreshold );
379
380    traverse( oqn );
381}
382
383void
384QueryFrameCountVisitor::apply( osg::OcclusionQueryNode& oqn )
385{
386    oqn.setQueryFrameCount( _count );
387
388    traverse( oqn );
389}
390
391void
392EnableQueryVisitor::apply( osg::OcclusionQueryNode& oqn )
393{
394    oqn.setQueriesEnabled( _enabled );
395
396    traverse( oqn );
397}
398
399
400void 
401DebugDisplayVisitor::apply( osg::OcclusionQueryNode& oqn )
402{
403    oqn.setDebugDisplay( _debug );
404
405    traverse( oqn );
406}
407
408
409RemoveOcclusionQueryVisitor::RemoveOcclusionQueryVisitor()
410  : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
411{
412}
413
414RemoveOcclusionQueryVisitor::~RemoveOcclusionQueryVisitor()
415{
416}
417
418void
419RemoveOcclusionQueryVisitor::apply( osg::OcclusionQueryNode& oqn )
420{
421    if (oqn.getNumParents() == 0)
422    {
423        // Even if this is an OQN, can't delete it because it's the root.
424        traverse( oqn );
425        return;
426    }
427
428    osg::ref_ptr<osg::OcclusionQueryNode> oqnPtr = &oqn;
429
430    unsigned int np = oqn.getNumParents();
431    while (np--)
432    {
433        osg::Group* parent = dynamic_cast<osg::Group*>( oqn.getParent( np ) );
434        if (parent != NULL)
435        {
436            // Remove OQN from parent.
437            parent->removeChild( oqnPtr.get() );
438
439            // Add OQN's children to parent.
440            unsigned int nc = oqn.getNumChildren();
441            while (nc--)
442                parent->addChild( oqn.getChild( nc ) );
443        }
444    }
445}
446
447
448
449StatisticsVisitor::StatisticsVisitor( osg::NodeVisitor::TraversalMode mode )
450  : osg::NodeVisitor( mode ),
451    _numOQNs( 0 ),
452    _numPassed( 0 )
453{
454}
455
456StatisticsVisitor::~StatisticsVisitor()
457{
458}
459
460void
461StatisticsVisitor::apply( osg::OcclusionQueryNode& oqn )
462{
463    _numOQNs++;
464    if (oqn.getPassed())
465        _numPassed++;
466
467    traverse( oqn );
468}
469
470void
471StatisticsVisitor::reset()
472{
473    _numOQNs = _numPassed = 0;
474}
475
476unsigned int
477StatisticsVisitor::getNumOQNs() const
478{
479    return _numOQNs;
480}
481unsigned int
482StatisticsVisitor::getNumPassed() const
483{
484    return _numPassed;
485}
486
487// End NodeVisitors
488
489
490
491// KetHandler --
492// Allow user to do interesting things with an
493// OcclusionQueryNode-enabled scene graph at run time.
494class KeyHandler : public osgGA::GUIEventHandler
495{
496public:
497    KeyHandler( osg::Node& node )
498      : _node( node ),
499        _enable( true ),
500        _debug( false )
501    {}
502
503    bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& )
504    {
505        switch( ea.getEventType() )
506        {
507            case(osgGA::GUIEventAdapter::KEYUP):
508            {
509                if (ea.getKey()==osgGA::GUIEventAdapter::KEY_F6)
510                {
511                    // F6 -- Toggle osgOQ testing.
512                    _enable = !_enable;
513                    EnableQueryVisitor eqv( _enable );
514                    _node.accept( eqv );
515                    return true;
516                }
517                else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_F7)
518                {
519                    // F7 -- Toggle display of OQ test bounding volumes
520                    _debug = !_debug;
521                    DebugDisplayVisitor ddv( _debug );
522                    _node.accept( ddv );
523                    return true;
524                }
525                else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_F8)
526                {
527                    // F8 -- Gether stats and display
528                    StatisticsVisitor sv;
529                    _node.accept( sv );
530                    std::cout << "osgOQ: Stats: numOQNs " << sv.getNumOQNs() << ", numPased " << sv.getNumPassed() << std::endl;
531                    return true;
532                }
533                else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_F9)
534                {
535                    // F9 -- Remove all OcclusionQueryNodes
536                    RemoveOcclusionQueryVisitor roqv;
537                    _node.accept( roqv );
538                    return true;
539                }
540                else if (ea.getKey()=='o')
541                {
542                    if (osgDB::writeNodeFile( _node, "saved_model.osg" ))
543                        osg::notify( osg::ALWAYS ) << "osgOQ: Wrote scene graph to \"saved_model.osg\"" << std::endl;
544                    else
545                        osg::notify( osg::ALWAYS ) << "osgOQ: Wrote failed for \"saved_model.osg\"" << std::endl;
546                    return true;
547                }
548                return false;
549            }
550            default:
551                break;
552        }
553        return false;
554    }
555
556    osg::Node& _node;
557
558    bool _enable, _debug;
559};
560
561// Create a cube with one side missing. This makes a great simple occluder.
562osg::ref_ptr<osg::Node>
563createBox()
564{
565    osg::ref_ptr<osg::Geode> box = new osg::Geode;
566
567    osg::StateSet* state = box->getOrCreateStateSet();
568    osg::PolygonMode* pm = new osg::PolygonMode(
569        osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL );
570    state->setAttributeAndModes( pm,
571        osg::StateAttribute::ON | osg::StateAttribute::PROTECTED );
572
573    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
574    osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
575    geom->setVertexArray( v.get() );
576
577    {
578        const float x( 0.f );
579        const float y( 0.f );
580        const float z( 0.f );
581        const float r( 1.1f );
582
583        v->push_back( osg::Vec3( x-r, y-r, z-r ) ); //left -X
584        v->push_back( osg::Vec3( x-r, y-r, z+r ) );
585        v->push_back( osg::Vec3( x-r, y+r, z+r ) );
586        v->push_back( osg::Vec3( x-r, y+r, z-r ) );
587
588        v->push_back( osg::Vec3( x+r, y-r, z+r ) ); //right +X
589        v->push_back( osg::Vec3( x+r, y-r, z-r ) );
590        v->push_back( osg::Vec3( x+r, y+r, z-r ) );
591        v->push_back( osg::Vec3( x+r, y+r, z+r ) );
592
593        v->push_back( osg::Vec3( x-r, y-r, z-r ) ); // bottom -Z
594        v->push_back( osg::Vec3( x-r, y+r, z-r ) );
595        v->push_back( osg::Vec3( x+r, y+r, z-r ) );
596        v->push_back( osg::Vec3( x+r, y-r, z-r ) );
597
598        v->push_back( osg::Vec3( x-r, y-r, z+r ) ); // top +Z
599        v->push_back( osg::Vec3( x+r, y-r, z+r ) );
600        v->push_back( osg::Vec3( x+r, y+r, z+r ) );
601        v->push_back( osg::Vec3( x-r, y+r, z+r ) );
602
603        v->push_back( osg::Vec3( x-r, y+r, z-r ) ); // back +Y
604        v->push_back( osg::Vec3( x-r, y+r, z+r ) );
605        v->push_back( osg::Vec3( x+r, y+r, z+r ) );
606        v->push_back( osg::Vec3( x+r, y+r, z-r ) );
607    }
608
609    osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
610    geom->setColorArray( c.get() );
611    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
612    c->push_back( osg::Vec4( 0.f, 1.f, 1.f, 1.f ) );
613
614    osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
615    geom->setNormalArray( n.get() );
616    geom->setNormalBinding( osg::Geometry::BIND_PER_PRIMITIVE );
617    n->push_back( osg::Vec3( -1.f, 0.f, 0.f ) );
618    n->push_back( osg::Vec3( 1.f, 0.f, 0.f ) );
619    n->push_back( osg::Vec3( 0.f, 0.f, -1.f ) );
620    n->push_back( osg::Vec3( 0.f, 0.f, 1.f ) );
621    n->push_back( osg::Vec3( 0.f, 1.f, 0.f ) );
622
623    geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 20 ) );
624    box->addDrawable( geom.get() );
625
626    return box.get();
627}
628
629// Make a Geometry that renders slow intentionally.
630// To make sure it renders slow, we do the following:
631//  * Disable display lists
632//  * Force glBegin/glEnd slow path
633//  * Lots of vertices and color data per vertex
634//  * No vertex sharing
635osg::ref_ptr<osg::Node>
636createRandomTriangles( unsigned int num )
637{
638    osg::ref_ptr<osg::Geode> tris = new osg::Geode;
639
640    osg::StateSet* state = tris->getOrCreateStateSet();
641    osg::StateSet* ss = tris->getOrCreateStateSet();
642    // Force wireframe. Many gfx cards handle this poorly.
643    osg::PolygonMode* pm = new osg::PolygonMode(
644        osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
645    ss->setAttributeAndModes( pm, osg::StateAttribute::ON |
646        osg::StateAttribute::PROTECTED);
647    ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF |
648        osg::StateAttribute::PROTECTED);
649
650    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
651    // Disable display lists to decrease performance.
652    geom->setUseDisplayList( false );
653
654    osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
655    geom->setVertexArray( v.get() );
656    v->resize( num*3 );
657
658    unsigned int i;
659    srand( 0 );
660#define RAND_NEG1_TO_1 ( ((rand()%20)-10)*.1 )
661    for (i=0; i<num; i++)
662    {
663        osg::Vec3& v0 = (*v)[ i*3+0 ];
664        osg::Vec3& v1 = (*v)[ i*3+1 ];
665        osg::Vec3& v2 = (*v)[ i*3+2 ];
666        v0 = osg::Vec3( RAND_NEG1_TO_1, RAND_NEG1_TO_1, RAND_NEG1_TO_1 );
667        v1 = osg::Vec3( RAND_NEG1_TO_1, RAND_NEG1_TO_1, RAND_NEG1_TO_1 );
668        v2 = osg::Vec3( RAND_NEG1_TO_1, RAND_NEG1_TO_1, RAND_NEG1_TO_1 );
669    }
670
671    osg::ref_ptr<osg::Vec4Array> c = new osg::Vec4Array;
672    geom->setColorArray( c.get() );
673    // Bind per primitive to force slow glBegin/glEnd path.
674    geom->setColorBinding( osg::Geometry::BIND_PER_PRIMITIVE );
675    c->resize( num );
676
677#define RAND_0_TO_1 ( (rand()%10)*.1 )
678    for (i=0; i<num; i++)
679    {
680        osg::Vec4& c0 = (*c)[ i ];
681        c0 = osg::Vec4( RAND_0_TO_1, RAND_0_TO_1, RAND_0_TO_1, 1. );
682    }
683
684    geom->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLES, 0, num*3 ) );
685    tris->addDrawable( geom.get() );
686
687    return tris.get();
688}
689
690// Create the stock scene:
691// Top level Group
692//   Geode (simple occluder
693//   OcclusionQueryNode
694//     Geode with complex, slow geometry.
695osg::ref_ptr<osg::Node>
696createStockScene()
697{
698    // Create a simple box occluder
699    osg::ref_ptr<osg::Group> root = new osg::Group();
700    root->addChild( createBox().get() );
701
702    // Create a complex mess of triangles as a child below an
703    //   OcclusionQueryNode. The OQN will ensure that the
704    //   subgraph isn't rendered when it's not visible.
705    osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
706    oqn->addChild( createRandomTriangles( 20000 ).get() );
707    root->addChild( oqn.get() );
708
709    return root.get();
710}
711
712
713int main(int argc, char** argv)
714{
715    // use an ArgumentParser object to manage the program arguments.
716    osg::ArgumentParser arguments(&argc,argv);
717
718    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
719    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" demonstrates OpenGL occlusion query in OSG using the OcclusionQueryNode.");
720    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] [filename(s)]");
721    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display command line parameters");
722
723    // if user request help write it out to cout.
724    if (arguments.read("-h") || arguments.read("--help"))
725    {
726        arguments.getApplicationUsage()->write(std::cout, osg::ApplicationUsage::COMMAND_LINE_OPTION);
727        return 1;
728    }
729
730    // report any errors if they have occurred when parsing the program arguments.
731    if (arguments.errors())
732    {
733        arguments.writeErrorMessages(std::cout);
734        return 1;
735    }
736   
737    osgViewer::Viewer viewer( arguments );
738
739    // add the state manipulator
740    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
741   
742    // add the stats handler
743    viewer.addEventHandler(new osgViewer::StatsHandler);
744
745    // add the help handler
746    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
747
748
749    // load the specified model
750    osg::ref_ptr<osg::Node> root = osgDB::readNodeFiles( arguments );
751    if (!root)
752    {
753        std::cout << arguments.getApplicationName() <<": No files specified, or can't load them." << std::endl;
754        root = createStockScene().get();
755        if (!root)
756        {
757            std::cout << arguments.getApplicationName() <<": Failed to create stock scene." << std::endl;
758            return 1;
759        }
760        std::cout << "Using stock scene instead." << std::endl;
761    }
762    else
763    {
764        // Run a NodeVisitor to insert OcclusionQueryNodes in the scene graph.
765        OcclusionQueryVisitor oqv;
766        root->accept( oqv );
767    }
768
769    bool optimize = arguments.read( "--opt" );
770
771    // any option left unread are converted into errors to write out later.
772    arguments.reportRemainingOptionsAsUnrecognized();
773
774    // report any errors if they have occurred when parsing the program arguments.
775    if (arguments.errors())
776    {
777        arguments.writeErrorMessages(std::cout);
778        return 1;
779    }
780
781
782    // optimize the scene graph, remove redundant nodes and state etc.
783    if (optimize)
784    {
785        osgUtil::Optimizer optimizer;
786        optimizer.optimize( root.get() );
787    }
788
789    viewer.setSceneData( root.get() );
790
791    KeyHandler* kh = new KeyHandler( *root );
792    viewer.addEventHandler( kh );
793
794    return viewer.run();
795}
Note: See TracBrowser for help on using the browser.