Added SmokeTrailEffect which renders created particles as single quad or line

strip, in the case of the quad strip the strip is aligned to the be orthogonal with
the eye point.
This commit is contained in:
Robert Osfield 2005-10-12 18:42:36 +00:00
parent 540e676dae
commit 1e508d432b
16 changed files with 611 additions and 73 deletions

View File

@ -163,6 +163,10 @@ SOURCE=..\..\src\osgParticle\SmokeEffect.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\..\src\osgParticle\SmokeTrailEffect.cpp
# End Source File
# Begin Source File
SOURCE=..\..\src\osgParticle\Version.cpp SOURCE=..\..\src\osgParticle\Version.cpp
# End Source File # End Source File
# End Group # End Group
@ -187,6 +191,10 @@ SOURCE=..\..\include\osgParticle\Counter
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\..\include\osgParticle\ConstantRateCounter
# End Source File
# Begin Source File
SOURCE=..\..\include\osgParticle\Emitter SOURCE=..\..\include\osgParticle\Emitter
# End Source File # End Source File
# Begin Source File # Begin Source File
@ -307,6 +315,10 @@ SOURCE=..\..\include\osgParticle\SmokeEffect
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\..\include\osgParticle\SmokeTrailEffect
# End Source File
# Begin Source File
SOURCE=..\..\include\osgParticle\VariableRateCounter SOURCE=..\..\include\osgParticle\VariableRateCounter
# End Source File # End Source File
# Begin Source File # Begin Source File

View File

@ -16,6 +16,7 @@
#include <osgParticle/ExplosionEffect> #include <osgParticle/ExplosionEffect>
#include <osgParticle/ExplosionDebrisEffect> #include <osgParticle/ExplosionDebrisEffect>
#include <osgParticle/SmokeEffect> #include <osgParticle/SmokeEffect>
#include <osgParticle/SmokeTrailEffect>
#include <osgParticle/FireEffect> #include <osgParticle/FireEffect>
// for the grid data.. // for the grid data..
@ -276,8 +277,12 @@ public:
osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, scale, intensity); osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, scale, intensity);
osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, scale, intensity); osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, scale, intensity);
osgParticle::ParticleEffect* smoke = new osgParticle::SmokeEffect(position, scale, intensity);
osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, scale, intensity); osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, scale, intensity);
osgParticle::ParticleEffect* smoke = 0;
if (handleMovingModels)
smoke = new osgParticle::SmokeTrailEffect(position, scale, intensity);
else
smoke = new osgParticle::SmokeEffect(position, scale, intensity);
explosion->setWind(wind); explosion->setWind(wind);
explosionDebri->setWind(wind); explosionDebri->setWind(wind);

View File

@ -16,7 +16,7 @@
#include <osg/Polytope> #include <osg/Polytope>
#include <osg/ShadowVolumeOccluder> #include <osg/ShadowVolumeOccluder>
#include <osg/Referenced> #include <osg/Viewport>
#include <math.h> #include <math.h>
@ -328,8 +328,11 @@ class OSG_EXPORT CullingSet : public Referenced
void popOccludersCurrentMask(NodePath& nodePath); void popOccludersCurrentMask(NodePath& nodePath);
static osg::Vec4 computePixelSizeVector(const Viewport& W, const Matrix& P, const Matrix& M);
virtual ~CullingSet(); virtual ~CullingSet();
protected: protected:

View File

@ -35,12 +35,10 @@ namespace osgParticle
/// Create a new particle from the specified template (or the default one if <CODE>ptemplate</CODE> is null). /// Create a new particle from the specified template (or the default one if <CODE>ptemplate</CODE> is null).
virtual Particle* createParticle(const Particle* ptemplate); virtual Particle* createParticle(const Particle* ptemplate);
/// Destroy the i-th particle. /// Reuse the i-th particle.
virtual void destroyParticle(int i); virtual void reuseParticle(int i);
/// Update the particles. Don't call this directly, use a <CODE>ConnectedParticleSystemUpdater</CODE> instead.
virtual void update(double dt);
/// Draw the connected particles as either a line or a quad strip, depending upon viewing distance. .
virtual void drawImplementation(osg::State& state) const; virtual void drawImplementation(osg::State& state) const;
protected: protected:
@ -49,8 +47,8 @@ namespace osgParticle
ConnectedParticleSystem& operator=(const ConnectedParticleSystem&) { return *this; } ConnectedParticleSystem& operator=(const ConnectedParticleSystem&) { return *this; }
Particle* _startParticle; int _startParticle;
Particle* _lastParticleCreated; int _lastParticleCreated;
}; };
} }

