Show
Ignore:
Timestamp:
11/17/05 21:22:55 (9 years ago)
Author:
robert
Message:

Replaced tabs with spaces in examples.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • OpenSceneGraph/trunk/examples/osgparticle/osgparticle.cpp

    r3528 r4805  
    2626class VortexOperator: public osgParticle::Operator { 
    2727public: 
    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_RF 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_RF) { 
    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         } 
     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_RF 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_RF) { 
     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    } 
    8282 
    8383protected: 
    84         virtual ~VortexOperator() {} 
     84    virtual ~VortexOperator() {} 
    8585 
    8686private: 
    87         osg::Vec3 center_; 
    88         osg::Vec3 xf_center_; 
    89         osg::Vec3 axis_; 
    90         osg::Vec3 xf_axis_; 
    91         float intensity_; 
     87    osg::Vec3 center_; 
     88    osg::Vec3 xf_center_; 
     89    osg::Vec3 axis_; 
     90    osg::Vec3 xf_axis_; 
     91    float intensity_; 
    9292}; 
    9393 
     
    101101{ 
    102102 
    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; 
     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; 
    178178 
    179179} 
     
    188188osgParticle::ParticleSystem *create_complex_particle_system(osg::Group *root) 
    189189{ 
    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; 
     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; 
    304304} 
    305305 
     
    313313{ 
    314314 
    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); 
     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); 
    336336 
    337337}