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

Revision 10657, 15.1 kB (checked in by robert, 5 years ago)

Added stats handler to track performance effects of new GLBeginEndAdapter usage

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