View File

@ -0,0 +1,75 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGPARTICLE_CONSTANTRATECOUNTER
#define OSGPARTICLE_CONSTANTRATECOUNTER 1
#include <osgParticle/Counter>
#include <osg/Object>
#include <osg/Math>
namespace osgParticle
{
class ConstantRateCounter: public Counter {
public:
ConstantRateCounter():
Counter(),
_minimumNumberOfParticlesToCreate(0),
_numberOfParticlesPerSecondToCreate(0)
{
}
ConstantRateCounter(const ConstantRateCounter& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY):
Counter(copy, copyop),
_minimumNumberOfParticlesToCreate(copy._minimumNumberOfParticlesToCreate),
_numberOfParticlesPerSecondToCreate(copy._numberOfParticlesPerSecondToCreate)
{
}
META_Object(osgParticle, ConstantRateCounter);
void setMinimumNumberOfParticlesToCreate(int minNumToCreate) { _minimumNumberOfParticlesToCreate = minNumToCreate; }
int getMinimumNumberOfParticlesToCreate() const { return _minimumNumberOfParticlesToCreate; }
void setNumberOfParticlesPerSecondToCreate(double numPerSecond) { _numberOfParticlesPerSecondToCreate = numPerSecond; }
double getNumberOfParticlesPerSecondToCreate() const { return _numberOfParticlesPerSecondToCreate; }
/// Return the number of particles to be created in this frame
virtual int numParticlesToCreate(double dt) const
{
double v = (dt*_numberOfParticlesPerSecondToCreate);
int i = (int)(v);
_carryOver += (v-(double)i);
if (_carryOver>1.0)
{
++i;
_carryOver -= 1.0;
}
return osg::maximum(_minimumNumberOfParticlesToCreate, i);
}
protected:
virtual ~ConstantRateCounter() {}
int _minimumNumberOfParticlesToCreate;
double _numberOfParticlesPerSecondToCreate;
mutable double _carryOver;
};
}
#endif

View File

