root/OpenSceneGraph/trunk/examples/osgparticle/osgparticle.cpp @ 3516

Revision 3516, 15.1 kB (checked in by robert, 10 years ago)

Changed the ReferenceFrame? enums to be RELEATIVE and ABSOLUTE, and
deprecated the RELATIVE_TO_ABSOLUTE and RELATIVE_TO_PARENTS.

  • 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
6#include <osgParticle/Particle>
7#include <osgParticle/ParticleSystem>
8#include <osgParticle/ParticleSystemUpdater>
9#include <osgParticle/ModularEmitter>
10#include <osgParticle/ModularProgram>
11#include <osgParticle/RandomRateCounter>
12#include <osgParticle/SectorPlacer>
13#include <osgParticle/RadialShooter>
14#include <osgParticle/AccelOperator>
15#include <osgParticle/FluidFrictionOperator>
16
17
18
19//////////////////////////////////////////////////////////////////////////////
20// CUSTOM OPERATOR CLASS
21//////////////////////////////////////////////////////////////////////////////
22
23// This class demonstrates Operator subclassing. This way you can create
24// custom operators to apply your motion effects to the particles. See docs
25// for more details.
26class VortexOperator: public osgParticle::Operator {
27public:
28        VortexOperator()
29                : osgParticle::Operator(), center_(0, 0, 0), axis_(0, 0, 1), intensity_(0.1f) {}
30
31        VortexOperator(const VortexOperator &copy, const osg::CopyOp &copyop = osg::CopyOp::SHALLOW_COPY)
32                : osgParticle::Operator(copy, copyop), center_(copy.center_), axis_(copy.axis_), intensity_(copy.intensity_) {}
33
34        META_Object(osgParticle, VortexOperator);
35
36        void setCenter(const osg::Vec3 &c)
37        {
38                center_ = c;
39        }
40
41        void setAxis(const osg::Vec3 &a)
42        {
43                axis_ = a / a.length();
44        }
45
46        // this method is called by ModularProgram before applying
47        // operators on the particle set via the operate() method.     
48        void beginOperate(osgParticle::Program *prg)
49        {
50                // we have to check whether the reference frame is relative to parents
51                // or it's absolute; in the first case, we must transform the vectors
52                // from local to world space.
53                if (prg->getReferenceFrame() == osgParticle::Program::RELATIVE) {
54                        // transform the center point (full transformation)
55                        xf_center_ = prg->transformLocalToWorld(center_);
56                        // transform the axis vector (only rotation and scale)
57                        xf_axis_ = prg->rotateLocalToWorld(axis_);
58                } else {
59                        xf_center_ = center_;
60                        xf_axis_ = axis_;
61                }
62        }
63
64        // apply a vortex-like acceleration. This code is not optimized,
65        // it's here only for demonstration purposes.
66        void operate(osgParticle::Particle *P, double dt)
67        {
68                float l = xf_axis_ * (P->getPosition() - xf_center_);
69                osg::Vec3 lc = xf_center_ + xf_axis_ * l;
70                osg::Vec3 R = P->getPosition() - lc;
71                osg::Vec3 v = (R ^ xf_axis_) * P->getMassInv() * intensity_;
72
73                // compute new position
74                osg::Vec3 newpos = P->getPosition() + v * dt;
75
76                // update the position of the particle without modifying its
77                // velocity vector (this is unusual, normally you should call
78                // the Particle::setVelocity() or Particle::addVelocity()
79                // methods).
80                P->setPosition(newpos);
81        }
82
83protected:
84        virtual ~VortexOperator() {}
85
86private:
87        osg::Vec3 center_;
88        osg::Vec3 xf_center_;
89        osg::Vec3 axis_;
90        osg::Vec3 xf_axis_;
91        float intensity_;
92};
93
94
95//////////////////////////////////////////////////////////////////////////////
96// SIMPLE PARTICLE SYSTEM CREATION
97//////////////////////////////////////////////////////////////////////////////
98
99
100osgParticle::ParticleSystem *create_simple_particle_system(osg::Group *root)
101{
102
103        // Ok folks, this is the first particle system we build; it will be
104        // very simple, with no textures and no special effects, just default
105        // values except for a couple of attributes.
106
107        // First of all, we create the ParticleSystem object; it will hold
108        // our particles and expose the interface for managing them; this object
109        // is a Drawable, so we'll have to add it to a Geode later.
110
111        osgParticle::ParticleSystem *ps = new osgParticle::ParticleSystem;
112
113        // As for other Drawable classes, the aspect of graphical elements of
114        // ParticleSystem (the particles) depends on the StateAttribute's we
115        // give it. The ParticleSystem class has an helper function that let
116        // us specify a set of the most common attributes: setDefaultAttributes().
117        // This method can accept up to three parameters; the first is a texture
118        // name (std::string), which can be empty to disable texturing, the second
119        // sets whether particles have to be "emissive" (additive blending) or not;
120        // the third parameter enables or disables lighting.
121
122        ps->setDefaultAttributes("", true, false);
123
124        // Now that our particle system is set we have to create an emitter, that is
125        // an object (actually a Node descendant) that generate new particles at
126        // each frame. The best choice is to use a ModularEmitter, which allow us to
127        // achieve a wide variety of emitting styles by composing the emitter using
128        // three objects: a "counter", a "placer" and a "shooter". The counter must
129        // tell the ModularEmitter how many particles it has to create for the
130        // current frame; then, the ModularEmitter creates these particles, and for
131        // each new particle it instructs the placer and the shooter to set its
132        // position vector and its velocity vector, respectively.
133        // By default, a ModularEmitter object initializes itself with a counter of
134        // type RandomRateCounter, a placer of type PointPlacer and a shooter of
135        // type RadialShooter (see documentation for details). We are going to leave
136        // these default objects there, but we'll modify the counter so that it
137        // counts faster (more particles are emitted at each frame).
138
139        osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
140
141        // the first thing you *MUST* do after creating an emitter is to set the
142        // destination particle system, otherwise it won't know where to create
143        // new particles.
144
145        emitter->setParticleSystem(ps);
146
147        // Ok, get a pointer to the emitter's Counter object. We could also
148        // create a new RandomRateCounter object and assign it to the emitter,
149        // but since the default counter is already a RandomRateCounter, we
150        // just get a pointer to it and change a value.
151
152        osgParticle::RandomRateCounter *rrc =
153                static_cast<osgParticle::RandomRateCounter *>(emitter->getCounter());
154
155        // Now set the rate range to a better value. The actual rate at each frame
156        // will be chosen randomly within that range.
157
158        rrc->setRateRange(20, 30);      // generate 20 to 30 particles per second
159
160        // The emitter is done! Let's add it to the scene graph. The cool thing is
161        // that any emitter node will take into account the accumulated local-to-world
162        // matrix, so you can attach an emitter to a transform node and see it move.
163
164        root->addChild(emitter);
165
166        // Ok folks, we have almost finished. We don't add any particle modifier
167        // here (see ModularProgram and Operator classes), so all we still need is
168        // to create a Geode and add the particle system to it, so it can be
169        // displayed.
170
171        osg::Geode *geode = new osg::Geode;     
172        geode->addDrawable(ps);
173
174        // add the geode to the scene graph
175        root->addChild(geode);
176
177        return ps;
178
179}
180
181
182
183//////////////////////////////////////////////////////////////////////////////
184// COMPLEX PARTICLE SYSTEM CREATION
185//////////////////////////////////////////////////////////////////////////////
186
187
188osgParticle::ParticleSystem *create_complex_particle_system(osg::Group *root)
189{
190        // Are you ready for a more complex particle system? Well, read on!
191
192        // Now we take one step we didn't before: create a particle template.
193        // A particle template is simply a Particle object for which you set
194        // the desired properties (see documentation for details). When the
195        // particle system has to create a new particle and it's been assigned
196        // a particle template, the new particle will inherit the template's
197        // properties.
198        // You can even assign different particle templates to each emitter; in
199        // this case, the emitter's template will override the particle system's
200        // default template.
201
202        osgParticle::Particle ptemplate;
203
204        ptemplate.setLifeTime(3);               // 3 seconds of life
205
206        // the following ranges set the envelope of the respective
207        // graphical properties in time.
208        ptemplate.setSizeRange(osgParticle::rangef(0.75f, 3.0f));
209        ptemplate.setAlphaRange(osgParticle::rangef(0.0f, 1.5f));
210        ptemplate.setColorRange(osgParticle::rangev4(
211                osg::Vec4(1, 0.5f, 0.3f, 1.5f),
212                osg::Vec4(0, 0.7f, 1.0f, 0.0f)));
213
214        // these are physical properties of the particle
215        ptemplate.setRadius(0.05f);     // 5 cm wide particles
216        ptemplate.setMass(0.05f);       // 50 g heavy
217
218        // As usual, let's create the ParticleSystem object and set its
219        // default state attributes. This time we use a texture named
220        // "smoke.rgb", you can find it in the data distribution of OSG.
221        // We turn off the additive blending, because smoke has no self-
222        // illumination.
223        osgParticle::ParticleSystem *ps = new osgParticle::ParticleSystem;
224        ps->setDefaultAttributes("Images/smoke.rgb", false, false);
225
226        // assign the particle template to the system.
227        ps->setDefaultParticleTemplate(ptemplate);
228
229        // now we have to create an emitter; this will be a ModularEmitter, for which
230        // we define a RandomRateCounter as counter, a SectorPlacer as placer, and
231        // a RadialShooter as shooter.
232        osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
233        emitter->setParticleSystem(ps);
234
235        // setup the counter
236        osgParticle::RandomRateCounter *counter = new osgParticle::RandomRateCounter;
237        counter->setRateRange(60, 60);
238        emitter->setCounter(counter);
239
240        // setup the placer; it will be a circle of radius 5 (the particles will
241        // be placed inside this circle).
242        osgParticle::SectorPlacer *placer = new osgParticle::SectorPlacer;
243        placer->setCenter(8, 0, 10);
244        placer->setRadiusRange(2.5, 5);
245        placer->setPhiRange(0, 2 * osg::PI);    // 360° angle to make a circle
246        emitter->setPlacer(placer);
247
248        // now let's setup the shooter; we use a RadialShooter but we set the
249        // initial speed to zero, because we want the particles to fall down
250        // only under the effect of the gravity force. Since we se the speed
251        // to zero, there is no need to setup the shooting angles.
252        osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter;
253        shooter->setInitialSpeedRange(0, 0);
254        emitter->setShooter(shooter);
255
256        // add the emitter to the scene graph
257        root->addChild(emitter);
258
259        // WELL, we got our particle system and a nice emitter. Now we want to
260        // simulate the effect of the earth gravity, so first of all we have to
261        // create a Program. It is a particle processor just like the Emitter
262        // class, but it allows to modify particle properties *after* they have
263        // been created.
264        // The ModularProgram class can be thought as a sequence of operators,
265        // each one performing some actions on the particles. So, the trick is:
266        // create the ModularProgram object, create one or more Operator objects,
267        // add those operators to the ModularProgram, and finally add the
268        // ModularProgram object to the scene graph.
269        // NOTE: since the Program objects perform actions after the particles
270        // have been emitted by one or more Emitter objects, all instances of
271        // Program (and its descendants) should be placed *after* the instances
272        // of Emitter objects in the scene graph.
273
274        osgParticle::ModularProgram *program = new osgParticle::ModularProgram;
275        program->setParticleSystem(ps);
276
277        // create an operator that simulates the gravity acceleration.
278        osgParticle::AccelOperator *op1 = new osgParticle::AccelOperator;
279        op1->setToGravity();
280        program->addOperator(op1);
281
282        // now create a custom operator, we have defined it before (see
283        // class VortexOperator).
284        VortexOperator *op2 = new VortexOperator;
285        op2->setCenter(osg::Vec3(8, 0, 0));
286        program->addOperator(op2);
287
288        // let's add a fluid operator to simulate air friction.
289        osgParticle::FluidFrictionOperator *op3 = new osgParticle::FluidFrictionOperator;
290        op3->setFluidToAir();
291        program->addOperator(op3);
292
293        // add the program to the scene graph
294        root->addChild(program);
295
296        // create a Geode to contain our particle system.
297        osg::Geode *geode = new osg::Geode;
298        geode->addDrawable(ps);
299
300        // add the geode to the scene graph.
301        root->addChild(geode);
302
303        return ps;
304}
305
306
307//////////////////////////////////////////////////////////////////////////////
308// MAIN SCENE GRAPH BUILDING FUNCTION
309//////////////////////////////////////////////////////////////////////////////
310
311
312void build_world(osg::Group *root)
313{
314
315        // In this function we are going to create two particle systems;
316        // the first one will be very simple, based mostly on default properties;
317        // the second one will be a little bit more complex, showing how to
318        // create custom operators.
319        // To avoid inserting too much code in a single function, we have
320        // splitted the work into two functions which accept a Group node as
321        // parameter, and return a pointer to the particle system they created.
322
323        osgParticle::ParticleSystem *ps1 = create_simple_particle_system(root);
324        osgParticle::ParticleSystem *ps2 = create_complex_particle_system(root);
325
326        // Now that the particle systems and all other related objects have been
327        // created, we have to add an "updater" node to the scene graph. This node
328        // will react to cull traversal by updating the specified particles system.
329
330        osgParticle::ParticleSystemUpdater *psu = new osgParticle::ParticleSystemUpdater;
331        psu->addParticleSystem(ps1);
332        psu->addParticleSystem(ps2);
333
334        // add the updater node to the scene graph
335        root->addChild(psu);
336
337}
338
339
340//////////////////////////////////////////////////////////////////////////////
341// main()
342//////////////////////////////////////////////////////////////////////////////
343
344
345int main(int argc, char **argv)
346{
347    // use an ArgumentParser object to manage the program arguments.
348    osg::ArgumentParser arguments(&argc,argv);
349   
350    // set up the usage document, in case we need to print out how to use this program.
351    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of particle systems.");
352    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] image_file_left_eye image_file_right_eye");
353    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
354   
355
356    // construct the viewer.
357    osgProducer::Viewer viewer(arguments);
358
359    // set up the value with sensible default event handlers.
360    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
361
362    // get details on keyboard and mouse bindings used by the viewer.
363    viewer.getUsage(*arguments.getApplicationUsage());
364
365    // if user request help write it out to cout.
366    if (arguments.read("-h") || arguments.read("--help"))
367    {
368        arguments.getApplicationUsage()->write(std::cout);
369        return 1;
370    }
371
372    // any option left unread are converted into errors to write out later.
373    arguments.reportRemainingOptionsAsUnrecognized();
374
375    // report any errors if they have occured when parsing the program aguments.
376    if (arguments.errors())
377    {
378        arguments.writeErrorMessages(std::cout);
379        return 1;
380    }
381   
382    osg::Group *root = new osg::Group;
383    build_world(root);
384   
385    // add a viewport to the viewer and attach the scene graph.
386    viewer.setSceneData(root);
387       
388    // create the windows and run the threads.
389    viewer.realize();
390
391    while( !viewer.done() )
392    {
393        // wait for all cull and draw threads to complete.
394        viewer.sync();
395
396        // update the scene by traversing it with the the update visitor which will
397        // call all node update callbacks and animations.
398        viewer.update();
399         
400        // fire off the cull and draw traversals of the scene.
401        viewer.frame();
402       
403    }
404   
405    // wait for all cull and draw threads to complete before exit.
406    viewer.sync();
407
408    return 0;
409}
Note: See TracBrowser for help on using the browser.