| | 326 | |
| | 327 | ////////////////////////////////////////////////////////////////////////////// |
| | 328 | // ANIMATED PARTICLE SYSTEM CREATION |
| | 329 | ////////////////////////////////////////////////////////////////////////////// |
| | 330 | |
| | 331 | |
| | 332 | osgParticle::ParticleSystem *create_animated_particle_system(osg::Group *root) |
| | 333 | { |
| | 334 | |
| | 335 | // Now we will create a particle system that uses two emitters to |
| | 336 | // display two animated particles, one showing an explosion, the other |
| | 337 | // a smoke cloud. A particle system can only use one texture, so |
| | 338 | // the animations for both particles are stored in a single bitmap. |
| | 339 | // The frames of the animation are stored in tiles. For each particle |
| | 340 | // template, the start and end tile of their animation have to be given. |
| | 341 | // The example file used here has 64 tiles, stored in eight rows with |
| | 342 | // eight images each. |
| | 343 | |
| | 344 | // First create a prototype for the explosion particle. |
| | 345 | osgParticle::Particle pexplosion; |
| | 346 | |
| | 347 | // The frames of the explosion particle are played from birth to |
| | 348 | // death of the particle. So if lifetime is one second, all 16 images |
| | 349 | // of the particle are shown in this second. |
| | 350 | pexplosion.setLifeTime(1); |
| | 351 | |
| | 352 | // some other particle properties just as in the last example. |
| | 353 | pexplosion.setSizeRange(osgParticle::rangef(0.75f, 3.0f)); |
| | 354 | pexplosion.setAlphaRange(osgParticle::rangef(0.5f, 1.0f)); |
| | 355 | pexplosion.setColorRange(osgParticle::rangev4( |
| | 356 | osg::Vec4(1, 1, 1, 1), |
| | 357 | osg::Vec4(1, 1, 1, 1))); |
| | 358 | pexplosion.setRadius(0.05f); |
| | 359 | pexplosion.setMass(0.05f); |
| | 360 | |
| | 361 | // This command sets the animation tiles to be shown for the particle. |
| | 362 | // The first two parameters define the tile layout of the texture image. |
| | 363 | // 8, 8 means the texture has eight rows of tiles with eight columns each. |
| | 364 | // 0, 15 defines the start and end tile |
| | 365 | pexplosion.setTextureTileRange(8, 8, 0, 15); |
| | 366 | |
| | 367 | // The smoke particle is just the same, only plays another tile range. |
| | 368 | osgParticle::Particle psmoke = pexplosion; |
| | 369 | psmoke.setTextureTileRange(8, 8, 32, 45); |
| | 370 | |
| | 371 | // Create a single particle system for both particle types |
| | 372 | osgParticle::ParticleSystem *ps = new osgParticle::ParticleSystem; |
| | 373 | |
| | 374 | // Assign the tiled texture |
| | 375 | ps->setDefaultAttributes("Images/fireparticle8x8.png", false, false); |
| | 376 | |
| | 377 | // Create two emitters, one for the explosions, one for the smoke balls. |
| | 378 | osgParticle::ModularEmitter *emitter1 = new osgParticle::ModularEmitter; |
| | 379 | emitter1->setParticleSystem(ps); |
| | 380 | emitter1->setParticleTemplate(pexplosion); |
| | 381 | |
| | 382 | osgParticle::ModularEmitter *emitter2 = new osgParticle::ModularEmitter; |
| | 383 | emitter2->setParticleSystem(ps); |
| | 384 | emitter2->setParticleTemplate(psmoke); |
| | 385 | |
| | 386 | // create a counter each. We could reuse the counter for both emitters, but |
| | 387 | // then we could not control the ratio of smoke balls to explosions |
| | 388 | osgParticle::RandomRateCounter *counter1 = new osgParticle::RandomRateCounter; |
| | 389 | counter1->setRateRange(10, 10); |
| | 390 | emitter1->setCounter(counter1); |
| | 391 | |
| | 392 | osgParticle::RandomRateCounter *counter2 = new osgParticle::RandomRateCounter; |
| | 393 | counter2->setRateRange(3, 4); |
| | 394 | emitter2->setCounter(counter2); |
| | 395 | |
| | 396 | // setup a single placer for both emitters. |
| | 397 | osgParticle::SectorPlacer *placer = new osgParticle::SectorPlacer; |
| | 398 | placer->setCenter(-8, 0, 0); |
| | 399 | placer->setRadiusRange(2.5, 5); |
| | 400 | placer->setPhiRange(0, 2 * osg::PI); // 360° angle to make a circle |
| | 401 | emitter1->setPlacer(placer); |
| | 402 | emitter2->setPlacer(placer); |
| | 403 | |
| | 404 | // the shooter is reused for both emitters |
| | 405 | osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter; |
| | 406 | shooter->setInitialSpeedRange(0, 0); |
| | 407 | |
| | 408 | // give particles a little spin |
| | 409 | shooter->setInitialRotationalSpeedRange(osgParticle::rangev3( |
| | 410 | osg::Vec3(0, 0, -1), |
| | 411 | osg::Vec3(0, 0, 1))); |
| | 412 | emitter1->setShooter(shooter); |
| | 413 | emitter2->setShooter(shooter); |
| | 414 | |
| | 415 | // add both emitters to the scene graph |
| | 416 | root->addChild(emitter1); |
| | 417 | root->addChild(emitter2); |
| | 418 | |
| | 419 | // create a program, just as before |
| | 420 | osgParticle::ModularProgram *program = new osgParticle::ModularProgram; |
| | 421 | program->setParticleSystem(ps); |
| | 422 | |
| | 423 | // create an operator that moves the particles upwards |
| | 424 | osgParticle::AccelOperator *op1 = new osgParticle::AccelOperator; |
| | 425 | op1->setAcceleration(osg::Vec3(0, 0, 2.0f)); |
| | 426 | program->addOperator(op1); |
| | 427 | |
| | 428 | // add the program to the scene graph |
| | 429 | root->addChild(program); |
| | 430 | |
| | 431 | // create a Geode to contain our particle system. |
| | 432 | osg::Geode *geode = new osg::Geode; |
| | 433 | geode->addDrawable(ps); |
| | 434 | |
| | 435 | // add the geode to the scene graph. |
| | 436 | root->addChild(geode); |
| | 437 | |
| | 438 | return ps; |
| | 439 | } |
| | 440 | |