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

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

Renamed ExplosionDebriEffect? to ExplosionDebrisEffect?

  • 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.3f;
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.3f;
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),100.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 = 20.0f * ((float)rand() / (float)RAND_MAX);
275            float intensity = handleMovingModels ? 5.0f : 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::SmokeEffect* 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.