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

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

From Alberto Luaces, "Here I send minor corrections to the text output by the osgforest example when
building its graphs. The message "Creating billboard based forest" appears
four times regardless of the actual type of the graph being built.
"

  • 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 osg::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 double quad 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 osg::MatrixTransform 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 shader 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 OpenGL shader 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.