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

Revision 6941, 15.0 kB (checked in by robert, 8 years ago)

From Martin Lavery and Robert Osfield, Updated examples to use a variation of the MIT License

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