diff --git a/VisualStudio/osgParticle/osgParticle.dsp b/VisualStudio/osgParticle/osgParticle.dsp index b092f77cc..e0fd9ea2e 100644 --- a/VisualStudio/osgParticle/osgParticle.dsp +++ b/VisualStudio/osgParticle/osgParticle.dsp @@ -163,6 +163,10 @@ SOURCE=..\..\src\osgParticle\SmokeEffect.cpp # End Source File # Begin Source File +SOURCE=..\..\src\osgParticle\SmokeTrailEffect.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osgParticle\Version.cpp # End Source File # End Group @@ -187,6 +191,10 @@ SOURCE=..\..\include\osgParticle\Counter # End Source File # Begin Source File +SOURCE=..\..\include\osgParticle\ConstantRateCounter +# End Source File +# Begin Source File + SOURCE=..\..\include\osgParticle\Emitter # End Source File # Begin Source File @@ -307,6 +315,10 @@ SOURCE=..\..\include\osgParticle\SmokeEffect # End Source File # Begin Source File +SOURCE=..\..\include\osgParticle\SmokeTrailEffect +# End Source File +# Begin Source File + SOURCE=..\..\include\osgParticle\VariableRateCounter # End Source File # Begin Source File diff --git a/examples/osgparticleeffects/osgparticleeffects.cpp b/examples/osgparticleeffects/osgparticleeffects.cpp index e100bb9f3..bb8683009 100644 --- a/examples/osgparticleeffects/osgparticleeffects.cpp +++ b/examples/osgparticleeffects/osgparticleeffects.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include // for the grid data.. @@ -276,8 +277,12 @@ public: osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(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::ParticleEffect* smoke = 0; + if (handleMovingModels) + smoke = new osgParticle::SmokeTrailEffect(position, scale, intensity); + else + smoke = new osgParticle::SmokeEffect(position, scale, intensity); explosion->setWind(wind); explosionDebri->setWind(wind); diff --git a/include/osg/CullingSet b/include/osg/CullingSet index 5bcf8776f..f217bcfc2 100644 --- a/include/osg/CullingSet +++ b/include/osg/CullingSet @@ -16,7 +16,7 @@ #include #include -#include +#include #include @@ -328,7 +328,10 @@ class OSG_EXPORT CullingSet : public Referenced void popOccludersCurrentMask(NodePath& nodePath); + static osg::Vec4 computePixelSizeVector(const Viewport& W, const Matrix& P, const Matrix& M); + virtual ~CullingSet(); + protected: diff --git a/include/osgParticle/ConnectedParticleSystem b/include/osgParticle/ConnectedParticleSystem index 1f12d1dd0..223a4e6f1 100644 --- a/include/osgParticle/ConnectedParticleSystem +++ b/include/osgParticle/ConnectedParticleSystem @@ -35,12 +35,10 @@ namespace osgParticle /// Create a new particle from the specified template (or the default one if ptemplate is null). virtual Particle* createParticle(const Particle* ptemplate); - /// Destroy the i-th particle. - virtual void destroyParticle(int i); + /// Reuse the i-th particle. + virtual void reuseParticle(int i); - /// Update the particles. Don't call this directly, use a ConnectedParticleSystemUpdater 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; protected: @@ -49,8 +47,8 @@ namespace osgParticle ConnectedParticleSystem& operator=(const ConnectedParticleSystem&) { return *this; } - Particle* _startParticle; - Particle* _lastParticleCreated; + int _startParticle; + int _lastParticleCreated; }; } diff --git a/include/osgParticle/ConstantRateCounter b/include/osgParticle/ConstantRateCounter new file mode 100644 index 000000000..98909f91e --- /dev/null +++ b/include/osgParticle/ConstantRateCounter @@ -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 + +#include +#include + +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 diff --git a/include/osgParticle/Particle b/include/osgParticle/Particle index 1b5b2a407..b79941af7 100644 --- a/include/osgParticle/Particle +++ b/include/osgParticle/Particle @@ -28,6 +28,9 @@ namespace osgParticle { + // forward declare so we can reference it + class ParticleSystem; + /** Implementation of a particle. Objects of this class are particles, they have some graphical properties and some physical properties. Particles are created by emitters and then placed @@ -44,6 +47,11 @@ namespace osgParticle */ class OSGPARTICLE_EXPORT Particle { public: + + enum + { + INVALID_INDEX = -1 + }; /** Shape of particles. @@ -125,6 +133,18 @@ namespace osgParticle /// Get the previous angle vector. 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 NOTE: after calling this function, the isAlive() method will still @@ -216,10 +236,26 @@ 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 + /// Specify how the particle texture is tiled 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; rangef _sr; @@ -259,8 +295,8 @@ namespace osgParticle float _t_coord; // previous and next Particles are only used in ConnectedParticleSystems - Particle* _previousParticle; - Particle* _nextParticle; + int _previousParticle; + int _nextParticle; }; // INLINE FUNCTIONS diff --git a/include/osgParticle/ParticleSystem b/include/osgParticle/ParticleSystem index 1aee1229c..9120887f6 100644 --- a/include/osgParticle/ParticleSystem +++ b/include/osgParticle/ParticleSystem @@ -123,6 +123,9 @@ namespace osgParticle /// Destroy the i-th particle. 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. inline int getLastFrameNumber() const; @@ -170,7 +173,6 @@ namespace osgParticle inline void update_bounds(const osg::Vec3& p, float r); void single_pass_render(osg::State& state, const osg::Matrix& modelview) const; - private: typedef std::vector Particle_vector; typedef std::stack Death_stack; diff --git a/include/osgParticle/SmokeTrailEffect b/include/osgParticle/SmokeTrailEffect new file mode 100644 index 000000000..9c2cd54c2 --- /dev/null +++ b/include/osgParticle/SmokeTrailEffect @@ -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 +#include +#include + +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 _emitter; + osg::ref_ptr _program; + + }; +} + +#endif diff --git a/src/osg/CullStack.cpp b/src/osg/CullStack.cpp index d2b404144..2885e4ef5 100644 --- a/src/osg/CullStack.cpp +++ b/src/osg/CullStack.cpp @@ -79,37 +79,8 @@ void CullStack::pushCullingSet() const osg::Viewport& W = *_viewportStack.back(); const osg::Matrix& P = *_projectionStack.back(); const osg::Matrix& M = *_modelviewStack.back(); - - // 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; + osg::Vec4 pixelSizeVector = CullingSet::computePixelSizeVector(W,P,M); if (_index_modelviewCullingStack>=_modelviewCullingStack.size()) { diff --git a/src/osg/CullingSet.cpp b/src/osg/CullingSet.cpp index d458b2a3c..314a1ad6e 100644 --- a/src/osg/CullingSet.cpp +++ b/src/osg/CullingSet.cpp @@ -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; +} diff --git a/src/osgParticle/ConnectedParticleSystem.cpp b/src/osgParticle/ConnectedParticleSystem.cpp index ba70e3ffd..94c3f98f7 100644 --- a/src/osgParticle/ConnectedParticleSystem.cpp +++ b/src/osgParticle/ConnectedParticleSystem.cpp @@ -12,24 +12,23 @@ */ #include +#include +#include #include using namespace osgParticle; ConnectedParticleSystem::ConnectedParticleSystem(): - _startParticle(0), - _lastParticleCreated(0) + _startParticle(Particle::INVALID_INDEX), + _lastParticleCreated(Particle::INVALID_INDEX) { } ConnectedParticleSystem::ConnectedParticleSystem(const ConnectedParticleSystem& copy, const osg::CopyOp& copyop): ParticleSystem(copy,copyop), - _startParticle(0), - _lastParticleCreated(0) + _startParticle(copy._startParticle), + _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."<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); -} - -void ConnectedParticleSystem::update(double dt) -{ - ParticleSystem::update(dt); + // osg::notify(osg::NOTICE)<=(int)_particles.size()) return; + + 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 { - 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; + igetNextParticle()!=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; + igetNextParticle()!=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(); + } + } diff --git a/src/osgParticle/GNUmakefile b/src/osgParticle/GNUmakefile index caaed636d..2335a8366 100644 --- a/src/osgParticle/GNUmakefile +++ b/src/osgParticle/GNUmakefile @@ -6,6 +6,7 @@ CXXFILES =\ ExplosionEffect.cpp\ ExplosionDebrisEffect.cpp\ SmokeEffect.cpp\ + SmokeTrailEffect.cpp\ FireEffect.cpp\ FluidFrictionOperator.cpp\ FluidProgram.cpp\ diff --git a/src/osgParticle/ModularEmitter.cpp b/src/osgParticle/ModularEmitter.cpp index 17e753c99..0b6d9b8f0 100644 --- a/src/osgParticle/ModularEmitter.cpp +++ b/src/osgParticle/ModularEmitter.cpp @@ -1,5 +1,6 @@ #include #include +#include #include osgParticle::ModularEmitter::ModularEmitter() @@ -22,6 +23,8 @@ osgParticle::ModularEmitter::ModularEmitter(const ModularEmitter& copy, const os void osgParticle::ModularEmitter::emit(double dt) { + ConnectedParticleSystem* cps = dynamic_cast(getParticleSystem()); + if (getReferenceFrame() == RELATIVE_RF) { const osg::Matrix& ltw = getLocalToWorldMatrix(); @@ -60,6 +63,9 @@ void osgParticle::ModularEmitter::emit(double dt) float r = ((float)rand()/(float)RAND_MAX); P->transformPositionVelocity(ltw, previous_ltw, r); //P->transformPositionVelocity(ltw); + + if (cps) P->setUpTexCoordsAsPartOfConnectedParticleSystem(cps); + } else { @@ -77,6 +83,8 @@ void osgParticle::ModularEmitter::emit(double dt) { _placer->place(P); _shooter->shoot(P); + + if (cps) P->setUpTexCoordsAsPartOfConnectedParticleSystem(cps); } } } diff --git a/src/osgParticle/Particle.cpp b/src/osgParticle/Particle.cpp index 92b18f9cc..f8c252a5e 100644 --- a/src/osgParticle/Particle.cpp +++ b/src/osgParticle/Particle.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -48,8 +49,8 @@ osgParticle::Particle::Particle() _cur_tile(-1), _s_coord(0.0f), _t_coord(0.0f), - _previousParticle(0), - _nextParticle(0) + _previousParticle(INVALID_INDEX), + _nextParticle(INVALID_INDEX) { } @@ -81,10 +82,14 @@ bool osgParticle::Particle::update(double dt) int currentTile = static_cast(x * _num_tile); //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; _s_coord = _s_tile * fmod(_cur_tile , 1.0 / _s_tile); _t_coord = 1.0 - _t_tile * (static_cast(_cur_tile * _t_tile) + 1); + + // osg::notify(osg::NOTICE)<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; + } +} diff --git a/src/osgParticle/ParticleSystem.cpp b/src/osgParticle/ParticleSystem.cpp index 3d27bc6cb..d207526a5 100644 --- a/src/osgParticle/ParticleSystem.cpp +++ b/src/osgParticle/ParticleSystem.cpp @@ -68,17 +68,19 @@ void osgParticle::ParticleSystem::update(double dt) // reset bounds _reset_bounds_flag = true; - // set up iterators for particles - Particle_vector::iterator i; - Particle_vector::iterator end = _particles.end(); - // update particles - for (i=_particles.begin(); i!=end; ++i) { - if (i->isAlive()) { - if (i->update(dt)) { - update_bounds(i->getPosition(), i->getCurrentSize()); - } else { - _deadparts.push(&(*i)); + for(unsigned int i=0; i<_particles.size(); ++i) + { + Particle& particle = _particles[i]; + if (particle.isAlive()) + { + if (particle.update(dt)) + { + 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->setFilter(osg::Texture2D::MIN_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); osg::TexEnv *texenv = new osg::TexEnv; diff --git a/src/osgParticle/SmokeTrailEffect.cpp b/src/osgParticle/SmokeTrailEffect.cpp new file mode 100644 index 000000000..c2389971e --- /dev/null +++ b/src/osgParticle/SmokeTrailEffect.cpp @@ -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 + +#include +#include +#include +#include +#include + +#include + +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(_emitter->getCounter()); + if (counter) + { + counter->setMinimumNumberOfParticlesToCreate(1); + counter->setNumberOfParticlesPerSecondToCreate(0.0); + } + + osgParticle::SectorPlacer* placer = dynamic_cast(_emitter->getPlacer()); + if (placer) + { + placer->setCenter(_position); + placer->setRadiusRange(0.0f*_scale,0.0f*_scale); + } + + osgParticle::RadialShooter* shooter = dynamic_cast(_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); + } +}