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

Revision 7699, 33.5 kB (checked in by robert, 7 years ago)

Added parsing of parameters to Viewer constructor.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* OpenSceneGraph example, osgforest.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include <osg/AlphaFunc>
20#include <osg/Billboard>
21#include <osg/BlendFunc>
22#include <osg/Depth>
23#include <osg/Geode>
24#include <osg/Geometry>
25#include <osg/Material>
26#include <osg/Math>
27#include <osg/MatrixTransform>
28#include <osg/PolygonOffset>
29#include <osg/Projection>
30#include <osg/ShapeDrawable>
31#include <osg/StateSet>
32#include <osg/Switch>
33#include <osg/Texture2D>
34#include <osg/TexEnv>
35
36#include <osgDB/ReadFile>
37#include <osgDB/FileUtils>
38
39#include <osgUtil/IntersectVisitor>
40#include <osgUtil/SmoothingVisitor>
41
42#include <osgText/Text>
43
44#include <osgViewer/Viewer>
45#include <osgGA/StateSetManipulator>
46
47#include <iostream>
48
49// for the grid data..
50#include "../osghangglide/terrain_coords.h"
51
52// class to create the forest and manage the movement between various techniques.
53class ForestTechniqueManager : public osg::Referenced
54{
55public:
56
57    ForestTechniqueManager() {}
58
59    class Tree : public osg::Referenced
60    {
61    public:
62
63        Tree():
64            _color(255,255,255,255),
65            _width(1.0f),
66            _height(1.0f),
67            _type(0) {}
68
69        Tree(const osg::Vec3& position, const osg::Vec4ub& color, float width, float height, unsigned int type):
70            _position(position),
71            _color(color),
72            _width(width),
73            _height(height),
74            _type(type) {}
75
76        osg::Vec3       _position;
77        osg::Vec4ub     _color;
78        float           _width;
79        float           _height;
80        unsigned int    _type;
81    };
82   
83    typedef std::vector< osg::ref_ptr<Tree> > TreeList;
84   
85    class Cell : public osg::Referenced
86    {
87    public:
88        typedef std::vector< osg::ref_ptr<Cell> > CellList;
89
90        Cell():_parent(0) {}
91        Cell(osg::BoundingBox& bb):_parent(0), _bb(bb) {}
92       
93        void addCell(Cell* cell) { cell->_parent=this; _cells.push_back(cell); }
94
95        void addTree(Tree* tree) { _trees.push_back(tree); }
96       
97        void addTrees(const TreeList& trees) { _trees.insert(_trees.end(),trees.begin(),trees.end()); }
98       
99        void computeBound();
100       
101        bool contains(const osg::Vec3& position) const { return _bb.contains(position); }
102       
103        bool divide(unsigned int maxNumTreesPerCell=10);
104       
105        bool divide(bool xAxis, bool yAxis, bool zAxis);
106       
107        void bin();
108
109
110        Cell*               _parent;
111        osg::BoundingBox    _bb;
112        CellList            _cells;
113        TreeList            _trees;
114       
115    };
116
117    float random(float min,float max) { return min + (max-min)*(float)rand()/(float)RAND_MAX; }
118    int random(int min,int max) { return min + (int)((float)(max-min)*(float)rand()/(float)RAND_MAX); }
119
120    osg::Geode* createTerrain(const osg::Vec3& origin, const osg::Vec3& size);
121
122    void createTreeList(osg::Node* terrain,const osg::Vec3& origin, const osg::Vec3& size,unsigned int numTreesToCreate,TreeList& trees);
123
124    osg::Geometry* createSprite( float w, float h, osg::Vec4ub color );
125
126    osg::Geometry* createOrthogonalQuads( const osg::Vec3& pos, float w, float h, osg::Vec4ub color );
127    osg::Geometry* createOrthogonalQuadsNoColor( const osg::Vec3& pos, float w, float h );
128
129    osg::Node* createBillboardGraph(Cell* cell,osg::StateSet* stateset);
130
131    osg::Node* createXGraph(Cell* cell,osg::StateSet* stateset);
132
133    osg::Node* createTransformGraph(Cell* cell,osg::StateSet* stateset);
134
135    osg::Node* createShaderGraph(Cell* cell,osg::StateSet* stateset);
136   
137    osg::Node* createHUDWithText(const std::string& text);
138
139    osg::Node* createScene(unsigned int numTreesToCreates);
140   
141    void advanceToNextTechnique(int delta=1)
142    {
143        if (_techniqueSwitch.valid())
144        {
145            _currentTechnique += delta;
146            if (_currentTechnique<0)
147                _currentTechnique = _techniqueSwitch->getNumChildren()-1;
148            if (_currentTechnique>=(int)_techniqueSwitch->getNumChildren())
149                _currentTechnique = 0;
150            _techniqueSwitch->setSingleChildOn(_currentTechnique);
151        }
152    }
153   
154    osg::ref_ptr<osg::Switch>   _techniqueSwitch;
155    int                         _currentTechnique;
156   
157
158};
159
160// event handler to capture keyboard events and use them to advance the technique used for rendering
161class TechniqueEventHandler : public osgGA::GUIEventHandler
162{
163public:
164
165    TechniqueEventHandler(ForestTechniqueManager* ttm=0) { _ForestTechniqueManager = ttm; }
166   
167    META_Object(osgforestApp,TechniqueEventHandler);
168
169    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*);
170   
171    virtual void getUsage(osg::ApplicationUsage& usage) const;
172
173protected:
174
175    ~TechniqueEventHandler() {}
176   
177    TechniqueEventHandler(const TechniqueEventHandler&,const osg::CopyOp&) {}
178   
179    osg::ref_ptr<ForestTechniqueManager> _ForestTechniqueManager;
180
181       
182};
183
184bool TechniqueEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*)
185{
186    switch(ea.getEventType())
187    {
188        case(osgGA::GUIEventAdapter::KEYDOWN):
189        {
190            if (ea.getKey()=='n' ||
191                ea.getKey()==osgGA::GUIEventAdapter::KEY_Right ||
192                ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right)
193            {
194                _ForestTechniqueManager->advanceToNextTechnique(1);
195                return true;
196            }
197            else if (ea.getKey()=='p' ||
198                     ea.getKey()==osgGA::GUIEventAdapter::KEY_Left ||
199                     ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left)
200            {
201                _ForestTechniqueManager->advanceToNextTechnique(-1);
202                return true;
203            }
204            return false;
205        }
206
207        default:
208            return false;
209    }
210}
211
212void TechniqueEventHandler::getUsage(osg::ApplicationUsage& usage) const
213{
214    usage.addKeyboardMouseBinding("n or Left Arrow","Advance to next technique");
215    usage.addKeyboardMouseBinding("p or Right Array","Move to previous technique");
216}
217
218
219void ForestTechniqueManager::Cell::computeBound()
220{
221    _bb.init();
222    for(CellList::iterator citr=_cells.begin();
223        citr!=_cells.end();
224        ++citr)
225    {
226        (*citr)->computeBound();
227        _bb.expandBy((*citr)->_bb);
228    }
229
230    for(TreeList::iterator titr=_trees.begin();
231        titr!=_trees.end();
232        ++titr)
233    {
234        _bb.expandBy((*titr)->_position);
235    }
236}
237
238bool ForestTechniqueManager::Cell::divide(unsigned int maxNumTreesPerCell)
239{
240
241    if (_trees.size()<=maxNumTreesPerCell) return false;
242
243    computeBound();
244
245    float radius = _bb.radius();
246    float divide_distance = radius*0.7f;
247    if (divide((_bb.xMax()-_bb.xMin())>divide_distance,(_bb.yMax()-_bb.yMin())>divide_distance,(_bb.zMax()-_bb.zMin())>divide_distance))
248    {
249        // recusively divide the new cells till maxNumTreesPerCell is met.
250        for(CellList::iterator citr=_cells.begin();
251            citr!=_cells.end();
252            ++citr)
253        {
254            (*citr)->divide(maxNumTreesPerCell);
255        }
256        return true;
257   }
258   else
259   {
260        return false;
261   }
262}
263
264bool ForestTechniqueManager::Cell::divide(bool xAxis, bool yAxis, bool zAxis)
265{
266    if (!(xAxis || yAxis || zAxis)) return false;
267
268    if (_cells.empty())
269        _cells.push_back(new Cell(_bb));
270
271    if (xAxis)
272    {
273        unsigned int numCellsToDivide=_cells.size();
274        for(unsigned int i=0;i<numCellsToDivide;++i)
275        {
276            Cell* orig_cell = _cells[i].get();
277            Cell* new_cell = new Cell(orig_cell->_bb);
278
279            float xCenter = (orig_cell->_bb.xMin()+orig_cell->_bb.xMax())*0.5f;
280            orig_cell->_bb.xMax() = xCenter;
281            new_cell->_bb.xMin() = xCenter;
282
283            _cells.push_back(new_cell);
284        }
285    }
286
287    if (yAxis)
288    {
289        unsigned int numCellsToDivide=_cells.size();
290        for(unsigned int i=0;i<numCellsToDivide;++i)
291        {
292            Cell* orig_cell = _cells[i].get();
293            Cell* new_cell = new Cell(orig_cell->_bb);
294
295            float yCenter = (orig_cell->_bb.yMin()+orig_cell->_bb.yMax())*0.5f;
296            orig_cell->_bb.yMax() = yCenter;
297            new_cell->_bb.yMin() = yCenter;
298
299            _cells.push_back(new_cell);
300        }
301    }
302
303    if (zAxis)
304    {
305        unsigned int numCellsToDivide=_cells.size();
306        for(unsigned int i=0;i<numCellsToDivide;++i)
307        {
308            Cell* orig_cell = _cells[i].get();
309            Cell* new_cell = new Cell(orig_cell->_bb);
310
311            float zCenter = (orig_cell->_bb.zMin()+orig_cell->_bb.zMax())*0.5f;
312            orig_cell->_bb.zMax() = zCenter;
313            new_cell->_bb.zMin() = zCenter;
314
315            _cells.push_back(new_cell);
316        }
317    }
318
319    bin();
320
321    return true;
322
323}
324
325void ForestTechniqueManager::Cell::bin()
326{   
327    // put trees in apprpriate cells.
328    TreeList treesNotAssigned;
329    for(TreeList::iterator titr=_trees.begin();
330        titr!=_trees.end();
331        ++titr)
332    {
333        Tree* tree = titr->get();
334        bool assigned = false;
335        for(CellList::iterator citr=_cells.begin();
336            citr!=_cells.end() && !assigned;
337            ++citr)
338        {
339            if ((*citr)->contains(tree->_position))
340            {
341                (*citr)->addTree(tree);
342                assigned = true;
343            }
344        }
345        if (!assigned) treesNotAssigned.push_back(tree);
346    }
347
348    // put the unassigned trees back into the original local tree list.
349    _trees.swap(treesNotAssigned);
350
351
352    // prune empty cells.
353    CellList cellsNotEmpty;
354    for(CellList::iterator citr=_cells.begin();
355        citr!=_cells.end();
356        ++citr)
357    {
358        if (!((*citr)->_trees.empty()))
359        {
360            cellsNotEmpty.push_back(*citr);
361        }
362    }
363    _cells.swap(cellsNotEmpty);
364
365
366}
367
368osg::Geode* ForestTechniqueManager::createTerrain(const osg::Vec3& origin, const osg::Vec3& size)
369{
370    osg::Geode* geode = new osg::Geode();
371
372    // ---------------------------------------
373    // Set up a StateSet to texture the objects
374    // ---------------------------------------
375    osg::StateSet* stateset = new osg::StateSet();
376
377    osg::Image* image = osgDB::readImageFile("Images/lz.rgb");
378    if (image)
379    {
380        osg::Texture2D* texture = new osg::Texture2D;
381        texture->setImage(image);
382        stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
383    }
384   
385    geode->setStateSet( stateset );
386   
387    unsigned int numColumns = 38;
388    unsigned int numRows = 39;
389    unsigned int r;
390    unsigned int c;
391
392    // compute z range of z values of grid data so we can scale it.
393    float min_z = FLT_MAX;
394    float max_z = -FLT_MAX;
395    for(r=0;r<numRows;++r)
396    {
397        for(c=0;c<numColumns;++c)
398        {
399            min_z = osg::minimum(min_z,vertex[r+c*numRows][2]);
400            max_z = osg::maximum(max_z,vertex[r+c*numRows][2]);
401        }
402    }
403   
404    float scale_z = size.z()/(max_z-min_z);
405
406
407    bool createGrid = false;
408    if (createGrid)
409    {
410
411        osg::HeightField* grid = new osg::HeightField;
412        grid->allocate(numColumns,numRows);
413        grid->setOrigin(origin);
414        grid->setXInterval(size.x()/(float)(numColumns-1));
415        grid->setYInterval(size.y()/(float)(numRows-1));
416
417        for(r=0;r<numRows;++r)
418        {
419            for(c=0;c<numColumns;++c)
420            {
421                grid->setHeight(c,r,(vertex[r+c*numRows][2]-min_z)*scale_z);
422            }
423        }
424       
425        geode->addDrawable(new osg::ShapeDrawable(grid));
426    }
427    else
428    {
429        osg::Geometry* geometry = new osg::Geometry;
430       
431        osg::Vec3Array& v = *(new osg::Vec3Array(numColumns*numRows));
432        osg::Vec2Array& t = *(new osg::Vec2Array(numColumns*numRows));
433        osg::Vec4ubArray& color = *(new osg::Vec4ubArray(1));
434       
435        color[0].set(255,255,255,255);
436
437        float rowCoordDelta = size.y()/(float)(numRows-1);
438        float columnCoordDelta = size.x()/(float)(numColumns-1);
439       
440        float rowTexDelta = 1.0f/(float)(numRows-1);
441        float columnTexDelta = 1.0f/(float)(numColumns-1);
442
443        osg::Vec3 pos = origin;
444        osg::Vec2 tex(0.0f,0.0f);
445        int vi=0;
446        for(r=0;r<numRows;++r)
447        {
448            pos.x() = origin.x();
449            tex.x() = 0.0f;
450            for(c=0;c<numColumns;++c)
451            {
452                v[vi].set(pos.x(),pos.y(),pos.z()+(vertex[r+c*numRows][2]-min_z)*scale_z);
453                t[vi].set(tex.x(),tex.y());
454                pos.x()+=columnCoordDelta;
455                tex.x()+=columnTexDelta;
456                ++vi;
457            }
458            pos.y() += rowCoordDelta;
459            tex.y() += rowTexDelta;
460        }
461       
462        geometry->setVertexArray(&v);
463        geometry->setColorArray(&color);
464        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
465        geometry->setTexCoordArray(0,&t);
466       
467        for(r=0;r<numRows-1;++r)
468        {
469            osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numColumns));
470            geometry->addPrimitiveSet(&drawElements);
471            int ei=0;
472            for(c=0;c<numColumns;++c)
473            {
474                drawElements[ei++] = (r+1)*numColumns+c;
475                drawElements[ei++] = (r)*numColumns+c;
476            }
477        }
478       
479        geode->addDrawable(geometry);
480       
481        osgUtil::SmoothingVisitor sv;
482        sv.smooth(*geometry);
483    }
484   
485    return geode;
486}
487
488void ForestTechniqueManager::createTreeList(osg::Node* terrain,const osg::Vec3& origin, const osg::Vec3& size,unsigned int numTreesToCreate,TreeList& trees)
489{
490
491    float max_TreeHeight = sqrtf(size.length2()/(float)numTreesToCreate);
492    float max_TreeWidth = max_TreeHeight*0.5f;
493   
494    float min_TreeHeight = max_TreeHeight*0.3f;
495    float min_TreeWidth = min_TreeHeight*0.5f;
496
497    trees.reserve(trees.size()+numTreesToCreate);
498
499
500    for(unsigned int i=0;i<numTreesToCreate;++i)
501    {
502        Tree* tree = new Tree;
503        tree->_position.set(random(origin.x(),origin.x()+size.x()),random(origin.y(),origin.y()+size.y()),origin.z());
504        tree->_color.set(random(128,255),random(128,255),random(128,255),255);
505        tree->_width = random(min_TreeWidth,max_TreeWidth);
506        tree->_height = random(min_TreeHeight,max_TreeHeight);
507        tree->_type = 0;
508       
509        if (terrain)
510        {
511            osgUtil::IntersectVisitor iv;
512            osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
513
514            segDown->set(tree->_position,tree->_position+osg::Vec3(0.0f,0.0f,size.z()));
515            iv.addLineSegment(segDown.get());
516           
517            terrain->accept(iv);
518
519            if (iv.hits())
520            {
521                osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
522                if (!hitList.empty())
523                {
524                    osg::Vec3 ip = hitList.front().getWorldIntersectPoint();
525                    osg::Vec3 np = hitList.front().getWorldIntersectNormal();
526                    tree->_position = ip;
527                }
528            }
529        }
530       
531        trees.push_back(tree);
532    }
533}
534
535osg::Geometry* ForestTechniqueManager::createSprite( float w, float h, osg::Vec4ub color )
536{
537    // set up the coords
538    osg::Vec3Array& v = *(new osg::Vec3Array(4));
539    osg::Vec2Array& t = *(new osg::Vec2Array(4));
540    osg::Vec4ubArray& c = *(new osg::Vec4ubArray(1));
541
542    v[0].set(-w*0.5f,0.0f,0.0f);
543    v[1].set( w*0.5f,0.0f,0.0f);
544    v[2].set( w*0.5f,0.0f,h);
545    v[3].set(-w*0.5f,0.0f,h);
546
547    c[0] = color;
548
549    t[0].set(0.0f,0.0f);
550    t[1].set(1.0f,0.0f);
551    t[2].set(1.0f,1.0f);
552    t[3].set(0.0f,1.0f);
553
554    osg::Geometry *geom = new osg::Geometry;
555
556    geom->setVertexArray( &v );
557
558    geom->setTexCoordArray( 0, &t );
559
560    geom->setColorArray( &c );
561    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
562
563    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4) );
564
565    return geom;
566}
567
568osg::Geometry* ForestTechniqueManager::createOrthogonalQuads( const osg::Vec3& pos, float w, float h, osg::Vec4ub color )
569{
570    // set up the coords
571    osg::Vec3Array& v = *(new osg::Vec3Array(8));
572    osg::Vec2Array& t = *(new osg::Vec2Array(8));
573    osg::Vec4ubArray& c = *(new osg::Vec4ubArray(1));
574   
575    float rotation = random(0.0f,osg::PI/2.0f);
576    float sw = sinf(rotation)*w*0.5f;
577    float cw = cosf(rotation)*w*0.5f;
578
579    v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f);
580    v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f);
581    v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h);
582    v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h);
583
584    v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f);
585    v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f);
586    v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h);
587    v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h);
588
589    c[0] = color;
590
591    t[0].set(0.0f,0.0f);
592    t[1].set(1.0f,0.0f);
593    t[2].set(1.0f,1.0f);
594    t[3].set(0.0f,1.0f);
595
596    t[4].set(0.0f,0.0f);
597    t[5].set(1.0f,0.0f);
598    t[6].set(1.0f,1.0f);
599    t[7].set(0.0f,1.0f);
600
601    osg::Geometry *geom = new osg::Geometry;
602
603    geom->setVertexArray( &v );
604
605    geom->setTexCoordArray( 0, &t );
606
607    geom->setColorArray( &c );
608    geom->setColorBinding( osg::Geometry::BIND_OVERALL );
609
610    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
611
612    return geom;
613}
614
615osg::Node* ForestTechniqueManager::createBillboardGraph(Cell* cell,osg::StateSet* stateset)
616{
617    bool needGroup = !(cell->_cells.empty());
618    bool needBillboard = !(cell->_trees.empty());
619   
620    osg::Billboard* billboard = 0;
621    osg::Group* group = 0;
622   
623    if (needBillboard)
624    {
625        billboard = new osg::Billboard;
626        billboard->setStateSet(stateset);
627        for(TreeList::iterator itr=cell->_trees.begin();
628            itr!=cell->_trees.end();
629            ++itr)
630        {
631            Tree& tree = **itr;
632            billboard->addDrawable(createSprite(tree._width,tree._height,tree._color),tree._position);   
633        }
634    }
635   
636    if (needGroup)
637    {
638        group = new osg::Group;
639        for(Cell::CellList::iterator itr=cell->_cells.begin();
640            itr!=cell->_cells.end();
641            ++itr)
642        {
643            group->addChild(createBillboardGraph(itr->get(),stateset));
644        }
645       
646        if (billboard) group->addChild(billboard);
647       
648    }
649    if (group) return group;
650    else return billboard;
651}
652
653osg::Node* ForestTechniqueManager::createXGraph(Cell* cell,osg::StateSet* stateset)
654{
655    bool needGroup = !(cell->_cells.empty());
656    bool needTrees = !(cell->_trees.empty());
657   
658    osg::Geode* geode = 0;
659    osg::Group* group = 0;
660   
661    if (needTrees)
662    {
663        geode = new osg::Geode;
664        geode->setStateSet(stateset);
665       
666        for(TreeList::iterator itr=cell->_trees.begin();
667            itr!=cell->_trees.end();
668            ++itr)
669        {
670            Tree& tree = **itr;
671            geode->addDrawable(createOrthogonalQuads(tree._position,tree._width,tree._height,tree._color));
672        }
673    }
674   
675    if (needGroup)
676    {
677        group = new osg::Group;
678        for(Cell::CellList::iterator itr=cell->_cells.begin();
679            itr!=cell->_cells.end();
680            ++itr)
681        {
682            group->addChild(createXGraph(itr->get(),stateset));
683        }
684       
685        if (geode) group->addChild(geode);
686       
687    }
688    if (group) return group;
689    else return geode;
690}
691
692osg::Node* ForestTechniqueManager::createTransformGraph(Cell* cell,osg::StateSet* stateset)
693{
694    bool needGroup = !(cell->_cells.empty());
695    bool needTrees = !(cell->_trees.empty());
696   
697    osg::Group* transform_group = 0;
698    osg::Group* group = 0;
699   
700    if (needTrees)
701    {
702        transform_group = new osg::Group;
703       
704        osg::Geometry* geometry = createOrthogonalQuads(osg::Vec3(0.0f,0.0f,0.0f),1.0f,1.0f,osg::Vec4ub(255,255,255,255));
705       
706        for(TreeList::iterator itr=cell->_trees.begin();
707            itr!=cell->_trees.end();
708            ++itr)
709        {
710            Tree& tree = **itr;
711            osg::MatrixTransform* transform = new osg::MatrixTransform;
712            transform->setMatrix(osg::Matrix::scale(tree._width,tree._width,tree._height)*osg::Matrix::translate(tree._position));
713   
714            osg::Geode* geode = new osg::Geode;
715            geode->setStateSet(stateset);
716            geode->addDrawable(geometry);
717            transform->addChild(geode);
718            transform_group->addChild(transform);
719        }
720    }
721   
722    if (needGroup)
723    {
724        group = new osg::Group;
725        for(Cell::CellList::iterator itr=cell->_cells.begin();
726            itr!=cell->_cells.end();
727            ++itr)
728        {
729            group->addChild(createTransformGraph(itr->get(),stateset));
730        }
731       
732        if (transform_group) group->addChild(transform_group);
733       
734    }
735    if (group) return group;
736    else return transform_group;
737}
738
739osg::Geometry* ForestTechniqueManager::createOrthogonalQuadsNoColor( const osg::Vec3& pos, float w, float h)
740{
741    // set up the coords
742    osg::Vec3Array& v = *(new osg::Vec3Array(8));
743    osg::Vec2Array& t = *(new osg::Vec2Array(8));
744   
745    float rotation = random(0.0f,osg::PI/2.0f);
746    float sw = sinf(rotation)*w*0.5f;
747    float cw = cosf(rotation)*w*0.5f;
748
749    v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f);
750    v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f);
751    v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h);
752    v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h);
753
754    v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f);
755    v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f);
756    v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h);
757    v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h);
758
759    t[0].set(0.0f,0.0f);
760    t[1].set(1.0f,0.0f);
761    t[2].set(1.0f,1.0f);
762    t[3].set(0.0f,1.0f);
763
764    t[4].set(0.0f,0.0f);
765    t[5].set(1.0f,0.0f);
766    t[6].set(1.0f,1.0f);
767    t[7].set(0.0f,1.0f);
768
769    osg::Geometry *geom = new osg::Geometry;
770
771    geom->setVertexArray( &v );
772
773    geom->setTexCoordArray( 0, &t );
774
775    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
776
777    return geom;
778}
779
780class ShaderGeometry : public osg::Drawable
781{
782    public:
783        ShaderGeometry() { setUseDisplayList(false); }
784
785        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
786        ShaderGeometry(const ShaderGeometry& ShaderGeometry,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
787            osg::Drawable(ShaderGeometry,copyop) {}
788
789        META_Object(osg,ShaderGeometry)
790
791        typedef std::vector<osg::Vec4> PositionSizeList;
792       
793        virtual void drawImplementation(osg::RenderInfo& renderInfo) const
794        {
795            for(PositionSizeList::const_iterator itr = _trees.begin();
796                itr != _trees.end();
797                ++itr)
798            {
799                glColor4fv(itr->ptr());
800                _geometry->draw(renderInfo);
801            }
802        }
803
804        virtual osg::BoundingBox computeBound() const
805        {
806            osg::BoundingBox geom_box = _geometry->getBound();
807            osg::BoundingBox bb;
808            for(PositionSizeList::const_iterator itr = _trees.begin();
809                itr != _trees.end();
810                ++itr)
811            {
812                bb.expandBy(geom_box.corner(0)*(*itr)[3] +
813                            osg::Vec3( (*itr)[0], (*itr)[1], (*itr)[2] ));
814                bb.expandBy(geom_box.corner(7)*(*itr)[3] +
815                            osg::Vec3( (*itr)[0], (*itr)[1], (*itr)[2] ));
816            }
817            return bb;
818        }
819       
820        void setGeometry(osg::Geometry* geometry)
821        {
822            _geometry = geometry;
823        }
824       
825        void addTree(ForestTechniqueManager::Tree& tree)
826        {
827            _trees.push_back(osg::Vec4(tree._position.x(), tree._position.y(), tree._position.z(), tree._height));
828        }
829       
830        osg::ref_ptr<osg::Geometry> _geometry;
831
832        PositionSizeList _trees;
833
834    protected:
835   
836        virtual ~ShaderGeometry() {}
837       
838};
839
840osg::Geometry* shared_geometry = 0;
841
842osg::Node* ForestTechniqueManager::createShaderGraph(Cell* cell,osg::StateSet* stateset)
843{
844    if (shared_geometry==0)
845    {
846        shared_geometry = createOrthogonalQuadsNoColor(osg::Vec3(0.0f,0.0f,0.0f),1.0f,1.0f);
847        //shared_geometry->setUseDisplayList(false);
848    }
849
850
851    bool needGroup = !(cell->_cells.empty());
852    bool needTrees = !(cell->_trees.empty());
853   
854    osg::Geode* geode = 0;
855    osg::Group* group = 0;
856   
857    if (needTrees)
858    {
859        geode = new osg::Geode;
860       
861        ShaderGeometry* shader_geometry = new ShaderGeometry;
862        shader_geometry->setGeometry(shared_geometry);
863       
864       
865        for(TreeList::iterator itr=cell->_trees.begin();
866            itr!=cell->_trees.end();
867            ++itr)
868        {
869            Tree& tree = **itr;
870            shader_geometry->addTree(tree);
871
872        }
873
874        geode->setStateSet(stateset);
875        geode->addDrawable(shader_geometry);
876    }
877   
878    if (needGroup)
879    {
880        group = new osg::Group;
881        for(Cell::CellList::iterator itr=cell->_cells.begin();
882            itr!=cell->_cells.end();
883            ++itr)
884        {
885            group->addChild(createShaderGraph(itr->get(),stateset));
886        }
887       
888        if (geode) group->addChild(geode);
889       
890    }
891    if (group) return group;
892    else return geode;
893}
894
895osg::Node* ForestTechniqueManager::createHUDWithText(const std::string& str)
896{
897    osg::Geode* geode = new osg::Geode();
898   
899    std::string timesFont("fonts/arial.ttf");
900
901    // turn lighting off for the text and disable depth test to ensure its always ontop.
902    osg::StateSet* stateset = geode->getOrCreateStateSet();
903    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
904
905    // or disable depth test, and make sure that the hud is drawn after everything
906    // else so that it always appears ontop.
907    stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
908    stateset->setRenderBinDetails(11,"RenderBin");
909
910    osg::Vec3 position(150.0f,800.0f,0.0f);
911    osg::Vec3 delta(0.0f,-120.0f,0.0f);
912
913    {
914        osgText::Text* text = new  osgText::Text;
915        geode->addDrawable( text );
916
917        text->setFont(timesFont);
918        text->setPosition(position);
919        text->setText(str);
920       
921        position += delta;
922    }   
923
924   
925    // create the hud.
926    osg::MatrixTransform* modelview_abs = new osg::MatrixTransform;
927    modelview_abs->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
928    modelview_abs->setMatrix(osg::Matrix::identity());
929    modelview_abs->addChild(geode);
930
931    osg::Projection* projection = new osg::Projection;
932    projection->setMatrix(osg::Matrix::ortho2D(0,1280,0,1024));
933    projection->addChild(modelview_abs);
934
935    return projection;
936}
937
938osg::Node* ForestTechniqueManager::createScene(unsigned int numTreesToCreates)
939{
940    osg::Vec3 origin(0.0f,0.0f,0.0f);
941    osg::Vec3 size(1000.0f,1000.0f,200.0f);
942
943    std::cout<<"Creating terrain...";
944    osg::ref_ptr<osg::Node> terrain = createTerrain(origin,size);
945    std::cout<<"done."<<std::endl;
946   
947    std::cout<<"Creating tree locations...";std::cout.flush();
948    TreeList trees;
949    createTreeList(terrain.get(),origin,size,numTreesToCreates,trees);
950    std::cout<<"done."<<std::endl;
951   
952    std::cout<<"Creating cell subdivision...";
953    osg::ref_ptr<Cell> cell = new Cell;
954    cell->addTrees(trees);
955    cell->divide();
956     std::cout<<"done."<<std::endl;
957   
958   
959    osg::Texture2D *tex = new osg::Texture2D;
960    tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
961    tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
962    tex->setImage(osgDB::readImageFile("Images/tree0.rgba"));
963
964    osg::StateSet *dstate = new osg::StateSet;
965    {   
966        dstate->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
967        dstate->setTextureAttribute(0, new osg::TexEnv );
968
969        dstate->setAttributeAndModes( new osg::BlendFunc, osg::StateAttribute::ON );
970
971        osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
972        alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
973        dstate->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
974
975        dstate->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
976
977        dstate->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
978    }
979   
980
981    _techniqueSwitch = new osg::Switch;
982
983    {
984        std::cout<<"Creating billboard based forest...";
985        osg::Group* group = new osg::Group;
986        group->addChild(createBillboardGraph(cell.get(),dstate));
987        group->addChild(createHUDWithText("Using osg::Billboard's to create a forest\n\nPress left cursor key to select OpenGL shader based forest\nPress right cursor key to select double quad based forest"));
988        _techniqueSwitch->addChild(group);
989        std::cout<<"done."<<std::endl;
990    }
991   
992    {
993        std::cout<<"Creating billboard based forest...";
994        osg::Group* group = new osg::Group;
995        group->addChild(createXGraph(cell.get(),dstate));
996        group->addChild(createHUDWithText("Using double quads to create a forest\n\nPress left cursor key to select osg::Billboard based forest\nPress right cursor key to select osg::MatrixTransform based forest\n"));
997        _techniqueSwitch->addChild(group);
998        std::cout<<"done."<<std::endl;
999    }
1000
1001    {
1002        std::cout<<"Creating billboard based forest...";
1003        osg::Group* group = new osg::Group;
1004        group->addChild(createTransformGraph(cell.get(),dstate));
1005        group->addChild(createHUDWithText("Using osg::MatrixTransform's to create a forest\n\nPress left cursor key to select double quad based forest\nPress right cursor key to select OpenGL shder based forest"));
1006        _techniqueSwitch->addChild(group);
1007        std::cout<<"done."<<std::endl;
1008    }
1009
1010    {
1011        osg::Group* group = new osg::Group;
1012       
1013
1014        osg::StateSet* stateset = new osg::StateSet;
1015
1016        stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
1017        stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
1018
1019        {
1020            osg::Program* program = new osg::Program;
1021            stateset->setAttribute(program);
1022
1023#if 1
1024            // use inline shaders
1025           
1026            ///////////////////////////////////////////////////////////////////
1027            // vertex shader using just Vec4 coefficients
1028            char vertexShaderSource[] =
1029                "varying vec2 texcoord;\n"
1030                "\n"
1031                "void main(void)\n"
1032                "{\n"
1033                "    vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
1034                "    gl_Position     = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
1035                "    gl_FrontColor = vec4(1.0,1.0,1.0,1.0);\n"
1036                "    texcoord = gl_MultiTexCoord0.st;\n"
1037                "}\n";
1038
1039            //////////////////////////////////////////////////////////////////
1040            // fragment shader
1041            //
1042            char fragmentShaderSource[] =
1043                "uniform sampler2D baseTexture; \n"
1044                "varying vec2 texcoord; \n"
1045                "\n"
1046                "void main(void) \n"
1047                "{ \n"
1048                "    gl_FragColor = texture2D( baseTexture, texcoord); \n"
1049                "}\n";
1050
1051            osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
1052            program->addShader(vertex_shader);
1053
1054            osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
1055            program->addShader(fragment_shader);
1056           
1057#else
1058
1059            // get shaders from source
1060            program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/forest.vert")));
1061            program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/forest.frag")));
1062
1063#endif
1064
1065            osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
1066            stateset->addUniform(baseTextureSampler);
1067        }
1068
1069        std::cout<<"Creating billboard based forest...";
1070        group->addChild(createShaderGraph(cell.get(),stateset));
1071        group->addChild(createHUDWithText("Using OpenGL Shader to create a forest\n\nPress left cursor key to select osg::MatrixTransform based forest\nPress right cursor key to select osg::Billboard based forest"));
1072        _techniqueSwitch->addChild(group);
1073        std::cout<<"done."<<std::endl;
1074    }
1075
1076    _currentTechnique = 0;
1077    _techniqueSwitch->setSingleChildOn(_currentTechnique);
1078   
1079
1080    osg::Group* scene = new osg::Group;
1081   
1082    scene->addChild(terrain.get());
1083    scene->addChild(_techniqueSwitch.get());
1084
1085    return scene;
1086}
1087
1088int main( int argc, char **argv )
1089{
1090
1091    // use an ArgumentParser object to manage the program arguments.
1092    osg::ArgumentParser arguments(&argc,argv);
1093   
1094    // construct the viewer.
1095    osgViewer::Viewer viewer(arguments);
1096
1097    float numTreesToCreates = 10000;
1098    arguments.read("--trees",numTreesToCreates);
1099   
1100    osg::ref_ptr<ForestTechniqueManager> ttm = new ForestTechniqueManager;
1101   
1102    viewer.addEventHandler(new TechniqueEventHandler(ttm.get()));
1103    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
1104
1105    // add model to viewer.
1106    viewer.setSceneData( ttm->createScene((unsigned int)numTreesToCreates) );
1107
1108
1109    return viewer.run();
1110}
Note: See TracBrowser for help on using the browser.