393 lines
12 KiB
C++
393 lines
12 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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.
|
|
*/
|
|
//osgParticle - Copyright (C) 2002 Marco Jez
|
|
|
|
#ifndef OSGPARTICLE_PARTICLESYSTEM
|
|
#define OSGPARTICLE_PARTICLESYSTEM 1
|
|
|
|
#include <osgParticle/Export>
|
|
#include <osgParticle/Particle>
|
|
|
|
#include <vector>
|
|
#include <stack>
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
#include <osg/Object>
|
|
#include <osg/Drawable>
|
|
#include <osg/CopyOp>
|
|
#include <osg/State>
|
|
#include <osg/Vec3>
|
|
#include <osg/BoundingBox>
|
|
|
|
#include <OpenThreads/ReadWriteMutex>
|
|
|
|
namespace osgParticle
|
|
{
|
|
|
|
/** The heart of this class library; its purpose is to hold a set of particles and manage particle creation, update, rendering and destruction.
|
|
* You can add this drawable to any Geode as you usually do with other
|
|
* Drawable classes. Each instance of ParticleSystem is a separate set of
|
|
* particles; it provides the interface for creating particles and iterating
|
|
* through them (see the Emitter and Program classes).
|
|
*/
|
|
class OSGPARTICLE_EXPORT ParticleSystem: public osg::Drawable {
|
|
public:
|
|
|
|
enum Alignment {
|
|
BILLBOARD,
|
|
FIXED
|
|
};
|
|
|
|
ParticleSystem();
|
|
ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
|
|
|
|
META_Object(osgParticle, ParticleSystem);
|
|
|
|
/// Get the alignment type of particles.
|
|
inline Alignment getParticleAlignment() const;
|
|
|
|
/// Set the alignment type of particles.
|
|
inline void setParticleAlignment(Alignment a);
|
|
|
|
/// Get the X-axis alignment vector.
|
|
inline const osg::Vec3& getAlignVectorX() const;
|
|
|
|
/// Set the X-axis alignment vector.
|
|
inline void setAlignVectorX(const osg::Vec3& v);
|
|
|
|
/// Get the Y-axis alignment vector.
|
|
inline const osg::Vec3& getAlignVectorY() const;
|
|
|
|
/// Set the Y-axis alignment vector.
|
|
inline void setAlignVectorY(const osg::Vec3& v);
|
|
|
|
/// Set the alignment vectors.
|
|
inline void setAlignVectors(const osg::Vec3& X, const osg::Vec3& Y);
|
|
|
|
/// Get the default bounding box
|
|
inline const osg::BoundingBox& getDefaultBoundingBox() const;
|
|
|
|
/** Set the default bounding box.
|
|
The default bounding box is used when a real bounding box cannot be computed, for example
|
|
because no particles has been updated yet.
|
|
*/
|
|
inline void setDefaultBoundingBox(const osg::BoundingBox& bbox);
|
|
|
|
/// Get the double pass rendering flag.
|
|
inline bool getDoublePassRendering() const;
|
|
|
|
/** Set the double pass rendering flag.
|
|
Double pass rendering avoids overdraw problems between particle systems
|
|
and other opaque objects. If you can render all the particle systems after
|
|
the opaque objects, then double pass is not necessary and can be turned off (best choice).
|
|
If you set the default attributes with setDefaultAttributes, then the particle
|
|
system will fall into a transparent bin.
|
|
*/
|
|
inline void setDoublePassRendering(bool v);
|
|
|
|
/// Return true if the particle system is frozen.
|
|
inline bool isFrozen() const;
|
|
|
|
/** Set or reset the <I>frozen</I> state.
|
|
When the particle system is frozen, emitters and programs won't do anything on it.
|
|
*/
|
|
inline void setFrozen(bool v);
|
|
|
|
/// Get the number of allocated particles (alive + dead).
|
|
inline int numParticles() const;
|
|
|
|
/// Get the number of dead particles.
|
|
inline int numDeadParticles() const;
|
|
|
|
/// Get whether all particles are dead
|
|
inline bool areAllParticlesDead() const { return numDeadParticles()==numParticles(); }
|
|
|
|
/// Get a pointer to the i-th particle.
|
|
inline Particle* getParticle(int i);
|
|
|
|
/// Get a const pointer to the i-th particle.
|
|
inline const Particle* getParticle(int i) const;
|
|
|
|
/// Create a new particle from the specified template (or the default one if <CODE>ptemplate</CODE> is null).
|
|
inline virtual Particle* createParticle(const Particle* ptemplate);
|
|
|
|
/// 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;
|
|
|
|
/// Get a reference to the default particle template.
|
|
inline Particle& getDefaultParticleTemplate();
|
|
|
|
/// Get a const reference to the default particle template.
|
|
inline const Particle& getDefaultParticleTemplate() const;
|
|
|
|
/// Set the default particle template (particle is copied).
|
|
inline void setDefaultParticleTemplate(const Particle& p);
|
|
|
|
/// Get whether the particle system can freeze when culled
|
|
inline bool getFreezeOnCull() const;
|
|
|
|
/// Set whether the particle system can freeze when culled (default is true)
|
|
inline void setFreezeOnCull(bool v);
|
|
|
|
/** A useful method to set the most common <CODE>StateAttribute</CODE>'s in one call.
|
|
If <CODE>texturefile</CODE> is empty, then texturing is turned off.
|
|
*/
|
|
void setDefaultAttributes(const std::string& texturefile = "", bool emissive_particles = true, bool lighting = false, int texture_unit = 0);
|
|
|
|
/// (<B>EXPERIMENTAL</B>) Get the level of detail.
|
|
inline int getLevelOfDetail() const;
|
|
|
|
/** (<B>EXPERIMENTAL</B>) Set the level of detail. The total number of particles is divided by the detail value to
|
|
get the actual number of particles to be drawn. This value must be greater than zero.
|
|
*/
|
|
inline void setLevelOfDetail(int v);
|
|
|
|
/// Update the particles. Don't call this directly, use a <CODE>ParticleSystemUpdater</CODE> instead.
|
|
virtual void update(double dt);
|
|
|
|
virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
|
|
|
|
virtual osg::BoundingBox computeBound() const;
|
|
|
|
OpenThreads::ReadWriteMutex* getReadWriteMutex() const { return &_readWriteMutex; }
|
|
|
|
protected:
|
|
|
|
virtual ~ParticleSystem();
|
|
|
|
ParticleSystem& operator=(const ParticleSystem&) { return *this; }
|
|
|
|
inline void update_bounds(const osg::Vec3& p, float r);
|
|
void single_pass_render(osg::State& state, const osg::Matrix& modelview) const;
|
|
|
|
typedef std::vector<Particle> Particle_vector;
|
|
typedef std::stack<Particle*> Death_stack;
|
|
|
|
Particle_vector _particles;
|
|
Death_stack _deadparts;
|
|
|
|
osg::BoundingBox _def_bbox;
|
|
|
|
Alignment _alignment;
|
|
osg::Vec3 _align_X_axis;
|
|
osg::Vec3 _align_Y_axis;
|
|
|
|
bool _doublepass;
|
|
bool _frozen;
|
|
|
|
osg::Vec3 _bmin;
|
|
osg::Vec3 _bmax;
|
|
|
|
bool _reset_bounds_flag;
|
|
bool _bounds_computed;
|
|
|
|
Particle _def_ptemp;
|
|
mutable int _last_frame;
|
|
bool _freeze_on_cull;
|
|
|
|
int _detail;
|
|
mutable int _draw_count;
|
|
|
|
mutable OpenThreads::ReadWriteMutex _readWriteMutex;
|
|
|
|
};
|
|
|
|
// INLINE FUNCTIONS
|
|
|
|
inline ParticleSystem::Alignment ParticleSystem::getParticleAlignment() const
|
|
{
|
|
return _alignment;
|
|
}
|
|
|
|
inline void ParticleSystem::setParticleAlignment(Alignment a)
|
|
{
|
|
_alignment = a;
|
|
}
|
|
|
|
inline const osg::Vec3& ParticleSystem::getAlignVectorX() const
|
|
{
|
|
return _align_X_axis;
|
|
}
|
|
|
|
inline void ParticleSystem::setAlignVectorX(const osg::Vec3& v)
|
|
{
|
|
_align_X_axis = v;
|
|
}
|
|
|
|
inline const osg::Vec3& ParticleSystem::getAlignVectorY() const
|
|
{
|
|
return _align_Y_axis;
|
|
}
|
|
|
|
inline void ParticleSystem::setAlignVectorY(const osg::Vec3& v)
|
|
{
|
|
_align_Y_axis = v;
|
|
}
|
|
|
|
inline void ParticleSystem::setAlignVectors(const osg::Vec3& X, const osg::Vec3& Y)
|
|
{
|
|
_align_X_axis = X;
|
|
_align_Y_axis = Y;
|
|
}
|
|
|
|
inline bool ParticleSystem::isFrozen() const
|
|
{
|
|
return _frozen;
|
|
}
|
|
|
|
inline void ParticleSystem::setFrozen(bool v)
|
|
{
|
|
_frozen = v;
|
|
}
|
|
|
|
inline const osg::BoundingBox& ParticleSystem::getDefaultBoundingBox() const
|
|
{
|
|
return _def_bbox;
|
|
}
|
|
|
|
inline void ParticleSystem::setDefaultBoundingBox(const osg::BoundingBox& bbox)
|
|
{
|
|
_def_bbox = bbox;
|
|
}
|
|
|
|
inline bool ParticleSystem::getDoublePassRendering() const
|
|
{
|
|
return _doublepass;
|
|
}
|
|
|
|
inline void ParticleSystem::setDoublePassRendering(bool v)
|
|
{
|
|
_doublepass = v;
|
|
}
|
|
|
|
inline int ParticleSystem::numParticles() const
|
|
{
|
|
return static_cast<int>(_particles.size());
|
|
}
|
|
|
|
inline int ParticleSystem::numDeadParticles() const
|
|
{
|
|
return static_cast<int>(_deadparts.size());
|
|
}
|
|
|
|
inline Particle* ParticleSystem::getParticle(int i)
|
|
{
|
|
return &_particles[i];
|
|
}
|
|
|
|
inline const Particle* ParticleSystem::getParticle(int i) const
|
|
{
|
|
return &_particles[i];
|
|
}
|
|
|
|
inline void ParticleSystem::destroyParticle(int i)
|
|
{
|
|
_particles[i].kill();
|
|
}
|
|
|
|
inline int ParticleSystem::getLastFrameNumber() const
|
|
{
|
|
return _last_frame;
|
|
}
|
|
|
|
inline void ParticleSystem::update_bounds(const osg::Vec3& p, float r)
|
|
{
|
|
if (_reset_bounds_flag) {
|
|
_reset_bounds_flag = false;
|
|
_bmin = p - osg::Vec3(r,r,r);
|
|
_bmax = p + osg::Vec3(r,r,r);
|
|
} else {
|
|
if (p.x() - r < _bmin.x()) _bmin.x() = p.x() - r;
|
|
if (p.y() - r < _bmin.y()) _bmin.y() = p.y() - r;
|
|
if (p.z() - r < _bmin.z()) _bmin.z() = p.z() - r;
|
|
if (p.x() + r > _bmax.x()) _bmax.x() = p.x() + r;
|
|
if (p.y() + r > _bmax.y()) _bmax.y() = p.y() + r;
|
|
if (p.z() + r > _bmax.z()) _bmax.z() = p.z() + r;
|
|
}
|
|
if (!_bounds_computed)
|
|
_bounds_computed = true;
|
|
}
|
|
|
|
inline Particle& ParticleSystem::getDefaultParticleTemplate()
|
|
{
|
|
return _def_ptemp;
|
|
}
|
|
|
|
inline const Particle& ParticleSystem::getDefaultParticleTemplate() const
|
|
{
|
|
return _def_ptemp;
|
|
}
|
|
|
|
inline void ParticleSystem::setDefaultParticleTemplate(const Particle& p)
|
|
{
|
|
_def_ptemp = p;
|
|
}
|
|
|
|
inline bool ParticleSystem::getFreezeOnCull() const
|
|
{
|
|
return _freeze_on_cull;
|
|
}
|
|
|
|
inline void ParticleSystem::setFreezeOnCull(bool v)
|
|
{
|
|
_freeze_on_cull = v;
|
|
}
|
|
|
|
inline int ParticleSystem::getLevelOfDetail() const
|
|
{
|
|
return _detail;
|
|
}
|
|
|
|
inline void ParticleSystem::setLevelOfDetail(int v)
|
|
{
|
|
if (v < 1) v = 1;
|
|
_detail = v;
|
|
}
|
|
|
|
// I'm not sure this function should be inlined...
|
|
|
|
inline Particle* ParticleSystem::createParticle(const Particle* ptemplate)
|
|
{
|
|
// is there any dead particle?
|
|
if (!_deadparts.empty()) {
|
|
|
|
// retrieve a pointer to the last dead particle
|
|
Particle* P = _deadparts.top();
|
|
|
|
// create a new (alive) particle in the same place
|
|
*P = ptemplate? *ptemplate: _def_ptemp;
|
|
|
|
// remove the pointer from the death stack
|
|
_deadparts.pop();
|
|
return P;
|
|
|
|
} else {
|
|
|
|
// add a new particle to the vector
|
|
_particles.push_back(ptemplate? *ptemplate: _def_ptemp);
|
|
return &_particles.back();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#endif
|