This example shows how to create an OpenSceneGraph application. This particular example demonstrates the structure of a simple particle system.
A specific set of classes need to be configured correctly to produce a working OSG particle system; osgParticle::Particle, osgParticle::ParticleSystem, osgParticle::ParticleSystemUpdater, and osgParticle::ModularEmitter. The ModularEmitter also contains a particle placer, counter, and shooter which need to be configured. This example includes two optional custom osgParticle::Interpolator objects to demonstrate changing the size and color of each particle over its lifetime.
The example creates a single class which encompasses the entire particle system (It is a convinience to create the particle system as one object but not necessary). This particle system is designed to emit one type of particle; a puff of smoke. The class SimpleParticles is an osg::Group so it can just be placed in the scene-graph to work. At construction time, SimpleParticles creates instances of all the necessary classes to construct a working particle system. The scene structure (how the objects are parented) is important to how the particle system works. This class is given a child osg::Group which parents the osgParticle::ParticleSytemUpdater and an osg::Geode. The osg::Geode is given an osgParticle::ParticleSystem as a drawable. The ParticleSystemUpdater is given the same ParticleSystem for it to manipulate. The ParticleSystem is given an osgParticle::Particle as a template for how to construct new particles. This main SimpleParticles class is also given a child osg::PositionAttitudeTransform to place the emitter of the particles. The PositionAttitudeTransform parents an osgParticle::ModularEmitter which controls how the particles are emitted. The ModularEmitter constructs its own default placer, counter and shooter objects. The placer sets the intiaitial position of each particle, the counter sets a range of particles to create each frame, and the shooter sets an initial velocity for each particle.
The example also creates two custom osgParticle::Interpolator objects which are used to change the size and color of the particles over their lifetime. Without these custom Interpolators, each particle will change in a linear form from a beginning size or color to an ending size or color. These custorm Interpolator objects are used to demponstrate changes from the default values, but are not required for a minimal particle system.
This example is an application. You can run it by running ./bin/exampleOsgParticles.exe (on Windows) or ./bin/exampleOsgParticles (on Linux). For more information about running examples, please see Running Applications and Examples.
#include <string>
#include <vector>
#include <osg/Geode>
#include <osg/Object>
#include <osg/PositionAttitudeTransform>
#include <osg/Math>
#include <osg/Quat>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osgParticle/AccelOperator>
#include <osgParticle/FluidFrictionOperator>
#include <osgParticle/LinearInterpolator>
#include <osgParticle/ModularEmitter>
#include <osgParticle/ModularProgram>
#include <osgParticle/Particle>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/PointPlacer>
#include <osgParticle/RandomRateCounter>
#include <osgParticle/RadialShooter>
static const std::string textureImage("../data/Vehicles/Smoke.rgb");
using namespace osg;
class Colerp : public osgParticle::Interpolator
{
public:
Colerp()
: osgParticle::Interpolator()
, myColors()
{
setColorList();
}
Colerp(const Interpolator& copy, const osg::CopyOp& copyop =
osg::CopyOp::SHALLOW_COPY)
: osgParticle::Interpolator(copy, copyop)
, myColors()
{
setColorList();
}
virtual ~Colerp() {}
META_Object(test, Colerp)
void setColorList()
{
myColors.push_back(Vec4f(1.0f, 1.0f, 1.0f, 1.0f));
myColors.push_back(Vec4f(1.0f, 0.0f, 0.0f, 1.0f));
myColors.push_back(Vec4f(1.0f, 1.0f, 0.0f, 1.0f));
myColors.push_back(Vec4f(0.0f, 1.0f, 0.0f, 1.0f));
myColors.push_back(Vec4f(0.0f, 1.0f, 1.0f, 1.0f));
myColors.push_back(Vec4f(0.0f, 0.0f, 1.0f, 1.0f));
myColors.push_back(Vec4f(1.0f, 0.0f, 1.0f, 1.0f));
myColors.push_back(Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
}
virtual float interpolate(float t, float y1, float y2) const
{
return y1 + (y2 - y1) * t;
}
virtual osg::Vec4 interpolate(
float t, const osg::Vec4& y1, const osg::Vec4& y2) const
{
float count(myColors.size() - 1.0f);
float inv(1.0f / count);
myColors.at(0).set(y1.r(), y1.g(), y1.b(), y1.a());
myColors.at(count).set(y2.r(), y2.g(), y2.b(), y2.a());
for (float step(0.0f); step < count; ++step)
{
if (t <= inv * (step + 1.0))
{
return osgParticle::Interpolator::interpolate(
(t - (inv * step)) * count,
myColors.at(step),
myColors.at(step + 1));
}
}
return osgParticle::Interpolator::interpolate(t, y1, y2);
}
private:
mutable std::vector<Vec4f> myColors;
};
class Sizerp : public osgParticle::Interpolator
{
public:
Sizerp() : osgParticle::Interpolator() {}
Sizerp(const Interpolator& copy, const osg::CopyOp& copyop =
osg::CopyOp::SHALLOW_COPY)
: osgParticle::Interpolator(copy, copyop) {}
virtual ~Sizerp() { }
META_Object(test, Sizerp)
float doubleSize(float t, float y1, float y2) const
{
return y1 + (y2 - y1) * t * 2.0;
}
virtual float interpolate(float t, float y1, float y2) const
{
float r = doubleSize(t, y1, y2);
if (r > y2)
{
r = y2 - (r - y2);
}
return r;
}
};
, myPat(new osg::PositionAttitudeTransform)
, myParticle(new osgParticle::Particle)
, myParticleSystem(new osgParticle::ParticleSystem)
, myParticleSystemUpdater(new osgParticle::ParticleSystemUpdater)
, myModularProgram(new osgParticle::ModularProgram)
, myRandomRateCounter(dynamic_cast<osgParticle::RandomRateCounter*>(myModularEmitter->getCounter()))
, myPointPlacer(dynamic_cast<osgParticle::PointPlacer*>(myModularEmitter->getPlacer()))
, myRadialShooter(dynamic_cast<osgParticle::RadialShooter*>(myModularEmitter->getShooter()))
{
osg::Group* group(new osg::Group());
osg::Geode* geode(new osg::Geode());
addChild(group);
addChild(myPat);
myPat->setPosition(osg::Vec3f(0.0f, 0.0f, -1.0f));
myPat->setAttitude(osg::Quat(osg::DegreesToRadians(90.0f), osg::Vec3f(0.0f, 0.0f, 1.0f)));
myParticle->setShape(osgParticle::Particle::QUAD);
myParticle->setLifeTime(10.0f);
myParticle->setRadius(5.0f);
myParticle->setMass(0.01f);
myParticle->setSizeRange(osgParticle::rangef(0.1f, 5.0f));
myParticle->setSizeInterpolator(new Sizerp());
myParticle->setColorRange(osgParticle::rangev4(
osg::Vec4f(1.0f, 1.0f, 1.0f, 1.0f),
osg::Vec4f(1.0f, 1.0f, 1.0f, 1.0f)));
myParticle->setColorInterpolator(new Colerp());
myParticle->setAngularVelocity(
Vec3f(osg::DegreesToRadians(90.0f), 0.0f, 0.0f));
myParticleSystem->setDefaultAttributes(textureImage, false, false);
myParticleSystem->setDefaultParticleTemplate(*myParticle);
geode->addDrawable(myParticleSystem);
group->addChild(geode);
myParticleSystemUpdater->addParticleSystem(myParticleSystem);
group->addChild(myParticleSystemUpdater);
myRandomRateCounter->setRateRange(10.0f, 20.0f);
myRadialShooter->setThetaRange(osgParticle::rangef(0.0f, osg::DegreesToRadians(90.0f)));
myRadialShooter->setPhiRange(osgParticle::rangef(0.0f, osg::DegreesToRadians(90.0f)));
myRadialShooter->setInitialSpeedRange(osgParticle::rangef(5.0f, 5.0f));
myModularEmitter->setParticleSystem(myParticleSystem);
myPat->addChild(myModularEmitter);
osgParticle::AccelOperator* accelUp(new osgParticle::AccelOperator());
accelUp->setToGravity(-1.0f);
myModularProgram->addOperator(accelUp);
osgParticle::FluidFrictionOperator*
airFriction(new osgParticle::FluidFrictionOperator());
airFriction->setFluidToAir();
myModularProgram->setParticleSystem(myParticleSystem);
}
{
}