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

Revision 7731, 25.0 kB (checked in by robert, 6 years ago)

From Paul Martz, Introduced osg::OcclusionQueryNode? with support for OpenGL occlusion query extension

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