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

Revision 13376, 17.2 kB (checked in by robert, 4 hours ago)

From Mattias Helsing, "Seems I was only half right given what you asked for. CMP0017 only
says that modules that are found and ran from cmake modules dir should
prefer cmake-provided modules. find_package() and include() still look
in CMAKE_MODULE_PATH first.

After some investigating I've come up with a proposal examplified in
the attached FindGDAL.cmake script. It simply calls the cmake provided
FindGDAL.cmake if it exists and returns if it succeeds in finding GDAL
using that, otherwise continue with our local cmake code.
Pro: Wont clutter our root CMakeLists.txt
Con: If we begin to write more advanced Findxxx modules (using
COMPONENTS, REQUIRED etc.) we may have to revise this scheme.
"

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