root/OpenSceneGraph/trunk/examples/osgparticleeffects/osgparticleeffects.cpp @ 4584

Revision 4584, 16.7 kB (checked in by robert, 9 years ago)

Added SmokeTrailEffect? which renders created particles as single quad or line
strip, in the case of the quad strip the strip is aligned to the be orthogonal with
the eye point.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osgProducer/Viewer>
2
3#include <osg/Group>
4#include <osg/Geode>
5#include <osg/ShapeDrawable>
6#include <osg/Texture2D>
7#include <osg/PositionAttitudeTransform>
8#include <osg/MatrixTransform>
9
10#include <osgUtil/Optimizer>
11
12#include <osgDB/ReadFile>
13
14#include <osgText/Text>
15
16#include <osgParticle/ExplosionEffect>
17#include <osgParticle/ExplosionDebrisEffect>
18#include <osgParticle/SmokeEffect>
19#include <osgParticle/SmokeTrailEffect>
20#include <osgParticle/FireEffect>
21
22// for the grid data..
23#include "../osghangglide/terrain_coords.h"
24
25osg::Vec3 wind(1.0f,0.0f,0.0f);           
26
27osg::AnimationPath* createAnimationPath(const osg::Vec3& center,float radius,double looptime)
28{
29    // set up the animation path
30    osg::AnimationPath* animationPath = new osg::AnimationPath;
31    animationPath->setLoopMode(osg::AnimationPath::LOOP);
32   
33    int numSamples = 40;
34    float yaw = 0.0f;
35    float yaw_delta = 2.0f*osg::PI/((float)numSamples-1.0f);
36    float roll = osg::inDegrees(30.0f);
37   
38    double time=0.0f;
39    double time_delta = looptime/(double)numSamples;
40    for(int i=0;i<numSamples;++i)
41    {
42        osg::Vec3 position(center+osg::Vec3(sinf(yaw)*radius,cosf(yaw)*radius,0.0f));
43        osg::Quat rotation(osg::Quat(roll,osg::Vec3(0.0,1.0,0.0))*osg::Quat(-(yaw+osg::inDegrees(90.0f)),osg::Vec3(0.0,0.0,1.0)));
44       
45        animationPath->insert(time,osg::AnimationPath::ControlPoint(position,rotation));
46
47        yaw += yaw_delta;
48        time += time_delta;
49
50    }
51    return animationPath;   
52}
53
54osg::Node* createMovingModel(const osg::Vec3& center, float radius)
55{
56    float animationLength = 10.0f;
57
58    osg::AnimationPath* animationPath = createAnimationPath(center,radius,animationLength);
59
60    osg::Group* model = new osg::Group;
61
62    osg::Node* glider = osgDB::readNodeFile("glider.osg");
63    if (glider)
64    {
65        const osg::BoundingSphere& bs = glider->getBound();
66        float size = radius/bs.radius()*0.15f;
67
68        osg::MatrixTransform* positioned = new osg::MatrixTransform;
69        positioned->setDataVariance(osg::Object::STATIC);
70        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
71                                     osg::Matrix::scale(size,size,size)*
72                                     osg::Matrix::rotate(osg::inDegrees(-90.0f),0.0f,0.0f,1.0f));
73   
74        positioned->addChild(glider);
75   
76        osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform;   
77        xform->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
78        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0,0.5));
79        xform->addChild(positioned);
80
81        model->addChild(xform);
82    }
83 
84    osg::Node* cessna = osgDB::readNodeFile("cessna.osg");
85    if (cessna)
86    {
87        const osg::BoundingSphere& bs = cessna->getBound();
88        float size = radius/bs.radius()*0.15f;
89
90        osg::MatrixTransform* positioned = new osg::MatrixTransform;
91        positioned->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
92        positioned->setDataVariance(osg::Object::STATIC);
93        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
94                                     osg::Matrix::scale(size,size,size)*
95                                     osg::Matrix::rotate(osg::inDegrees(180.0f),0.0f,0.0f,1.0f));
96   
97        //positioned->addChild(cessna);
98        positioned->addChild(cessna);
99   
100        osg::MatrixTransform* xform = new osg::MatrixTransform;
101        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0f,1.0));
102        xform->addChild(positioned);
103
104        model->addChild(xform);
105    }
106   
107    return model;
108}
109
110
111osg::Vec3 computeTerrainIntersection(osg::Node* subgraph,float x,float y)
112{
113    osgUtil::IntersectVisitor iv;
114    osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
115
116    const osg::BoundingSphere& bs = subgraph->getBound();
117    float zMax = bs.center().z()+bs.radius();
118    float zMin = bs.center().z()-bs.radius();
119   
120    segDown->set(osg::Vec3(x,y,zMin),osg::Vec3(x,y,zMax));
121    iv.addLineSegment(segDown.get());
122
123    subgraph->accept(iv);
124
125    if (iv.hits())
126    {
127        osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
128        if (!hitList.empty())
129        {
130            osg::Vec3 ip = hitList.front().getWorldIntersectPoint();
131            return  ip;
132        }
133    }
134
135    return osg::Vec3(x,y,0.0f);
136}
137
138
139//////////////////////////////////////////////////////////////////////////////
140// MAIN SCENE GRAPH BUILDING FUNCTION
141//////////////////////////////////////////////////////////////////////////////
142
143void build_world(osg::Group *root)
144{
145
146    osg::Geode* terrainGeode = new osg::Geode;
147    // create terrain
148    {
149        osg::StateSet* stateset = new osg::StateSet();
150        osg::Image* image = osgDB::readImageFile("Images/lz.rgb");
151        if (image)
152        {
153            osg::Texture2D* texture = new osg::Texture2D;
154            texture->setImage(image);
155            stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
156        }
157
158        terrainGeode->setStateSet( stateset );
159
160        float size = 1000; // 10km;
161        float scale = size/39.0f; // 10km;
162        float z_scale = scale*3.0f;
163
164        osg::HeightField* grid = new osg::HeightField;
165        grid->allocate(38,39);
166        grid->setXInterval(scale);
167        grid->setYInterval(scale);
168
169        for(unsigned int r=0;r<39;++r)
170        {
171            for(unsigned int c=0;c<38;++c)
172            {
173                grid->setHeight(c,r,z_scale*vertex[r+c*39][2]);
174            }
175        }
176        terrainGeode->addDrawable(new osg::ShapeDrawable(grid));
177       
178        root->addChild(terrainGeode);
179    }   
180
181
182    // create particle effects
183    {   
184        osg::Vec3 position = computeTerrainIntersection(terrainGeode,100.0f,100.0f);
185
186        osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, 10.0f);
187        osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, 10.0f);
188        osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, 10.0f);
189        osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, 10.0f);
190
191        explosion->setWind(wind);
192        explosionDebri->setWind(wind);
193        smoke->setWind(wind);
194        fire->setWind(wind);
195
196        root->addChild(explosion);
197        root->addChild(explosionDebri);
198        root->addChild(smoke);
199        root->addChild(fire);
200    }
201   
202    // create particle effects
203    {   
204        osg::Vec3 position = computeTerrainIntersection(terrainGeode,200.0f,100.0f);
205
206        osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, 1.0f);
207        osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, 1.0f);
208        osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, 1.0f);
209        osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, 1.0f);
210
211        explosion->setWind(wind);
212        explosionDebri->setWind(wind);
213        smoke->setWind(wind);
214        fire->setWind(wind);
215
216        root->addChild(explosion);
217        root->addChild(explosionDebri);
218        root->addChild(smoke);
219        root->addChild(fire);
220    }
221
222    // create the moving models.
223    {
224        root->addChild(createMovingModel(osg::Vec3(500.0f,500.0f,500.0f),300.0f));
225    }
226}
227
228
229// class to handle events with a pick
230class PickHandler : public osgGA::GUIEventHandler {
231public:
232
233    PickHandler() {}       
234   
235    bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
236    {
237        switch(ea.getEventType())
238        {
239            case(osgGA::GUIEventAdapter::PUSH):
240            {
241                osgProducer::Viewer* viewer = dynamic_cast<osgProducer::Viewer*>(&aa);
242                pick(viewer,ea);
243            }
244            return false;
245
246        default:
247            return false;
248        }
249    }
250
251    void pick(osgProducer::Viewer* viewer, const osgGA::GUIEventAdapter& ea)
252    {
253        osg::Group* root = dynamic_cast<osg::Group*>(viewer->getSceneData());       
254        if (!root) return;
255
256        osgUtil::IntersectVisitor::HitList hlist;
257        if (viewer->computeIntersections(ea.getX(),ea.getY(),hlist))
258        {
259            osgUtil::Hit& hit = hlist.front();
260
261            bool handleMovingModels = false;
262            const osg::NodePath& nodePath = hit.getNodePath();
263            for(osg::NodePath::const_iterator nitr=nodePath.begin();
264                nitr!=nodePath.end();
265                ++nitr)
266            {
267                const osg::Transform* transform = dynamic_cast<const osg::Transform*>(*nitr);
268                if (transform)
269                {
270                    if (transform->getDataVariance()==osg::Object::DYNAMIC) handleMovingModels=true;
271                }
272            }
273           
274            osg::Vec3 position = handleMovingModels ? hit.getLocalIntersectPoint() : hit.getWorldIntersectPoint();
275            float scale = 10.0f * ((float)rand() / (float)RAND_MAX);
276            float intensity = 1.0f;
277
278            osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, scale, intensity);
279            osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, scale, intensity);
280            osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, scale, intensity);
281            osgParticle::ParticleEffect* smoke = 0;
282            if (handleMovingModels)
283                smoke =  new osgParticle::SmokeTrailEffect(position, scale, intensity);
284            else
285                smoke =  new osgParticle::SmokeEffect(position, scale, intensity);
286           
287            explosion->setWind(wind);
288            explosionDebri->setWind(wind);
289            smoke->setWind(wind);
290            fire->setWind(wind);
291
292            osg::Group* effectsGroup = new osg::Group;
293            effectsGroup->addChild(explosion);
294            effectsGroup->addChild(explosionDebri);
295            effectsGroup->addChild(smoke);
296            effectsGroup->addChild(fire);
297           
298
299            if (handleMovingModels)
300            {
301                // insert particle effects alongside the hit node, therefore able to track that nodes movement,
302                // however, this does require us to insert the ParticleSystem itself into the root of the scene graph
303                // seperately from the the main particle effects group which contains the emitters and programs.
304                // the follow code block implements this, note the path for handling particle effects which arn't attached to
305                // moving models is easy - just a single line of code!
306           
307                // tell the effects not to attach to the particle system locally for rendering, as we'll handle add it into the
308                // scene graph ourselves.
309                explosion->setUseLocalParticleSystem(false);
310                explosionDebri->setUseLocalParticleSystem(false);
311                smoke->setUseLocalParticleSystem(false);
312                fire->setUseLocalParticleSystem(false);
313
314                // find a place to insert the particle effects group alongside the hit node.
315                // there are two possible ways that this can be done, either insert it into
316                // a pre-existing group along side the hit node, or if no pre existing group
317                // is found then this needs to be inserted above the hit node, and then the
318                // particle effect can be inserted into this.
319                osg::ref_ptr<osg::Geode> hitGeode = hit.getGeode();
320                osg::Node::ParentList parents = hitGeode->getParents();               
321                osg::Group* insertGroup = 0;
322                unsigned int numGroupsFound = 0;
323                for(osg::Node::ParentList::iterator itr=parents.begin();
324                    itr!=parents.end();
325                    ++itr)
326                {
327                    if (typeid(*(*itr))==typeid(osg::Group))
328                    {
329                        ++numGroupsFound;
330                        insertGroup = *itr;
331                    }
332                }               
333                if (numGroupsFound==parents.size() && numGroupsFound==1 && insertGroup)
334                {
335                    osg::notify(osg::INFO)<<"PickHandler::pick(,) hit node's parent is a single osg::Group so we can simple the insert the particle effects group here."<<std::endl;
336
337                    // just reuse the existing group.
338                    insertGroup->addChild(effectsGroup);
339                }
340                else
341                {           
342                    osg::notify(osg::INFO)<<"PickHandler::pick(,) hit node doesn't have an appropriate osg::Group node to insert particle effects into, inserting a new osg::Group."<<std::endl;
343                    insertGroup = new osg::Group;
344                    for(osg::Node::ParentList::iterator itr=parents.begin();
345                        itr!=parents.end();
346                        ++itr)
347                    {
348                        (*itr)->replaceChild(hit.getGeode(),insertGroup);
349                    }
350                    insertGroup->addChild(hitGeode.get());
351                    insertGroup->addChild(effectsGroup);
352                }
353
354                // finally insert the particle systems into a Geode and attach to the root of the scene graph so the particle system
355                // can be rendered.
356                osg::Geode* geode = new osg::Geode;
357                geode->addDrawable(explosion->getParticleSystem());
358                geode->addDrawable(explosionDebri->getParticleSystem());
359                geode->addDrawable(smoke->getParticleSystem());
360                geode->addDrawable(fire->getParticleSystem());
361               
362                root->addChild(geode);
363
364            }
365            else
366            {
367                // when we don't have moving models we can simple insert the particle effect into the root of the scene graph
368                osg::notify(osg::INFO)<<"PickHandler::pick(,) adding particle effects to root node."<<std::endl;
369                root->addChild(effectsGroup);
370            }
371
372#if 0           
373            osg::Geode* geode = new osg::Geode;
374            geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(position,scale)));
375            group->addChild(geode);
376#endif
377 
378        }
379    }
380   
381protected:
382    virtual ~PickHandler() {}
383};
384
385//////////////////////////////////////////////////////////////////////////////
386// main()
387//////////////////////////////////////////////////////////////////////////////
388
389
390int main(int argc, char **argv)
391{
392    // use an ArgumentParser object to manage the program arguments.
393    osg::ArgumentParser arguments(&argc,argv);
394   
395    // set up the usage document, in case we need to print out how to use this program.
396    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of particle systems.");
397    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] image_file_left_eye image_file_right_eye");
398    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
399   
400
401    // construct the viewer.
402    osgProducer::Viewer viewer(arguments);
403
404    // set up the value with sensible default event handlers.
405    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
406
407    // get details on keyboard and mouse bindings used by the viewer.
408    viewer.getUsage(*arguments.getApplicationUsage());
409   
410    // register the pick handler
411    viewer.getEventHandlerList().push_front(new PickHandler());
412
413    // if user request help write it out to cout.
414    if (arguments.read("-h") || arguments.read("--help"))
415    {
416        arguments.getApplicationUsage()->write(std::cout);
417        return 1;
418    }
419
420    // any option left unread are converted into errors to write out later.
421    arguments.reportRemainingOptionsAsUnrecognized();
422
423    // report any errors if they have occured when parsing the program aguments.
424    if (arguments.errors())
425    {
426        arguments.writeErrorMessages(std::cout);
427        return 1;
428    }
429   
430    osg::Group *root = new osg::Group;
431    build_world(root);
432
433
434    osgUtil::Optimizer optimizer;
435    optimizer.optimize(root);
436   
437    // add a viewport to the viewer and attach the scene graph.
438    viewer.setSceneData(root);
439       
440    // create the windows and run the threads.
441    viewer.realize();
442
443    while( !viewer.done() )
444    {
445        // wait for all cull and draw threads to complete.
446        viewer.sync();
447
448        // update the scene by traversing it with the the update visitor which will
449        // call all node update callbacks and animations.
450        viewer.update();
451         
452        // fire off the cull and draw traversals of the scene.
453        viewer.frame();
454       
455    }
456   
457    // wait for all cull and draw threads to complete before exit.
458    viewer.sync();
459
460    return 0;
461}
Note: See TracBrowser for help on using the browser.