root/OpenSceneGraph/trunk/examples/osgforest/osgforest.cpp @ 2316

Revision 2316, 16.6 kB (checked in by robert, 11 years ago)

Improves to CullStack?.

From M.Grngr. options support for f=switching off internal imagery in .ive files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/Geode>
2#include <osg/ShapeDrawable>
3#include <osg/Material>
4#include <osg/Texture2D>
5#include <osg/Billboard>
6#include <osg/AlphaFunc>
7#include <osg/BlendFunc>
8#include <osg/StateSet>
9#include <osg/Geometry>
10#include <osg/MatrixTransform>
11#include <osg/Switch>
12
13#include <osgProducer/Viewer>
14
15#include <osgDB/ReadFile>
16#include <osgUtil/IntersectVisitor>
17#include <osgUtil/SmoothingVisitor>
18
19#include <osg/Math>
20
21// for the grid data..
22#include "../osghangglide/terrain_coords.h"
23
24// class to create the forest and manage the movement between various techniques.
25class ForestTechniqueManager : public osg::Referenced
26{
27public:
28
29    ForestTechniqueManager() {}
30
31    class Tree : public osg::Referenced
32    {
33    public:
34
35        Tree():
36            _color(255,255,255,255),
37            _width(1.0f),
38            _height(1.0f),
39            _type(0) {}
40
41        Tree(const osg::Vec3& position, const osg::UByte4& color, float width, float height, unsigned int type):
42            _position(position),
43            _color(color),
44            _width(width),
45            _height(height),
46            _type(type) {}
47
48        osg::Vec3       _position;
49        osg::UByte4     _color;
50        float           _width;
51        float           _height;
52        unsigned int    _type;
53    };
54
55    typedef std::vector< osg::ref_ptr<Tree> > TreeList;
56
57    float random(float min,float max) { return min + (max-min)*(float)rand()/(float)RAND_MAX; }
58    int random(int min,int max) { return min + (int)((float)(max-min)*(float)rand()/(float)RAND_MAX); }
59
60    osg::Geode* createTerrain(const osg::Vec3& origin, const osg::Vec3& size);
61
62    void createTreeList(osg::Node* terrain,const osg::Vec3& origin, const osg::Vec3& size,unsigned int numTreesToCreate,TreeList& trees);
63
64    osg::Geometry* createSprite( float w, float h, osg::UByte4 color );
65
66    osg::Geometry* createOrthogonalQuads( const osg::Vec3& pos, float w, float h, osg::UByte4 color );
67
68    osg::Node* createScene();
69   
70    void advanceToNextTechnique(int delta=1)
71    {
72        if (_techniqueSwitch.valid())
73        {
74            _currentTechnique = (_currentTechnique + delta)%_techniqueSwitch->getNumChildren();
75            _techniqueSwitch->setSingleChildOn(_currentTechnique);
76        }
77    }
78   
79    osg::ref_ptr<osg::Switch>   _techniqueSwitch;
80    int                         _currentTechnique;
81   
82
83};
84
85// event handler to capture keyboard events and use them to advance the technique used for rendering
86class TechniqueEventHandler : public osgGA::GUIEventHandler, public osg::NodeCallback
87{
88public:
89
90    TechniqueEventHandler(ForestTechniqueManager* ttm=0) { _ForestTechniqueManager = ttm; }
91   
92    META_Object(osgforestApp,TechniqueEventHandler);
93
94    virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }
95
96    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
97   
98    virtual void getUsage(osg::ApplicationUsage& usage) const;
99
100protected:
101
102    ~TechniqueEventHandler() {}
103   
104    TechniqueEventHandler(const TechniqueEventHandler&,const osg::CopyOp&) {}
105   
106    osg::ref_ptr<ForestTechniqueManager> _ForestTechniqueManager;
107
108       
109};
110
111bool TechniqueEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&)
112{
113    switch(ea.getEventType())
114    {
115        case(osgGA::GUIEventAdapter::KEYDOWN):
116        {
117            if (ea.getKey()=='n' ||
118                ea.getKey()==osgGA::GUIEventAdapter::KEY_Right ||
119                ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right)
120            {
121                _ForestTechniqueManager->advanceToNextTechnique(1);
122                return true;
123            }
124            else if (ea.getKey()=='p' ||
125                     ea.getKey()==osgGA::GUIEventAdapter::KEY_Left ||
126                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left)
127            {
128                _ForestTechniqueManager->advanceToNextTechnique(-1);
129                return true;
130            }
131            return false;
132        }
133
134        default:
135            return false;
136    }
137}
138
139void TechniqueEventHandler::getUsage(osg::ApplicationUsage& usage) const
140{
141    usage.addKeyboardMouseBinding("n or Left Arrow","Advance to next technique");
142    usage.addKeyboardMouseBinding("p or Right Array","Move to previous technique");
143}
144
145
146osg::Geode* ForestTechniqueManager::createTerrain(const osg::Vec3& origin, const osg::Vec3& size)
147{
148    osg::Geode* geode = new osg::Geode();
149
150    // ---------------------------------------
151    // Set up a StateSet to texture the objects
152    // ---------------------------------------
153    osg::StateSet* stateset = new osg::StateSet();
154
155    osg::Image* image = osgDB::readImageFile("Images/lz.rgb");
156    if (image)
157    {
158        osg::Texture2D* texture = new osg::Texture2D;
159        texture->setImage(image);
160        stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
161    }
162   
163    geode->setStateSet( stateset );
164   
165    unsigned int numColumns = 38;
166    unsigned int numRows = 39;
167    unsigned int r;
168    unsigned int c;
169
170    // compute z range of z values of grid data so we can scale it.
171    float min_z = FLT_MAX;
172    float max_z = -FLT_MAX;
173    for(r=0;r<numRows;++r)
174    {
175        for(c=0;c<numColumns;++c)
176        {
177            min_z = osg::minimum(min_z,vertex[r+c*numRows][2]);
178            max_z = osg::maximum(max_z,vertex[r+c*numRows][2]);
179        }
180    }
181   
182    float scale_z = size.z()/(max_z-min_z);
183
184
185    bool createGrid = false;
186    if (createGrid)
187    {
188
189        osg::Grid* grid = new osg::Grid;
190        grid->allocateGrid(numColumns,numRows);
191        grid->setOrigin(origin);
192        grid->setXInterval(size.x()/(float)(numColumns-1));
193        grid->setYInterval(size.y()/(float)(numRows-1));
194
195        for(r=0;r<numRows;++r)
196        {
197            for(c=0;c<numColumns;++c)
198            {
199                grid->setHeight(c,r,(vertex[r+c*numRows][2]-min_z)*scale_z);
200            }
201        }
202       
203        geode->addDrawable(new osg::ShapeDrawable(grid));
204    }
205    else
206    {
207        osg::Geometry* geometry = new osg::Geometry;
208       
209        osg::Vec3Array& v = *(new osg::Vec3Array(numColumns*numRows));
210        osg::Vec2Array& t = *(new osg::Vec2Array(numColumns*numRows));
211        osg::UByte4Array& color = *(new osg::UByte4Array(1));
212       
213        color[0].set(255,255,255,255);
214
215        float rowCoordDelta = size.y()/(float)(numRows-1);
216        float columnCoordDelta = size.x()/(float)(numColumns-1);
217       
218        float rowTexDelta = 1.0f/(float)(numRows-1);
219        float columnTexDelta = 1.0f/(float)(numColumns-1);
220
221        osg::Vec3 pos = origin;
222        osg::Vec2 tex(0.0f,0.0f);
223        int vi=0;
224        for(r=0;r<numRows;++r)
225        {
226            pos.x() = origin.x();
227            tex.x() = 0.0f;
228            for(c=0;c<numColumns;++c)
229            {
230                v[vi].set(pos.x(),pos.y(),pos.z()+(vertex[r+c*numRows][2]-min_z)*scale_z);
231                t[vi].set(tex.x(),tex.y());
232                pos.x()+=columnCoordDelta;
233                tex.x()+=columnTexDelta;
234                ++vi;
235            }
236            pos.y() += rowCoordDelta;
237            tex.y() += rowTexDelta;
238        }
239       
240        geometry->setVertexArray(&v);
241        geometry->setColorArray(&color);
242        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
243        geometry->setTexCoordArray(0,&t);
244       
245        for(r=0;r<numRows-1;++r)
246        {
247            osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numColumns));
248            geometry->addPrimitiveSet(&drawElements);
249            int ei=0;
250            for(c=0;c<numColumns;++c)
251            {
252                drawElements[ei++] = (r+1)*numColumns+c;
253                drawElements[ei++] = (r)*numColumns+c;
254            }
255        }
256       
257        geode->addDrawable(geometry);
258       
259        osgUtil::SmoothingVisitor sv;
260        sv.smooth(*geometry);
261    }
262   
263    return geode;
264}
265
266void ForestTechniqueManager::createTreeList(osg::Node* terrain,const osg::Vec3& origin, const osg::Vec3& size,unsigned int numTreesToCreate,TreeList& trees)
267{
268
269    float max_TreeHeight = sqrtf(size.length2()/(float)numTreesToCreate);
270    float max_TreeWidth = max_TreeHeight*0.5f;
271   
272    float min_TreeHeight = max_TreeHeight*0.3f;
273    float min_TreeWidth = min_TreeHeight*0.5f;
274
275    trees.reserve(trees.size()+numTreesToCreate);
276
277
278    for(unsigned int i=0;i<numTreesToCreate;++i)
279    {
280        Tree* tree = new Tree;
281        tree->_position.set(random(origin.x(),origin.x()+size.x()),random(origin.y(),origin.y()+size.y()),origin.z());
282        tree->_color.set(random(128,255),random(128,255),random(128,255),255);
283        tree->_width = random(min_TreeWidth,max_TreeWidth);
284        tree->_height = random(min_TreeHeight,max_TreeHeight);
285        tree->_type = 0;
286       
287        if (terrain)
288        {
289            osgUtil::IntersectVisitor iv;
290            osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
291
292            segDown->set(tree->_position,tree->_position+osg::Vec3(0.0f,0.0f,size.z()));
293            iv.addLineSegment(segDown.get());
294           
295            terrain->accept(iv);
296
297            if (iv.hits())
298            {
299                osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
300                if (!hitList.empty())
301                {
302                    osg::Vec3 ip = hitList.front().getWorldIntersectPoint();
303                    osg::Vec3 np = hitList.front().getWorldIntersectNormal();
304                    tree->_position = ip;
305                }
306            }
307        }
308       
309        trees.push_back(tree);
310    }
311}
312
313osg::Geometry* ForestTechniqueManager::createSprite( float w, float h, osg::UByte4 color )
314{
315    // set up the coords
316    osg::Vec3Array& v = *(new osg::Vec3Array(4));
317    osg::Vec2Array& t = *(new osg::Vec2Array(4));
318    osg::UByte4Array& c = *(new osg::UByte4Array(1));
319
320    v[0].set(-w*0.5f,0.0f,0.0f);
321    v[1].set( w*0.5f,0.0f,0.0f);
322    v[2].set( w*0.5f,0.0f,h);
323    v[3].set(-w*0.5f,0.0f,h);
324
325    c[0] = color;
326
327    t[0].set(0.0f,0.0f);
328    t[1].set(1.0f,0.0f);
329    t[2].set(1.0f,1.0f);
330    t[3].set(0.0f,1.0f);
331
332    osg::Geometry *geom = new osg::Geometry;
333
334    geom->setVertexArray( &v );
335
336    geom->setTexCoordArray( 0, &t );
337
338    geom->setColorArray( &c );
339    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
340
341    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4) );
342
343    return geom;
344}
345
346osg::Geometry* ForestTechniqueManager::createOrthogonalQuads( const osg::Vec3& pos, float w, float h, osg::UByte4 color )
347{
348    // set up the coords
349    osg::Vec3Array& v = *(new osg::Vec3Array(8));
350    osg::Vec2Array& t = *(new osg::Vec2Array(8));
351    osg::UByte4Array& c = *(new osg::UByte4Array(1));
352   
353    float rotation = random(0.0f,osg::PI/2.0f);
354    float sw = sinf(rotation)*w*0.5f;
355    float cw = cosf(rotation)*w*0.5f;
356
357    v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f);
358    v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f);
359    v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h);
360    v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h);
361
362    v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f);
363    v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f);
364    v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h);
365    v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h);
366
367    c[0] = color;
368
369    t[0].set(0.0f,0.0f);
370    t[1].set(1.0f,0.0f);
371    t[2].set(1.0f,1.0f);
372    t[3].set(0.0f,1.0f);
373
374    t[4].set(0.0f,0.0f);
375    t[5].set(1.0f,0.0f);
376    t[6].set(1.0f,1.0f);
377    t[7].set(0.0f,1.0f);
378
379    osg::Geometry *geom = new osg::Geometry;
380
381    geom->setVertexArray( &v );
382
383    geom->setTexCoordArray( 0, &t );
384
385    geom->setColorArray( &c );
386    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
387
388    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
389
390    return geom;
391}
392
393osg::Node* ForestTechniqueManager::createScene()
394{
395    osg::Vec3 origin(0.0f,0.0f,0.0f);
396    osg::Vec3 size(1000.0f,1000.0f,200.0f);
397    unsigned int numTreesToCreates = 10000;
398
399    osg::ref_ptr<osg::Node> terrain = createTerrain(origin,size);
400   
401    TreeList trees;
402    createTreeList(terrain.get(),origin,size,numTreesToCreates,trees);
403   
404    osg::Texture2D *tex = new osg::Texture2D;
405    tex->setImage(osgDB::readImageFile("Images/tree0.rgba"));
406
407    osg::StateSet *dstate = new osg::StateSet;
408    {   
409        dstate->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
410        dstate->setTextureAttribute(0, new osg::TexEnv );
411
412        dstate->setAttributeAndModes( new osg::BlendFunc, osg::StateAttribute::ON );
413
414        osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
415        alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
416        dstate->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
417
418        dstate->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
419
420        dstate->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
421    }
422   
423
424    _techniqueSwitch = new osg::Switch;
425   
426    {
427        osg::Billboard* billboard = new osg::Billboard;
428        billboard->setStateSet(dstate);
429
430        for(TreeList::iterator itr=trees.begin();
431            itr!=trees.end();
432            ++itr)
433        {
434            Tree& tree = **itr;
435            billboard->addDrawable(createSprite(tree._width,tree._height,tree._color),tree._position);   
436        }
437   
438        _techniqueSwitch->addChild(billboard);
439    }
440
441    {
442        osg::Geode* geode = new osg::Geode;
443        geode->setStateSet(dstate);
444       
445        for(TreeList::iterator itr=trees.begin();
446            itr!=trees.end();
447            ++itr)
448        {
449            Tree& tree = **itr;
450            geode->addDrawable(createOrthogonalQuads(tree._position,tree._width,tree._height,tree._color));
451        }
452       
453        _techniqueSwitch->addChild(geode);
454    }
455
456    {
457
458        osg::Group* transform_group = new osg::Group;
459        //group->setStateSet(dstate);
460       
461        osg::Geometry* geometry = createOrthogonalQuads(osg::Vec3(0.0f,0.0f,0.0f),1.0f,1.0f,osg::UByte4(255,255,255,255));
462       
463        for(TreeList::iterator itr=trees.begin();
464            itr!=trees.end();
465            ++itr)
466        {
467            Tree& tree = **itr;
468            osg::MatrixTransform* transform = new osg::MatrixTransform;
469            transform->setMatrix(osg::Matrix::scale(tree._width,tree._width,tree._height)*osg::Matrix::translate(tree._position));
470   
471            osg::Geode* geode = new osg::Geode;
472            geode->setStateSet(dstate);
473            geode->addDrawable(geometry);
474            transform->addChild(geode);
475            transform_group->addChild(transform);
476        }
477       
478        _techniqueSwitch->addChild(transform_group);
479    }
480   
481    _currentTechnique = 0;
482    _techniqueSwitch->setSingleChildOn(_currentTechnique);
483   
484
485    osg::Group* scene = new osg::Group;
486   
487    scene->addChild(terrain.get());
488    scene->addChild(_techniqueSwitch.get());
489
490    return scene;
491}
492
493int main( int argc, char **argv )
494{
495
496    // use an ArgumentParser object to manage the program arguments.
497    osg::ArgumentParser arguments(&argc,argv);
498
499    // set up the usage document, in case we need to print out how to use this program.
500    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates the osg::Shape classes.");
501    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
502    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
503   
504    // construct the viewer.
505    osgProducer::Viewer viewer(arguments);
506
507    // set up the value with sensible default event handlers.
508    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
509   
510    osg::ref_ptr<ForestTechniqueManager> ttm = new ForestTechniqueManager;
511   
512    viewer.getEventHandlerList().push_front(new TechniqueEventHandler(ttm.get()));
513
514    // get details on keyboard and mouse bindings used by the viewer.
515    viewer.getUsage(*arguments.getApplicationUsage());
516
517    // if user request help write it out to cout.
518    if (arguments.read("-h") || arguments.read("--help"))
519    {
520        arguments.getApplicationUsage()->write(std::cout);
521        return 1;
522    }
523
524    // any option left unread are converted into errors to write out later.
525    arguments.reportRemainingOptionsAsUnrecognized();
526
527    // report any errors if they have occured when parsing the program aguments.
528    if (arguments.errors())
529    {
530        arguments.writeErrorMessages(std::cout);
531        return 1;
532    }
533   
534    osg::Node* node = ttm->createScene();
535
536    // add model to viewer.
537    viewer.setSceneData( node );
538
539    // create the windows and run the threads.
540    viewer.realize();
541
542    while( !viewer.done() )
543    {
544        // wait for all cull and draw threads to complete.
545        viewer.sync();
546
547        // update the scene by traversing it with the the update visitor which will
548        // call all node update callbacks and animations.
549        viewer.update();
550         
551        // fire off the cull and draw traversals of the scene.
552        viewer.frame();
553       
554    }
555   
556    // wait for all cull and draw threads to complete before exit.
557    viewer.sync();
558
559    return 0;
560}
Note: See TracBrowser for help on using the browser.