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

Revision 7754, 22.9 kB (checked in by robert, 6 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.