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

Revision 9800, 36.3 kB (checked in by robert, 6 years ago)

From Christian Buchner, "I am hereby amending the osgforest sample with some code (functionally
equivalent to the GLSL shaders sample) that displays the forest with
shaders on Intel 945 GM hardware. This card supports OpenGL 1.4 and
ARB_fragment/vertex_program only.

I would be pleased if this change made it into the official set of
examples, as it illustrates the use of ARB shaders quite nicely. I did
not find any other example covering this topic."

  • 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                glColor4fv(itr->ptr());
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.