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

Revision 6941, 33.4 kB (checked in by robert, 7 years ago)

From Martin Lavery and Robert Osfield, Updated examples to use a variation of the MIT License

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