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

Revision 10711, 36.4 kB (checked in by robert, 5 years ago)

Replaced glColor4fv call with osg::State::Color(..)

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