diff --git a/examples/osgparticle/osgparticle.cpp b/examples/osgparticle/osgparticle.cpp index e7ca2532c..8dbf51e14 100644 --- a/examples/osgparticle/osgparticle.cpp +++ b/examples/osgparticle/osgparticle.cpp @@ -323,6 +323,121 @@ osgParticle::ParticleSystem *create_complex_particle_system(osg::Group *root) } + +////////////////////////////////////////////////////////////////////////////// +// ANIMATED PARTICLE SYSTEM CREATION +////////////////////////////////////////////////////////////////////////////// + + +osgParticle::ParticleSystem *create_animated_particle_system(osg::Group *root) +{ + + // Now we will create a particle system that uses two emitters to + // display two animated particles, one showing an explosion, the other + // a smoke cloud. A particle system can only use one texture, so + // the animations for both particles are stored in a single bitmap. + // The frames of the animation are stored in tiles. For each particle + // template, the start and end tile of their animation have to be given. + // The example file used here has 64 tiles, stored in eight rows with + // eight images each. + + // First create a prototype for the explosion particle. + osgParticle::Particle pexplosion; + + // The frames of the explosion particle are played from birth to + // death of the particle. So if lifetime is one second, all 16 images + // of the particle are shown in this second. + pexplosion.setLifeTime(1); + + // some other particle properties just as in the last example. + pexplosion.setSizeRange(osgParticle::rangef(0.75f, 3.0f)); + pexplosion.setAlphaRange(osgParticle::rangef(0.5f, 1.0f)); + pexplosion.setColorRange(osgParticle::rangev4( + osg::Vec4(1, 1, 1, 1), + osg::Vec4(1, 1, 1, 1))); + pexplosion.setRadius(0.05f); + pexplosion.setMass(0.05f); + + // This command sets the animation tiles to be shown for the particle. + // The first two parameters define the tile layout of the texture image. + // 8, 8 means the texture has eight rows of tiles with eight columns each. + // 0, 15 defines the start and end tile + pexplosion.setTextureTileRange(8, 8, 0, 15); + + // The smoke particle is just the same, only plays another tile range. + osgParticle::Particle psmoke = pexplosion; + psmoke.setTextureTileRange(8, 8, 32, 45); + + // Create a single particle system for both particle types + osgParticle::ParticleSystem *ps = new osgParticle::ParticleSystem; + + // Assign the tiled texture + ps->setDefaultAttributes("Images/fireparticle8x8.png", false, false); + + // Create two emitters, one for the explosions, one for the smoke balls. + osgParticle::ModularEmitter *emitter1 = new osgParticle::ModularEmitter; + emitter1->setParticleSystem(ps); + emitter1->setParticleTemplate(pexplosion); + + osgParticle::ModularEmitter *emitter2 = new osgParticle::ModularEmitter; + emitter2->setParticleSystem(ps); + emitter2->setParticleTemplate(psmoke); + + // create a counter each. We could reuse the counter for both emitters, but + // then we could not control the ratio of smoke balls to explosions + osgParticle::RandomRateCounter *counter1 = new osgParticle::RandomRateCounter; + counter1->setRateRange(10, 10); + emitter1->setCounter(counter1); + + osgParticle::RandomRateCounter *counter2 = new osgParticle::RandomRateCounter; + counter2->setRateRange(3, 4); + emitter2->setCounter(counter2); + + // setup a single placer for both emitters. + osgParticle::SectorPlacer *placer = new osgParticle::SectorPlacer; + placer->setCenter(-8, 0, 0); + placer->setRadiusRange(2.5, 5); + placer->setPhiRange(0, 2 * osg::PI); // 360° angle to make a circle + emitter1->setPlacer(placer); + emitter2->setPlacer(placer); + + // the shooter is reused for both emitters + osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter; + shooter->setInitialSpeedRange(0, 0); + + // give particles a little spin + shooter->setInitialRotationalSpeedRange(osgParticle::rangev3( + osg::Vec3(0, 0, -1), + osg::Vec3(0, 0, 1))); + emitter1->setShooter(shooter); + emitter2->setShooter(shooter); + + // add both emitters to the scene graph + root->addChild(emitter1); + root->addChild(emitter2); + + // create a program, just as before + osgParticle::ModularProgram *program = new osgParticle::ModularProgram; + program->setParticleSystem(ps); + + // create an operator that moves the particles upwards + osgParticle::AccelOperator *op1 = new osgParticle::AccelOperator; + op1->setAcceleration(osg::Vec3(0, 0, 2.0f)); + program->addOperator(op1); + + // add the program to the scene graph + root->addChild(program); + + // create a Geode to contain our particle system. + osg::Geode *geode = new osg::Geode; + geode->addDrawable(ps); + + // add the geode to the scene graph. + root->addChild(geode); + + return ps; +} + ////////////////////////////////////////////////////////////////////////////// // MAIN SCENE GRAPH BUILDING FUNCTION ////////////////////////////////////////////////////////////////////////////// @@ -341,6 +456,7 @@ void build_world(osg::Group *root) osgParticle::ParticleSystem *ps1 = create_simple_particle_system(root); osgParticle::ParticleSystem *ps2 = create_complex_particle_system(root); + osgParticle::ParticleSystem *ps3 = create_animated_particle_system(root); // Now that the particle systems and all other related objects have been // created, we have to add an "updater" node to the scene graph. This node @@ -349,6 +465,7 @@ void build_world(osg::Group *root) osgParticle::ParticleSystemUpdater *psu = new osgParticle::ParticleSystemUpdater; psu->addParticleSystem(ps1); psu->addParticleSystem(ps2); + psu->addParticleSystem(ps3); // add the updater node to the scene graph root->addChild(psu); diff --git a/include/osgParticle/Particle b/include/osgParticle/Particle index 5198932be..a0b78398b 100644 --- a/include/osgParticle/Particle +++ b/include/osgParticle/Particle @@ -154,7 +154,7 @@ namespace osgParticle inline int getTileT() const; /// Get number of texture tiles - inline int getNumTiles() const { return _num_tile; } + inline int getNumTiles() const { return _end_tile - _start_tile + 1; } /** Kill the particle on next update NOTE: after calling this function, the isAlive() method will still @@ -246,8 +246,13 @@ namespace osgParticle /// Get the current (interpolated) polygon size. Valid only after the first call to update(). inline float getCurrentSize() const; - /// Specify how the particle texture is tiled - inline void setTextureTile(int sTile, int tTile, int numTiles = 0); + /// Specify how the particle texture is tiled. + /// All tiles in the given range are sequentially displayed during the lifetime + /// of the particle. When no range is given, all tiles are displayed during the lifetime. + inline void setTextureTileRange(int sTile, int tTile, int startTile, int endTile); + + /// Same as above, range starts at 0 and ends at end + inline void setTextureTile(int sTile, int tTile, int end = -1); /// Set the previous particle inline void setPreviousParticle(int previous) { _previousParticle = previous; } @@ -299,7 +304,8 @@ namespace osgParticle float _s_tile; float _t_tile; - int _num_tile; + int _start_tile; + int _end_tile; int _cur_tile; float _s_coord; float _t_coord; @@ -566,20 +572,35 @@ namespace osgParticle return _current_size; } - inline void Particle::setTextureTile(int sTile, int tTile, int numTiles) + + inline void Particle::setTextureTile(int sTile, int tTile, int end) { - _s_tile = (sTile>0) ? 1.0f / static_cast(sTile) : 1.0f; - _t_tile = (tTile>0) ? 1.0f / static_cast(tTile) : 1.0f; - if (numTiles <= 0) - { - _num_tile = sTile * tTile; - } - else - { - _num_tile = numTiles; - } + setTextureTileRange(sTile, tTile, -1, end); } + inline void Particle::setTextureTileRange(int sTile, int tTile, int startTile, int endTile) + { + _s_tile = (sTile>0) ? 1.0f / static_cast(sTile) : 1.0f; + _t_tile = (tTile>0) ? 1.0f / static_cast(tTile) : 1.0f; + + if(startTile == -1) + { + _start_tile = 0; + } + else + { + _start_tile = startTile; + } + + if(endTile == -1) + { + _end_tile = sTile * tTile; + } + else + { + _end_tile = endTile; + } + } } diff --git a/src/osgParticle/Particle.cpp b/src/osgParticle/Particle.cpp index 8bfeae00f..4aa35ab92 100644 --- a/src/osgParticle/Particle.cpp +++ b/src/osgParticle/Particle.cpp @@ -45,7 +45,8 @@ osgParticle::Particle::Particle() _current_alpha(0), _s_tile(1.0f), _t_tile(1.0f), - _num_tile(1), + _start_tile(0), + _end_tile(0), _cur_tile(-1), _s_coord(0.0f), _t_coord(0.0f), @@ -79,7 +80,7 @@ bool osgParticle::Particle::update(double dt) } //Compute the current texture tile based on our normalized age - int currentTile = static_cast(x * _num_tile); + int currentTile = _start_tile + static_cast(x * getNumTiles()); //If the current texture tile is different from previous, then compute new texture coords if(currentTile != _cur_tile)