This example shows how to create an OpenSceneGraph application. This particular example demonstrates the use of an osgParticle::Interpolator to provide a Gaussian transform of each particle.
A new Gaussian interpolator is derived from osgParticle::Interpolator and performs a transform from the linear life time to a Gaussian curve which is then applied to the alpha value of the particle. This allows each particle to fade from opaque to invisible according to a Gaussian curve.
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 GaussianParticles is an osg::Group so it can just be placed in the scene-graph to work. At construction time, GaussianParticles 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 GaussianParticles 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 custom osgParticle::Interpolator object which is used to change the transparency of the particles over their lifetime. Without these custom Interpolator, each particle will change in a linear form from a beginning opaqueness to an ending opaqueness.
This example is an application. You can run it by running ./bin/exampleOsgParticlesGaussian.exe (on Windows) or ./bin/exampleOsgParticlesGaussian (on Linux). For more information about running examples, please see Running Applications and Examples.
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <osg/Light>
#include <osgViewer/Viewer>
#include <osgDB/WriteFile>
int main( int argc, char* argv[] )
{
double theta ( 360.0 );
double phi ( 360.0 );
double sigma( 1.0 );
double life( 4.0 );
for ( int ii( 1 ); ii < argc; ++ii )
{
if ( arg == "-h" || arg == "--help" || arg == "help" )
{
std::cout << "Usage:" << std::endl
<< argv[0] << "[options]" << std::endl
<< "\tWhere 'options' are as follows:" << std::endl
<< "\t\t'theta t' where t is an angle between 0.0 and 360.0" << std::endl
<< "\t\t'phi p' where p is an angle between 0.0 and 360.0" << std::endl
<< "\t\t'sigma s' where s is the sigma value for a Gaussian interpolator" << std::endl
<< "\t\t'life l' where l is a scaling value to adjust percentage of life each frame" << std::endl
<< "\t\t'height h' where h is a scaling value to adjust the interpolated value" << std::endl
<< std::endl;
return 1;
}
if ( ii == argc - 1 )
{
break;
}
if ( arg == "theta")
{
double val = std::strtod( argv[ii + 1], NULL );
if ( val > 0.0 && val <= 360.0 )
{
++ii;
}
continue;
}
if ( arg == "phi")
{
double val = std::strtod( argv[ii + 1], NULL );
if ( val > 0.0 && val <= 360.0 )
{
++ii;
}
continue;
}
if ( arg == "sigma")
{
long int val = std::strtol( argv[ii + 1], NULL, 10 );
if ( val > 0 )
{
sigma =
static_cast<double>(
val );
++ii;
}
continue;
}
if ( arg == "life")
{
double val = std::strtod( argv[ii + 1], NULL );
if ( val > 0.0 )
{
++ii;
}
continue;
}
if ( arg == "height")
{
double val = std::strtod( argv[ii + 1], NULL );
if ( val > 0.0 )
{
++ii;
}
continue;
}
}
osg::Group* root = new osg::Group();
osgViewer::Viewer viewer;
viewer.setUpViewInWindow( 100, 100, 600, 600 );
osg::Light* light = viewer.getLight();
light->setDiffuse( osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) );
viewer.setSceneData( root );
viewer.run();
return 0;
}
#include <cmath>
#include <iostream>
#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/FluidProgram>
#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 Gausserp : public osgParticle::Interpolator
{
public:
Gausserp( double sigma = 1.0, double life = 4.0, double height = 2.5 )
: osgParticle::Interpolator()
, mySigma( sigma )
, myLife( life )
, myHeight( height )
{}
Gausserp( const Interpolator& copy, const osg::CopyOp& copyop =
osg::CopyOp::SHALLOW_COPY )
: osgParticle::Interpolator( copy, copyop )
, mySigma( dynamic_cast<const Gausserp&>( copy ).mySigma )
, myLife( dynamic_cast<const Gausserp&>( copy ).myLife )
, myHeight( dynamic_cast<const Gausserp&>( copy ).myHeight )
{}
virtual ~Gausserp() {}
META_Object( test, Gausserp );
double gaussian( double x ) const
{
static const double f1 = 1.0 / ( mySigma * std::sqrt( 2.0 * osg::PI ) );
static const double f2 = 1.0 / ( 2.0 * std::pow( mySigma, 2.0 ) );
return f1 * std::exp( ( -std::pow( x, 2.0 ) ) * f2 );
}
virtual float interpolate( float t, float y1, float y2 ) const
{
double x = static_cast<double>( t * myLife );
double y = gaussian( x ) * myHeight;
return y1 + ( y2 -
y1 ) * static_cast<float>( y );
}
double mySigma;
double myLife;
double myHeight;
};
double life, double height )
, myPat( new osg::PositionAttitudeTransform )
, myParticle( new osgParticle::Particle )
, myParticleSystem( new osgParticle::ParticleSystem )
, myParticleSystemUpdater( new osgParticle::ParticleSystemUpdater )
, myProgram( new osgParticle::ModularProgram )
, myRandomRateCounter( dynamic_cast<osgParticle::RandomRateCounter*>( myModularEmitter->getCounter() ) )
, myPointPlacer( dynamic_cast<osgParticle::PointPlacer*>( myModularEmitter->getPlacer() ) )
, myRadialShooter( dynamic_cast<osgParticle::RadialShooter*>( myModularEmitter->getShooter() ) )
{
std::cout << "Theta " << theta << ", Phi " << phi << std::endl;
osg::Group*
group(
new osg::Group() );
osg::Geode* geode( new osg::Geode() );
addChild( myPat );
myPat->setAttitude( osg::Quat(
osg::DegreesToRadians( 0.0f ), osg::Vec3f( 0.0f, 0.0f, 1.0f ) ) );
myPat->setPosition( osg::Vec3f( 0.0f, 0.0f, 0.0f ) );
myParticle->setShape( osgParticle::Particle::QUAD );
myParticle->setLifeTime( 10.0f );
myParticle->setRadius( 5.0f );
myParticle->setMass( 0.01f );
myParticle->setSizeRange( osgParticle::rangef( 5.0f, 5.0f ) );
myParticle->setColorRange( osgParticle::rangev4(
osg::Vec4f( 1.0f, 1.0f, 1.0f, 1.0f ),
osg::Vec4f( 1.0f, 1.0f, 1.0f, 1.0f ) ) );
std::cout << "Gauss interpolation: sigma " << sigma
<< ", life " << life << ", height " << height << std::endl;
myParticle->setAlphaRange( osgParticle::rangef( 0.0f, 1.0f ) );
myParticle->setAlphaInterpolator( new Gausserp( sigma, life, height ) );
myParticle->setAngularVelocity(
Vec3f( osg::DegreesToRadians( 90.0f ), 0.0f, 0.0f ) );
float radius = 0.5f;
float density = 1.0f;
myParticle->setRadius( radius );
myParticle->setMass(
density * radius * radius * radius * osg::PI * 4.0f / 3.0f );
myParticleSystem->setDefaultAttributes( textureImage, false, false );
myParticleSystem->setDefaultParticleTemplate( *myParticle );
geode->addDrawable( myParticleSystem );
group->addChild( geode );
myParticleSystemUpdater->addParticleSystem( myParticleSystem );
group->addChild( myParticleSystemUpdater );
myRandomRateCounter->setRateRange( 200.0f, 200.0f );
myRadialShooter->setThetaRange(
osgParticle::rangef( 0.0f, osg::DegreesToRadians( theta ) ) );
myRadialShooter->setPhiRange(
osgParticle::rangef( 0.0f, osg::DegreesToRadians( phi ) ) );
myRadialShooter->setInitialSpeedRange( osgParticle::rangef( 5.0f, 5.0f ) );
myModularEmitter->setNumParticlesToCreateMovementCompensationRatio( 1.5f );
myModularEmitter->setParticleSystem( myParticleSystem );
myPat->addChild( myModularEmitter );
#if 0
osgParticle::AccelOperator* accelUp( new osgParticle::AccelOperator() );
accelUp->setToGravity( -1.0f );
myProgram->addOperator( accelUp );
osgParticle::FluidFrictionOperator*
airFriction( new osgParticle::FluidFrictionOperator() );
airFriction->setFluidToAir();
myProgram->addOperator( airFriction );
#else
myProgram = new osgParticle::FluidProgram();
osgParticle::FluidProgram* fp =
dynamic_cast<osgParticle::FluidProgram*>( myProgram.get() );
fp->setWind( osg::Vec3f( 0.0f, 0.0f, 0.0f ) );
#endif
#if 0
myProgram->setParticleSystem( myParticleSystem.get() );
addChild( myProgram );
#endif
}
{
}