@ -28,6 +28,9 @@
namespace osgParticle namespace osgParticle
{ {
// forward declare so we can reference it
class ParticleSystem;
/** Implementation of a <B>particle</B>. /** Implementation of a <B>particle</B>.
Objects of this class are particles, they have some graphical properties Objects of this class are particles, they have some graphical properties
and some physical properties. Particles are created by emitters and then placed and some physical properties. Particles are created by emitters and then placed
@ -45,6 +48,11 @@ namespace osgParticle
class OSGPARTICLE_EXPORT Particle { class OSGPARTICLE_EXPORT Particle {
public: public:
enum
{
INVALID_INDEX = -1
};
/** /**
Shape of particles. Shape of particles.
NOTE: the LINE shape should be used in conjunction with FIXED alignment mode (see ParticleSystem). NOTE: the LINE shape should be used in conjunction with FIXED alignment mode (see ParticleSystem).
@ -126,6 +134,18 @@ namespace osgParticle
/// Get the previous angle vector. /// Get the previous angle vector.
inline const osg::Vec3& getPreviousAngle() const; inline const osg::Vec3& getPreviousAngle() const;
/// Get the current color
inline const osg::Vec4& getCurrentColor() const { return _current_color; }
/// Get the current alpha
inline float getCurrentAlpha() const { return _current_alpha; }
/// Get the s texture coordinate of the bottom left of the particle
inline const float getSTexCoord() const { return _s_coord; }
/// Get the t texture coordinate of the bottom left of the particle
inline float getTCoord() const { return _t_coord; }
/** Kill the particle on next update /** Kill the particle on next update
NOTE: after calling this function, the <CODE>isAlive()</CODE> method will still NOTE: after calling this function, the <CODE>isAlive()</CODE> method will still
return true until the particle is updated again. return true until the particle is updated again.
@ -216,10 +236,26 @@ namespace osgParticle
/// Get the current (interpolated) polygon size. Valid only after the first call to update(). /// Get the current (interpolated) polygon size. Valid only after the first call to update().
inline float getCurrentSize() const; inline float getCurrentSize() const;
// Specify how the particle texture is tiled /// Specify how the particle texture is tiled
inline void setTextureTile(int sTile, int tTile, int numTiles = 0); inline void setTextureTile(int sTile, int tTile, int numTiles = 0);
private: /// Set the previous particle
inline void setPreviousParticle(int previous) { _previousParticle = previous; }
/// Get the previous particle
inline int getPreviousParticle() const { return _previousParticle; }
/// Set the next particle
inline void setNextParticle(int next) { _nextParticle = next; }
/// Get the const next particle
inline int getNextParticle() const { return _nextParticle; }
/// Method for initializing a particles texture coords as part of a connected particle system.
void setUpTexCoordsAsPartOfConnectedParticleSystem(ParticleSystem* ps);
protected:
Shape _shape; Shape _shape;
rangef _sr; rangef _sr;
@ -259,8 +295,8 @@ namespace osgParticle
float _t_coord; float _t_coord;
// previous and next Particles are only used in ConnectedParticleSystems // previous and next Particles are only used in ConnectedParticleSystems
Particle* _previousParticle; int _previousParticle;
Particle* _nextParticle; int _nextParticle;
}; };
// INLINE FUNCTIONS // INLINE FUNCTIONS

View File

@ -123,6 +123,9 @@ namespace osgParticle
/// Destroy the i-th particle. /// Destroy the i-th particle.
inline virtual void destroyParticle(int i); inline virtual void destroyParticle(int i);
/// Reuse the i-th particle.
inline virtual void reuseParticle(int i) { _deadparts.push(&(_particles[i])); }
/// Get the last frame number. /// Get the last frame number.
inline int getLastFrameNumber() const; inline int getLastFrameNumber() const;
@ -170,7 +173,6 @@ namespace osgParticle
inline void update_bounds(const osg::Vec3& p, float r); inline void update_bounds(const osg::Vec3& p, float r);
void single_pass_render(osg::State& state, const osg::Matrix& modelview) const; void single_pass_render(osg::State& state, const osg::Matrix& modelview) const;
private:
typedef std::vector<Particle> Particle_vector; typedef std::vector<Particle> Particle_vector;
typedef std::stack<Particle*> Death_stack; typedef std::stack<Particle*> Death_stack;

View File

@ -0,0 +1,52 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGPARTICLE_SMOKETRAILEFFECT
#define OSGPARTICLE_SMOKETRAILEFFECT
#include <osgParticle/ParticleEffect>
#include <osgParticle/ModularEmitter>
#include <osgParticle/FluidProgram>
namespace osgParticle
{
class OSGPARTICLE_EXPORT SmokeTrailEffect : public ParticleEffect
{
public:
SmokeTrailEffect(const osg::Vec3& position=osg::Vec3(0.0f,0.0f,0.0f), float scale=1.0f, float intensity=1.0f);
SmokeTrailEffect(const SmokeTrailEffect& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
META_Node(osgParticle,SmokeTrailEffect);
virtual void setDefaults();
virtual void setUpEmitterAndProgram();
virtual Emitter* getEmitter() { return _emitter.get(); }
virtual const Emitter* getEmitter() const { return _emitter.get(); }
virtual Program* getProgram() { return _program.get(); }
virtual const Program* getProgram() const { return _program.get(); }
protected:
osg::ref_ptr<ModularEmitter> _emitter;
osg::ref_ptr<FluidProgram> _program;
};
}
#endif

View File

@ -80,36 +80,7 @@ void CullStack::pushCullingSet()
const osg::Matrix& P = *_projectionStack.back(); const osg::Matrix& P = *_projectionStack.back();
const osg::Matrix& M = *_modelviewStack.back(); const osg::Matrix& M = *_modelviewStack.back();
// pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix. osg::Vec4 pixelSizeVector = CullingSet::computePixelSizeVector(W,P,M);
// here we do it in short hand with the knowledge of how the window matrix is formed
// note P23,P33 are multiplied by an implicit 1 which would come from the window matrix.
// Robert Osfield, June 2002.
// scaling for horizontal pixels
float P00 = P(0,0)*W.width()*0.5f;
float P20_00 = P(2,0)*W.width()*0.5f + P(2,3)*W.width()*0.5f;
osg::Vec3 scale_00(M(0,0)*P00 + M(0,2)*P20_00,
M(1,0)*P00 + M(1,2)*P20_00,
M(2,0)*P00 + M(2,2)*P20_00);
// scaling for vertical pixels
float P10 = P(1,1)*W.height()*0.5f;
float P20_10 = P(2,1)*W.height()*0.5f + P(2,3)*W.height()*0.5f;
osg::Vec3 scale_10(M(0,1)*P10 + M(0,2)*P20_10,
M(1,1)*P10 + M(1,2)*P20_10,
M(2,1)*P10 + M(2,2)*P20_10);
float P23 = P(2,3);
float P33 = P(3,3);
osg::Vec4 pixelSizeVector(M(0,2)*P23,
M(1,2)*P23,
M(2,2)*P23,
M(3,2)*P23 + M(3,3)*P33);
float scaleRatio = 0.7071067811f/sqrtf(scale_00.length2()+scale_10.length2());
pixelSizeVector *= scaleRatio;
if (_index_modelviewCullingStack>=_modelviewCullingStack.size()) if (_index_modelviewCullingStack>=_modelviewCullingStack.size())
{ {

View File

@ -63,3 +63,37 @@ void CullingSet::popOccludersCurrentMask(NodePath& nodePath)
} }
} }
} }
osg::Vec4 CullingSet::computePixelSizeVector(const Viewport& W, const Matrix& P, const Matrix& M)
{
// pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix.
// here we do it in short hand with the knowledge of how the window matrix is formed
// note P23,P33 are multiplied by an implicit 1 which would come from the window matrix.
// Robert Osfield, June 2002.
// scaling for horizontal pixels
float P00 = P(0,0)*W.width()*0.5f;
float P20_00 = P(2,0)*W.width()*0.5f + P(2,3)*W.width()*0.5f;
osg::Vec3 scale_00(M(0,0)*P00 + M(0,2)*P20_00,
M(1,0)*P00 + M(1,2)*P20_00,
M(2,0)*P00 + M(2,2)*P20_00);
// scaling for vertical pixels
float P10 = P(1,1)*W.height()*0.5f;
float P20_10 = P(2,1)*W.height()*0.5f + P(2,3)*W.height()*0.5f;
osg::Vec3 scale_10(M(0,1)*P10 + M(0,2)*P20_10,
M(1,1)*P10 + M(1,2)*P20_10,
M(2,1)*P10 + M(2,2)*P20_10);
float P23 = P(2,3);
float P33 = P(3,3);
osg::Vec4 pixelSizeVector(M(0,2)*P23,
M(1,2)*P23,
M(2,2)*P23,
M(3,2)*P23 + M(3,3)*P33);
float scaleRatio = 0.7071067811f/sqrtf(scale_00.length2()+scale_10.length2());
pixelSizeVector *= scaleRatio;
return pixelSizeVector;
}

View File

@ -12,24 +12,23 @@
*/ */
#include <osg/Notify> #include <osg/Notify>
#include <osg/CullingSet>
#include <osg/io_utils>
#include <osgParticle/ConnectedParticleSystem> #include <osgParticle/ConnectedParticleSystem>
using namespace osgParticle; using namespace osgParticle;
ConnectedParticleSystem::ConnectedParticleSystem(): ConnectedParticleSystem::ConnectedParticleSystem():
_startParticle(0), _startParticle(Particle::INVALID_INDEX),
_lastParticleCreated(0) _lastParticleCreated(Particle::INVALID_INDEX)
{ {
} }
ConnectedParticleSystem::ConnectedParticleSystem(const ConnectedParticleSystem& copy, const osg::CopyOp& copyop): ConnectedParticleSystem::ConnectedParticleSystem(const ConnectedParticleSystem& copy, const osg::CopyOp& copyop):
ParticleSystem(copy,copyop), ParticleSystem(copy,copyop),
_startParticle(0), _startParticle(copy._startParticle),
_lastParticleCreated(0) _lastParticleCreated(copy._lastParticleCreated)
{ {
// need to think about how to copy _startParticle and _lastParticleCreated...
// should we just use indices? Should we compute offsets into the particle system?
osg::notify(osg::NOTICE)<<"Warning: ConnectedParticleSystem copy constructor incomplete."<<std::endl;
} }
ConnectedParticleSystem::~ConnectedParticleSystem() ConnectedParticleSystem::~ConnectedParticleSystem()
@ -38,20 +37,184 @@ ConnectedParticleSystem::~ConnectedParticleSystem()
Particle* ConnectedParticleSystem::createParticle(const Particle* ptemplate) Particle* ConnectedParticleSystem::createParticle(const Particle* ptemplate)
{ {
return ParticleSystem::createParticle(ptemplate); // osg::notify(osg::NOTICE)<<this<< " Creating particle "<<std::endl;
Particle* particle = ParticleSystem::createParticle(ptemplate);
int particleIndex = (int)(particle - &_particles[0]);
if (particle)
{
if (_startParticle == Particle::INVALID_INDEX)
{
// we are the fisrt particle create, so start the connect particle list
_startParticle = particleIndex;
}
if (_lastParticleCreated != Particle::INVALID_INDEX)
{
// osg::notify(osg::NOTICE)<<this<< " Connecting "<<_lastParticleCreated<<" to "<<particleIndex<<std::endl;
// write up the last created particle to this new particle
_particles[_lastParticleCreated].setNextParticle(particleIndex);
particle->setPreviousParticle(_lastParticleCreated);
}
// set the new particle as the last particle created.
_lastParticleCreated = particleIndex;
}
return particle;
} }
void ConnectedParticleSystem::destroyParticle(int i) void ConnectedParticleSystem::reuseParticle(int particleIndex)
{ {
return ParticleSystem::destroyParticle(i); // osg::notify(osg::NOTICE)<<this<< " Reusing particle "<<particleIndex<<std::endl;
}
void ConnectedParticleSystem::update(double dt) if (particleIndex<0 || particleIndex>=(int)_particles.size()) return;
{
ParticleSystem::update(dt); Particle* particle = &_particles[particleIndex];
int previous = particle->getPreviousParticle();
int next = particle->getNextParticle();
// update start and last entries
if (_startParticle == particleIndex)
{
_startParticle = particle->getNextParticle();
}
if (_lastParticleCreated == particleIndex)
{
_lastParticleCreated = Particle::INVALID_INDEX;
}
// join up the previous and next particles to account for
// the deletion of the this particle
if (previous != Particle::INVALID_INDEX)
{
_particles[previous].setNextParticle(next);
}
if (next != Particle::INVALID_INDEX)
{
_particles[next].setPreviousParticle(previous);
}
// reset the next and previous particle entries of this particle
particle->setPreviousParticle(Particle::INVALID_INDEX);
particle->setNextParticle(Particle::INVALID_INDEX);
// do the actual destroy of the particle
ParticleSystem::destroyParticle(particleIndex);
} }
void ConnectedParticleSystem::drawImplementation(osg::State& state) const void ConnectedParticleSystem::drawImplementation(osg::State& state) const
{ {
ParticleSystem::drawImplementation(state); const Particle* particle = (_startParticle != Particle::INVALID_INDEX) ? &_particles[_startParticle] : 0;
if (!particle) return;
osg::Vec4 pixelSizeVector = osg::CullingSet::computePixelSizeVector(*state.getCurrentViewport(),state.getProjectionMatrix(),state.getModelViewMatrix());
float unitPixelSize = fabs(1.0/(particle->getPosition()*pixelSizeVector));
float pixelSizeOfFirstParticle = unitPixelSize * particle->getCurrentSize();
float desiredGapBetweenDrawnParticles = 50.0f/unitPixelSize;
float desiredGapBetweenDrawnParticles2 = desiredGapBetweenDrawnParticles*desiredGapBetweenDrawnParticles;
unsigned int maxNumParticlesToSkip = 200;
float maxPixelError2 = osg::square(1.0f/unitPixelSize);
if (pixelSizeOfFirstParticle<1.0)
{
// draw the connected particles as a line
glBegin(GL_LINE_STRIP);
while(particle != 0)
{
const osg::Vec4& color = particle->getCurrentColor();
const osg::Vec3& pos = particle->getPosition();
glColor4f( color.r(), color.g(), color.b(), color.a() * particle->getCurrentAlpha());
glTexCoord2f( particle->getSTexCoord(), 0.5f );
glVertex3fv(pos.ptr());
const Particle* nextParticle = (particle->getNextParticle() != Particle::INVALID_INDEX) ? &_particles[particle->getNextParticle()] : 0;
if (nextParticle)
{
const osg::Vec3& nextPos = nextParticle->getPosition();
osg::Vec3 startDelta = nextPos-pos;
startDelta.normalize();
float distance2 = 0.0;
// now skip particles of required
for(unsigned int i=0;
i<maxNumParticlesToSkip && ((distance2<maxPixelError2) && (nextParticle->getNextParticle()!=Particle::INVALID_INDEX));
++i)
{
nextParticle = &_particles[nextParticle->getNextParticle()];
const osg::Vec3& nextPos = nextParticle->getPosition();
osg::Vec3 delta = nextPos-pos;
distance2 = (delta^startDelta).length2();
}
}
particle = nextParticle;
}
glEnd();
}
else
{
// draw the connected particles as a quad stripped aligned to be orthogonal to the eye
osg::Matrix eyeToLocalTransform;
eyeToLocalTransform.invert(state.getModelViewMatrix());
osg::Vec3 eyeLocal = osg::Vec3(0.0f,0.0,0.0f)*eyeToLocalTransform;
osg::Vec3 delta(0.0f,0.0f,1.0f);
glBegin(GL_QUAD_STRIP);
while(particle != 0)
{
const osg::Vec4& color = particle->getCurrentColor();
const osg::Vec3& pos = particle->getPosition();
const Particle* nextParticle = (particle->getNextParticle() != Particle::INVALID_INDEX) ? &_particles[particle->getNextParticle()] : 0;
if (nextParticle)
{
const osg::Vec3& nextPos = nextParticle->getPosition();
osg::Vec3 startDelta = nextPos-pos;
startDelta.normalize();
float distance2 = 0.0;
// now skip particles of required
for(unsigned int i=0;
i<maxNumParticlesToSkip && ((distance2<maxPixelError2) && (nextParticle->getNextParticle()!=Particle::INVALID_INDEX));
++i)
{
nextParticle = &_particles[nextParticle->getNextParticle()];
const osg::Vec3& nextPos = nextParticle->getPosition();
delta = nextPos-pos;
distance2 = (delta^startDelta).length2();
}
delta = nextPos-pos;
}
osg::Vec3 normal( delta ^ (pos-eyeLocal));
normal.normalize();
normal *= particle->getCurrentSize();
osg::Vec3 bottom(pos-normal);
osg::Vec3 top(pos+normal);
glColor4f( color.r(), color.g(), color.b(), color.a() * particle->getCurrentAlpha());
glTexCoord2f( particle->getSTexCoord(), 0.0f );
glVertex3fv(bottom.ptr());
glTexCoord2f( particle->getSTexCoord(), 1.0f );
glVertex3fv(top.ptr());
particle = nextParticle;
}
glEnd();
}
} }

View File

@ -6,6 +6,7 @@ CXXFILES =\
ExplosionEffect.cpp\ ExplosionEffect.cpp\
ExplosionDebrisEffect.cpp\ ExplosionDebrisEffect.cpp\
SmokeEffect.cpp\ SmokeEffect.cpp\
SmokeTrailEffect.cpp\
FireEffect.cpp\ FireEffect.cpp\
FluidFrictionOperator.cpp\ FluidFrictionOperator.cpp\
FluidProgram.cpp\ FluidProgram.cpp\

View File

@ -1,5 +1,6 @@
#include <osgParticle/ModularEmitter> #include <osgParticle/ModularEmitter>
#include <osgParticle/Emitter> #include <osgParticle/Emitter>
#include <osgParticle/ConnectedParticleSystem>
#include <osg/Notify> #include <osg/Notify>
osgParticle::ModularEmitter::ModularEmitter() osgParticle::ModularEmitter::ModularEmitter()
@ -22,6 +23,8 @@ osgParticle::ModularEmitter::ModularEmitter(const ModularEmitter& copy, const os
void osgParticle::ModularEmitter::emit(double dt) void osgParticle::ModularEmitter::emit(double dt)
{ {
ConnectedParticleSystem* cps = dynamic_cast<ConnectedParticleSystem*>(getParticleSystem());
if (getReferenceFrame() == RELATIVE_RF) if (getReferenceFrame() == RELATIVE_RF)
{ {
const osg::Matrix& ltw = getLocalToWorldMatrix(); const osg::Matrix& ltw = getLocalToWorldMatrix();
@ -60,6 +63,9 @@ void osgParticle::ModularEmitter::emit(double dt)
float r = ((float)rand()/(float)RAND_MAX); float r = ((float)rand()/(float)RAND_MAX);
P->transformPositionVelocity(ltw, previous_ltw, r); P->transformPositionVelocity(ltw, previous_ltw, r);
//P->transformPositionVelocity(ltw); //P->transformPositionVelocity(ltw);
if (cps) P->setUpTexCoordsAsPartOfConnectedParticleSystem(cps);
} }
else else
{ {
@ -77,6 +83,8 @@ void osgParticle::ModularEmitter::emit(double dt)
{ {
_placer->place(P); _placer->place(P);
_shooter->shoot(P); _shooter->shoot(P);
if (cps) P->setUpTexCoordsAsPartOfConnectedParticleSystem(cps);
} }
} }
} }

View File

@ -1,5 +1,6 @@
#include <osgParticle/Particle> #include <osgParticle/Particle>
#include <osgParticle/LinearInterpolator> #include <osgParticle/LinearInterpolator>
#include <osgParticle/ParticleSystem>
#include <osg/Vec3> #include <osg/Vec3>
#include <osg/Vec4> #include <osg/Vec4>
@ -48,8 +49,8 @@ osgParticle::Particle::Particle()
_cur_tile(-1), _cur_tile(-1),
_s_coord(0.0f), _s_coord(0.0f),
_t_coord(0.0f), _t_coord(0.0f),
_previousParticle(0), _previousParticle(INVALID_INDEX),
_nextParticle(0) _nextParticle(INVALID_INDEX)
{ {
} }
@ -81,10 +82,14 @@ bool osgParticle::Particle::update(double dt)
int currentTile = static_cast<int>(x * _num_tile); int currentTile = static_cast<int>(x * _num_tile);
//If the current texture tile is different from previous, then compute new texture coords //If the current texture tile is different from previous, then compute new texture coords
if(currentTile != _cur_tile) { if(currentTile != _cur_tile)
{
_cur_tile = currentTile; _cur_tile = currentTile;
_s_coord = _s_tile * fmod(_cur_tile , 1.0 / _s_tile); _s_coord = _s_tile * fmod(_cur_tile , 1.0 / _s_tile);
_t_coord = 1.0 - _t_tile * (static_cast<int>(_cur_tile * _t_tile) + 1); _t_coord = 1.0 - _t_tile * (static_cast<int>(_cur_tile * _t_tile) + 1);
// osg::notify(osg::NOTICE)<<this<<" setting tex coords "<<_s_coord<<" "<<_t_coord<<std::endl;
} }
// compute the current values for size, alpha and color. // compute the current values for size, alpha and color.
@ -215,3 +220,23 @@ void osgParticle::Particle::render(const osg::Vec3& xpos, const osg::Vec3& px, c
osg::notify(osg::WARN) << "Invalid shape for particles\n"; osg::notify(osg::WARN) << "Invalid shape for particles\n";
} }
} }
void osgParticle::Particle::setUpTexCoordsAsPartOfConnectedParticleSystem(ParticleSystem* ps)
{
if (getPreviousParticle()!=Particle::INVALID_INDEX)
{
update(0.0);
Particle* previousParticle = ps->getParticle(getPreviousParticle());
const osg::Vec3& previousPosition = previousParticle->getPosition();
const osg::Vec3& newPosition = getPosition();
float distance = (newPosition-previousPosition).length();
float s_coord_delta = 0.5f*distance/getCurrentSize();
float s_coord = previousParticle->_s_coord + s_coord_delta;
setTextureTile(0,0,0);
_cur_tile = 0;
_s_coord = s_coord;
_t_coord = 0.0f;
}
}

