diff --git a/examples/osgparticleeffects/osgparticleeffects.cpp b/examples/osgparticleeffects/osgparticleeffects.cpp index 2226e16dd..e100bb9f3 100644 --- a/examples/osgparticleeffects/osgparticleeffects.cpp +++ b/examples/osgparticleeffects/osgparticleeffects.cpp @@ -62,7 +62,7 @@ osg::Node* createMovingModel(const osg::Vec3& center, float radius) if (glider) { const osg::BoundingSphere& bs = glider->getBound(); - float size = radius/bs.radius()*0.3f; + float size = radius/bs.radius()*0.15f; osg::MatrixTransform* positioned = new osg::MatrixTransform; positioned->setDataVariance(osg::Object::STATIC); @@ -84,7 +84,7 @@ osg::Node* createMovingModel(const osg::Vec3& center, float radius) if (cessna) { const osg::BoundingSphere& bs = cessna->getBound(); - float size = radius/bs.radius()*0.3f; + float size = radius/bs.radius()*0.15f; osg::MatrixTransform* positioned = new osg::MatrixTransform; positioned->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); @@ -220,7 +220,7 @@ void build_world(osg::Group *root) // create the moving models. { - root->addChild(createMovingModel(osg::Vec3(500.0f,500.0f,500.0f),100.0f)); + root->addChild(createMovingModel(osg::Vec3(500.0f,500.0f,500.0f),300.0f)); } } @@ -271,12 +271,12 @@ public: } osg::Vec3 position = handleMovingModels ? hit.getLocalIntersectPoint() : hit.getWorldIntersectPoint(); - float scale = 20.0f * ((float)rand() / (float)RAND_MAX); - float intensity = handleMovingModels ? 5.0f : 1.0f; + float scale = 10.0f * ((float)rand() / (float)RAND_MAX); + float intensity = 1.0f; osgParticle::ExplosionEffect* explosion = new osgParticle::ExplosionEffect(position, scale, intensity); osgParticle::ExplosionDebrisEffect* explosionDebri = new osgParticle::ExplosionDebrisEffect(position, scale, intensity); - osgParticle::SmokeEffect* smoke = new osgParticle::SmokeEffect(position, scale, intensity); + osgParticle::ParticleEffect* smoke = new osgParticle::SmokeEffect(position, scale, intensity); osgParticle::FireEffect* fire = new osgParticle::FireEffect(position, scale, intensity); explosion->setWind(wind); diff --git a/include/osgParticle/ModularEmitter b/include/osgParticle/ModularEmitter index 7b27f3c90..412ec740c 100644 --- a/include/osgParticle/ModularEmitter +++ b/include/osgParticle/ModularEmitter @@ -57,6 +57,13 @@ namespace osgParticle /// Set the Counter object. inline void setCounter(Counter* c); + /// Get the ratio between number of particle to create in compenstation for movement of the emitter + inline float getNumParticlesToCreateMovementCompenstationRatio() const; + + /// Set the ratio between number of particle to create in compenstation for movement of the emitter + inline void setNumParticlesToCreateMovementCompenstationRatio(float r); + + /// Get the Placer object. inline Placer* getPlacer(); @@ -82,6 +89,8 @@ namespace osgParticle virtual void emit(double dt); private: + + float _numParticleToCreateMovementCompensationRatio; osg::ref_ptr _counter; osg::ref_ptr _placer; osg::ref_ptr _shooter; @@ -104,6 +113,16 @@ namespace osgParticle _counter = c; } + inline float ModularEmitter::getNumParticlesToCreateMovementCompenstationRatio() const + { + return _numParticleToCreateMovementCompensationRatio; + } + + inline void ModularEmitter::setNumParticlesToCreateMovementCompenstationRatio(float r) + { + _numParticleToCreateMovementCompensationRatio = r; + } + inline Placer* ModularEmitter::getPlacer() { return _placer.get(); diff --git a/include/osgParticle/MultiSegmentPlacer b/include/osgParticle/MultiSegmentPlacer index 1b830154d..eb6e8617f 100644 --- a/include/osgParticle/MultiSegmentPlacer +++ b/include/osgParticle/MultiSegmentPlacer @@ -64,6 +64,9 @@ namespace osgParticle { /// Place a partice. Called automatically by ModularEmitter, do not call this method manually. void place(Particle* P) const; + /// return the control position + inline osg::Vec3 getControlPosition() const; + protected: virtual ~MultiSegmentPlacer() {} MultiSegmentPlacer& operator=(const MultiSegmentPlacer&) { return *this; } @@ -123,6 +126,11 @@ namespace osgParticle { _vx.erase(_vx.begin()+i); recompute_length(); } + + inline osg::Vec3 MultiSegmentPlacer::getControlPosition() const + { + return _vx.empty() ? osg::Vec3(0.0f,0.0f,0.0f) : _vx[0].first; + } } #endif diff --git a/include/osgParticle/Particle b/include/osgParticle/Particle index 36d6b2eb0..7a4b2b93a 100644 --- a/include/osgParticle/Particle +++ b/include/osgParticle/Particle @@ -178,6 +178,9 @@ namespace osgParticle /// Transform position and velocity vectors by a matrix. inline void transformPositionVelocity(const osg::Matrix& xform); + /// Transform position and velocity vectors by a combination of two matrices + void Particle::transformPositionVelocity(const osg::Matrix& xform1, const osg::Matrix& xform2, float r); + /// Set the angle vector. inline void setAngle(const osg::Vec3& a); @@ -410,14 +413,19 @@ namespace osgParticle inline void Particle::transformPositionVelocity(const osg::Matrix& xform) { - // this should be optimized! - - osg::Vec3 p1 = _position + _velocity; - _position = xform.preMult(_position); - p1 = xform.preMult(p1); - - _velocity = p1 - _position; + _velocity = osg::Matrix::transform3x3(_velocity, xform); + } + + inline void Particle::transformPositionVelocity(const osg::Matrix& xform1, const osg::Matrix& xform2, float r) + { + osg::Vec3 position1 = xform1.preMult(_position); + osg::Vec3 velocity1 = osg::Matrix::transform3x3(_velocity, xform1); + osg::Vec3 position2 = xform2.preMult(_position); + osg::Vec3 velocity2 = osg::Matrix::transform3x3(_velocity, xform2); + float one_minus_r = 1.0f-r; + _position = position1*r + position2*one_minus_r; + _velocity = velocity1*r + velocity2*one_minus_r; } inline void Particle::setAngle(const osg::Vec3& a) diff --git a/include/osgParticle/ParticleProcessor b/include/osgParticle/ParticleProcessor index bb6bd6e1a..0d0867fcb 100644 --- a/include/osgParticle/ParticleProcessor +++ b/include/osgParticle/ParticleProcessor @@ -119,6 +119,13 @@ namespace osgParticle /// Get the current world-to-local transformation matrix (valid only during cull traversal). inline const osg::Matrix& getWorldToLocalMatrix(); + /// Get the previous local-to-world transformation matrix (valid only during cull traversal). + inline const osg::Matrix& getPreviousLocalToWorldMatrix(); + + /// Get the previous world-to-local transformation matrix (valid only during cull traversal). + inline const osg::Matrix& getPreviousWorldToLocalMatrix(); + + /// Transform a point from local to world coordinates (valid only during cull traversal). inline osg::Vec3 transformLocalToWorld(const osg::Vec3& P); @@ -144,10 +151,14 @@ namespace osgParticle bool _enabled; double _t0; osg::ref_ptr _ps; + bool _first_ltw_compute; bool _need_ltw_matrix; + bool _first_wtl_compute; bool _need_wtl_matrix; osg::Matrix _ltw_matrix; osg::Matrix _wtl_matrix; + osg::Matrix _previous_ltw_matrix; + osg::Matrix _previous_wtl_matrix; osg::NodeVisitor* _current_nodevisitor; bool _endless; @@ -252,9 +263,13 @@ namespace osgParticle inline const osg::Matrix& ParticleProcessor::getLocalToWorldMatrix() { if (_need_ltw_matrix) { - _ltw_matrix = osg::Matrix::identity(); - //_current_nodevisitor->getLocalToWorldMatrix(_ltw_matrix, this); + _previous_ltw_matrix = _ltw_matrix; _ltw_matrix = osg::computeLocalToWorld(_current_nodevisitor->getNodePath()); + if (_first_ltw_compute) + { + _previous_ltw_matrix = _ltw_matrix; + _first_ltw_compute = false; + } _need_ltw_matrix = false; } return _ltw_matrix; @@ -263,14 +278,30 @@ namespace osgParticle inline const osg::Matrix& ParticleProcessor::getWorldToLocalMatrix() { if (_need_wtl_matrix) { - _wtl_matrix = osg::Matrix::identity(); - //_current_nodevisitor->getWorldToLocalMatrix(_wtl_matrix, this); + _previous_wtl_matrix = _wtl_matrix; _wtl_matrix = osg::computeWorldToLocal(_current_nodevisitor->getNodePath()); + if (_first_wtl_compute) + { + _previous_wtl_matrix = _wtl_matrix; + _first_wtl_compute = false; + } _need_wtl_matrix = false; } return _wtl_matrix; } + inline const osg::Matrix& ParticleProcessor::getPreviousLocalToWorldMatrix() + { + if (_need_ltw_matrix) getLocalToWorldMatrix(); + return _previous_ltw_matrix; + } + + inline const osg::Matrix& ParticleProcessor::getPreviousWorldToLocalMatrix() + { + if (_need_wtl_matrix) getWorldToLocalMatrix(); + return _previous_wtl_matrix; + } + inline osg::Vec3 ParticleProcessor::transformLocalToWorld(const osg::Vec3& P) { return getLocalToWorldMatrix().preMult(P); diff --git a/include/osgParticle/Placer b/include/osgParticle/Placer index 302f5a19d..1b710d866 100644 --- a/include/osgParticle/Placer +++ b/include/osgParticle/Placer @@ -17,6 +17,7 @@ #include #include +#include namespace osgParticle { @@ -39,6 +40,9 @@ namespace osgParticle /// Place a particle. Must be implemented in descendant classes. virtual void place(Particle* P) const = 0; + /// Return the control position of particles that placer will generate. Must be implemented in descendant classes. + virtual osg::Vec3 getControlPosition() const = 0; + protected: ~Placer() {} Placer& operator=(const Placer& ) { return *this; } diff --git a/include/osgParticle/PointPlacer b/include/osgParticle/PointPlacer index 3f6d6dd0c..d6530aa25 100644 --- a/include/osgParticle/PointPlacer +++ b/include/osgParticle/PointPlacer @@ -41,6 +41,9 @@ namespace osgParticle */ inline void place(Particle* P) const; + /// return the control position + inline osg::Vec3 getControlPosition() const; + protected: virtual ~PointPlacer() {} PointPlacer& operator=(const PointPlacer&) { return *this; } @@ -64,6 +67,11 @@ namespace osgParticle } + inline osg::Vec3 PointPlacer::getControlPosition() const + { + return getCenter(); + } + } diff --git a/include/osgParticle/SectorPlacer b/include/osgParticle/SectorPlacer index 660f12d9d..78a43dd95 100644 --- a/include/osgParticle/SectorPlacer +++ b/include/osgParticle/SectorPlacer @@ -61,6 +61,9 @@ namespace osgParticle /// Place a particle. Do not call it manually. inline void place(Particle* P) const; + /// return the control position + inline osg::Vec3 getControlPosition() const; + protected: virtual ~SectorPlacer() {} SectorPlacer& operator=(const SectorPlacer&) { return *this; } @@ -127,6 +130,10 @@ namespace osgParticle P->setPosition(pos); } + inline osg::Vec3 SectorPlacer::getControlPosition() const + { + return getCenter(); + } } diff --git a/include/osgParticle/SegmentPlacer b/include/osgParticle/SegmentPlacer index eace16fe4..232e923f5 100644 --- a/include/osgParticle/SegmentPlacer +++ b/include/osgParticle/SegmentPlacer @@ -60,6 +60,9 @@ namespace osgParticle { /// Place a particle. This method is called by ModularEmitter, do not call it manually. inline void place(Particle* P) const; + /// return the control position + inline osg::Vec3 getControlPosition() const; + protected: virtual ~SegmentPlacer() {} SegmentPlacer& operator=(const SegmentPlacer&) { return *this; } @@ -121,6 +124,12 @@ namespace osgParticle { { _vertexB.set(x, y, z); } + + inline osg::Vec3 SegmentPlacer::getControlPosition() const + { + return (_vertexA+_vertexB)*0.5f; + } + } diff --git a/include/osgParticle/range b/include/osgParticle/range index 64b87fd7f..d4f3a02c2 100644 --- a/include/osgParticle/range +++ b/include/osgParticle/range @@ -70,6 +70,11 @@ namespace osgParticle { return minimum + (maximum - minimum) * sqrtf( static_cast(rand()) / static_cast(RAND_MAX) ); } + + ValueType mid() const + { + return (minimum+maximum)*0.5f; + } }; diff --git a/src/osgParticle/FireEffect.cpp b/src/osgParticle/FireEffect.cpp index 2c1c35d78..c850e4248 100644 --- a/src/osgParticle/FireEffect.cpp +++ b/src/osgParticle/FireEffect.cpp @@ -100,6 +100,7 @@ void FireEffect::setUpEmitterAndProgram() if (!_emitter) { _emitter = new osgParticle::ModularEmitter; + _emitter->setNumParticlesToCreateMovementCompenstationRatio(1.5f); _emitter->setCounter(new osgParticle::RandomRateCounter); _emitter->setPlacer(new osgParticle::SectorPlacer); _emitter->setShooter(new osgParticle::RadialShooter); diff --git a/src/osgParticle/ModularEmitter.cpp b/src/osgParticle/ModularEmitter.cpp index f9876019c..17e753c99 100644 --- a/src/osgParticle/ModularEmitter.cpp +++ b/src/osgParticle/ModularEmitter.cpp @@ -1,16 +1,19 @@ #include #include +#include osgParticle::ModularEmitter::ModularEmitter() : Emitter(), + _numParticleToCreateMovementCompensationRatio(0.0f), _counter(new RandomRateCounter), _placer(new PointPlacer), _shooter(new RadialShooter) { } -osgParticle::ModularEmitter::ModularEmitter(const ModularEmitter& copy, const osg::CopyOp& copyop) -: Emitter(copy, copyop), +osgParticle::ModularEmitter::ModularEmitter(const ModularEmitter& copy, const osg::CopyOp& copyop): + Emitter(copy, copyop), + _numParticleToCreateMovementCompensationRatio(copy._numParticleToCreateMovementCompensationRatio), _counter(static_cast(copyop(copy._counter.get()))), _placer(static_cast(copyop(copy._placer.get()))), _shooter(static_cast(copyop(copy._shooter.get()))) @@ -19,14 +22,61 @@ osgParticle::ModularEmitter::ModularEmitter(const ModularEmitter& copy, const os void osgParticle::ModularEmitter::emit(double dt) { - int n = _counter->numParticlesToCreate(dt); - for (int i=0; icreateParticle(getUseDefaultTemplate()? 0: &getParticleTemplate()); - if (P) { - _placer->place(P); - _shooter->shoot(P); - if (getReferenceFrame() == RELATIVE_RF) { - P->transformPositionVelocity(getLocalToWorldMatrix()); + if (getReferenceFrame() == RELATIVE_RF) + { + const osg::Matrix& ltw = getLocalToWorldMatrix(); + const osg::Matrix& previous_ltw = getPreviousLocalToWorldMatrix(); + + int n = _counter->numParticlesToCreate(dt); + + if (_numParticleToCreateMovementCompensationRatio>0.0f) + { + // compute the distance moved between frames + const osg::Vec3 controlPosition = _placer->getControlPosition(); + osg::Vec3 previousPosition = controlPosition * previous_ltw; + osg::Vec3 currentPosition = controlPosition * ltw; + float distance = (currentPosition-previousPosition).length(); + + float size = getUseDefaultTemplate() ? + getParticleSystem()->getDefaultParticleTemplate().getSizeRange().minimum : + getParticleTemplate().getSizeRange().minimum; + + float num_extra_samples = _numParticleToCreateMovementCompensationRatio*distance/size; + float rounded_down = floor(num_extra_samples); + float remainder = num_extra_samples-rounded_down; + + n = osg::maximum(n, int(rounded_down) + (((float) rand() < remainder * (float)RAND_MAX) ? 1 : 0)); + } + + for (int i=0; icreateParticle(getUseDefaultTemplate()? 0: &getParticleTemplate()); + if (P) + { + _placer->place(P); + _shooter->shoot(P); + + // now need to transform the position and velocity because we having a moving model. + float r = ((float)rand()/(float)RAND_MAX); + P->transformPositionVelocity(ltw, previous_ltw, r); + //P->transformPositionVelocity(ltw); + } + else + { + osg::notify(osg::NOTICE)<<"run out of particle"<numParticlesToCreate(dt); + for (int i=0; icreateParticle(getUseDefaultTemplate()? 0: &getParticleTemplate()); + if (P) + { + _placer->place(P); + _shooter->shoot(P); } } } diff --git a/src/osgParticle/ParticleProcessor.cpp b/src/osgParticle/ParticleProcessor.cpp index c3b790fc1..b9ab60cb6 100644 --- a/src/osgParticle/ParticleProcessor.cpp +++ b/src/osgParticle/ParticleProcessor.cpp @@ -17,7 +17,9 @@ osgParticle::ParticleProcessor::ParticleProcessor() _enabled(true), _t0(-1), _ps(0), + _first_ltw_compute(true), _need_ltw_matrix(false), + _first_wtl_compute(true), _need_wtl_matrix(false), _current_nodevisitor(0), _endless(true), @@ -35,7 +37,9 @@ osgParticle::ParticleProcessor::ParticleProcessor(const ParticleProcessor& copy, _enabled(copy._enabled), _t0(copy._t0), _ps(static_cast(copyop(copy._ps.get()))), + _first_ltw_compute(copy._first_ltw_compute), _need_ltw_matrix(copy._need_ltw_matrix), + _first_wtl_compute(copy._first_wtl_compute), _need_wtl_matrix(copy._need_wtl_matrix), _current_nodevisitor(0), _endless(copy._endless), diff --git a/src/osgParticle/SmokeEffect.cpp b/src/osgParticle/SmokeEffect.cpp index 1074582ae..ace6ef06e 100644 --- a/src/osgParticle/SmokeEffect.cpp +++ b/src/osgParticle/SmokeEffect.cpp @@ -50,7 +50,7 @@ void SmokeEffect::setDefaults() // set up unit particle. _defaultParticleTemplate.setLifeTime(5.0*_scale); - _defaultParticleTemplate.setSizeRange(osgParticle::rangef(0.75f, 3.0f)); + _defaultParticleTemplate.setSizeRange(osgParticle::rangef(0.75f, 2.0f)); _defaultParticleTemplate.setAlphaRange(osgParticle::rangef(0.1f, 1.0f)); _defaultParticleTemplate.setColorRange(osgParticle::rangev4( osg::Vec4(1, 1.0f, 1.0f, 1.0f), @@ -96,6 +96,7 @@ void SmokeEffect::setUpEmitterAndProgram() if (!_emitter) { _emitter = new osgParticle::ModularEmitter; + _emitter->setNumParticlesToCreateMovementCompenstationRatio(1.5f); _emitter->setCounter(new osgParticle::RandomRateCounter); _emitter->setPlacer(new osgParticle::SectorPlacer); _emitter->setShooter(new osgParticle::RadialShooter);