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

Revision 4459, 16.5 kB (checked in by robert, 9 years ago)

Added support for rapid movement of the emitter, with particle now seeding between
the position of the emitter in the previous frame and the new position in the new
frame, the number of particles added also scales up to compensate for this movement.

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