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

Revision 5012, 18.2 kB (checked in by robert, 8 years ago)

Added debugging code to help pick out instablilities in osgParticle. Debugging
code now commented out.

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