View File

@ -68,17 +68,19 @@ void osgParticle::ParticleSystem::update(double dt)
// reset bounds // reset bounds
_reset_bounds_flag = true; _reset_bounds_flag = true;
// set up iterators for particles
Particle_vector::iterator i;
Particle_vector::iterator end = _particles.end();
// update particles for(unsigned int i=0; i<_particles.size(); ++i)
for (i=_particles.begin(); i!=end; ++i) { {
if (i->isAlive()) { Particle& particle = _particles[i];
if (i->update(dt)) { if (particle.isAlive())
update_bounds(i->getPosition(), i->getCurrentSize()); {
} else { if (particle.update(dt))
_deadparts.push(&(*i)); {
update_bounds(particle.getPosition(), particle.getCurrentSize());
}
else
{
reuseParticle(i);
} }
} }
} }
@ -141,6 +143,8 @@ void osgParticle::ParticleSystem::setDefaultAttributes(const std::string& textur
texture->setImage(osgDB::readImageFile(texturefile)); texture->setImage(osgDB::readImageFile(texturefile));
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR);
stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON);
osg::TexEnv *texenv = new osg::TexEnv; osg::TexEnv *texenv = new osg::TexEnv;

View File

@ -0,0 +1,149 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <osgParticle/SmokeTrailEffect>
#include <osgParticle/ConstantRateCounter>
#include <osgParticle/RadialShooter>
#include <osgParticle/SectorPlacer>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/ConnectedParticleSystem>
#include <osg/Geode>
using namespace osgParticle;
SmokeTrailEffect::SmokeTrailEffect(const osg::Vec3& position, float scale, float intensity)
{
setDefaults();
_position = position;
_scale = scale;
_intensity = intensity;
_emitterDuration = 65.0;
_defaultParticleTemplate.setLifeTime(5.0*_scale);
buildEffect();
}
SmokeTrailEffect::SmokeTrailEffect(const SmokeTrailEffect& copy, const osg::CopyOp& copyop):
ParticleEffect(copy,copyop)
{
}
void SmokeTrailEffect::setDefaults()
{
ParticleEffect::setDefaults();
_textureFileName = "Images/continous_smoke.rgb";
_emitterDuration = 65.0;
// set up unit particle.
_defaultParticleTemplate.setLifeTime(5.0*_scale);
_defaultParticleTemplate.setSizeRange(osgParticle::rangef(0.75f, 2.0f));
_defaultParticleTemplate.setAlphaRange(osgParticle::rangef(0.7f, 1.0f));
_defaultParticleTemplate.setColorRange(osgParticle::rangev4(
osg::Vec4(1, 1.0f, 1.0f, 1.0f),
osg::Vec4(1, 1.0f, 1.f, 0.0f)));
}
void SmokeTrailEffect::setUpEmitterAndProgram()
{
// set up particle system
if (!_particleSystem)
{
_particleSystem = new osgParticle::ConnectedParticleSystem;
}
if (_particleSystem.valid())
{
_particleSystem->setDefaultAttributes(_textureFileName, false, false);
osgParticle::Particle& ptemplate = _particleSystem->getDefaultParticleTemplate();
float radius = 0.5f*_scale;
float density = 1.0f; // 1.0kg/m^3
ptemplate.setLifeTime(_defaultParticleTemplate.getLifeTime());
// the following ranges set the envelope of the respective
// graphical properties in time.
ptemplate.setSizeRange(osgParticle::rangef(radius*_defaultParticleTemplate.getSizeRange().minimum,
radius*_defaultParticleTemplate.getSizeRange().maximum));
ptemplate.setAlphaRange(_defaultParticleTemplate.getAlphaRange());
ptemplate.setColorRange(_defaultParticleTemplate.getColorRange());
// these are physical properties of the particle
ptemplate.setRadius(radius);
ptemplate.setMass(density*radius*radius*radius*osg::PI*4.0f/3.0f);
}
// set up emitter
if (!_emitter)
{
_emitter = new osgParticle::ModularEmitter;
_emitter->setCounter(new osgParticle::ConstantRateCounter);
_emitter->setPlacer(new osgParticle::SectorPlacer);
_emitter->setShooter(new osgParticle::RadialShooter);
}
if (_emitter.valid())
{
_emitter->setParticleSystem(_particleSystem.get());
_emitter->setReferenceFrame(_useLocalParticleSystem?
osgParticle::ParticleProcessor::ABSOLUTE_RF:
osgParticle::ParticleProcessor::RELATIVE_RF);
_emitter->setStartTime(_startTime);
_emitter->setLifeTime(_emitterDuration);
_emitter->setEndless(false);
osgParticle::ConstantRateCounter* counter = dynamic_cast<osgParticle::ConstantRateCounter*>(_emitter->getCounter());
if (counter)
{
counter->setMinimumNumberOfParticlesToCreate(1);
counter->setNumberOfParticlesPerSecondToCreate(0.0);
}
osgParticle::SectorPlacer* placer = dynamic_cast<osgParticle::SectorPlacer*>(_emitter->getPlacer());
if (placer)
{
placer->setCenter(_position);
placer->setRadiusRange(0.0f*_scale,0.0f*_scale);
}
osgParticle::RadialShooter* shooter = dynamic_cast<osgParticle::RadialShooter*>(_emitter->getShooter());
if (shooter)
{
shooter->setThetaRange(0.0f, 0.0f);
shooter->setInitialSpeedRange(0.0f*_scale,0.0f*_scale);
}
}
// set up program.
if (!_program)
{
_program = new osgParticle::FluidProgram;
}
if (_program.valid())
{
_program->setParticleSystem(_particleSystem.get());
_program->setWind(_wind);
}
}