openscenegraph
ParticleSystem
Go to the documentation of this file.
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12*/
13//osgParticle - Copyright (C) 2002 Marco Jez
14
15#ifndef OSGPARTICLE_PARTICLESYSTEM
16#define OSGPARTICLE_PARTICLESYSTEM 1
17
18#include <osgParticle/Export>
19#include <osgParticle/Particle>
20
21#include <vector>
22#include <stack>
23#include <algorithm>
24#include <string>
25
26#include <osg/Object>
27#include <osg/Drawable>
28#include <osg/CopyOp>
29#include <osg/State>
30#include <osg/Vec3>
31#include <osg/BoundingBox>
32
33// 9th Febrary 2009, disabled the use of ReadWriteMutex as it looks like this
34// is introducing threading problems due to threading problems in OpenThreads::ReadWriteMutex.
35// #define OSGPARTICLE_USE_ReadWriteMutex
36
37#ifdef OSGPARTICLE_USE_ReadWriteMutex
38 #include <OpenThreads/ReadWriteMutex>
39#else
40 #include <OpenThreads/Mutex>
41 #include <OpenThreads/ScopedLock>
42#endif
43
44
45namespace osgParticle
46{
47
48 /** The heart of this class library; its purpose is to hold a set of particles and manage particle creation, update, rendering and destruction.
49 * You can add this drawable to any Geode as you usually do with other
50 * Drawable classes. Each instance of ParticleSystem is a separate set of
51 * particles; it provides the interface for creating particles and iterating
52 * through them (see the Emitter and Program classes).
53 */
54 class OSGPARTICLE_EXPORT ParticleSystem: public osg::Drawable {
55 public:
56
57 enum Alignment {
58 BILLBOARD,
59 FIXED
60 };
61
62 ParticleSystem();
63 ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
64
65 META_Object(osgParticle, ParticleSystem);
66
67 /// Get the alignment type of particles.
68 inline Alignment getParticleAlignment() const;
69
70 /// Set the alignment type of particles.
71 inline void setParticleAlignment(Alignment a);
72
73 /// Get the X-axis alignment vector.
74 inline const osg::Vec3& getAlignVectorX() const;
75
76 /// Set the X-axis alignment vector.
77 inline void setAlignVectorX(const osg::Vec3& v);
78
79 /// Get the Y-axis alignment vector.
80 inline const osg::Vec3& getAlignVectorY() const;
81
82 /// Set the Y-axis alignment vector.
83 inline void setAlignVectorY(const osg::Vec3& v);
84
85 /// Set the alignment vectors.
86 inline void setAlignVectors(const osg::Vec3& X, const osg::Vec3& Y);
87
88
89
90 enum ParticleScaleReferenceFrame
91 {
92 LOCAL_COORDINATES,
93 WORLD_COORDINATES
94 };
95
96 /** Set whether the particles should be scaled relative to world coordaintes or local coordinates.*/
97 void setParticleScaleReferenceFrame(ParticleScaleReferenceFrame rf) { _particleScaleReferenceFrame = rf; }
98
99 /** Get whether the particles should be scaled relative to world coordaintes or local coordinates.*/
100 ParticleScaleReferenceFrame getParticleScaleReferenceFrame() const { return _particleScaleReferenceFrame; }
101
102
103
104 /// Get the default bounding box
105 inline const osg::BoundingBox& getDefaultBoundingBox() const;
106
107 /** Set the default bounding box.
108 The default bounding box is used when a real bounding box cannot be computed, for example
109 because no particles has been updated yet.
110 */
111 inline void setDefaultBoundingBox(const osg::BoundingBox& bbox);
112
113 /// Return true if we use vertex arrays for rendering particles.
114 bool getUseVertexArray() const { return _useVertexArray; }
115
116 /** Set to use vertex arrays for rendering particles.
117 Lots of variables will be omitted: particles' shape, alive or not, visibility distance, and so on,
118 so the rendering result is not as good as we wish (although it's fast than using glBegin/glEnd).
119 We had better use this for GLSL shaders, in which particle parameters will be kept as uniforms.
120 This method is called automatically by <CODE>setDefaultAttributesUsingShaders()</CODE>.
121 */
122 void setUseVertexArray(bool v) { _useVertexArray = v; }
123
124 /// Return true if shaders are required.
125 bool getUseShaders() const { return _useShaders; }
126
127 /** Set to use GLSL shaders for rendering particles.
128 Particles' parameters will be used as shader attribute arrays, and necessary variables, including
129 the visibility distance, texture, etc, will be used and updated as uniforms.
130 */
131 void setUseShaders(bool v) { _useShaders = v; _dirty_uniforms = true; }
132
133 /// Get the double pass rendering flag.
134 inline bool getDoublePassRendering() const;
135
136 /** Set the double pass rendering flag.
137 Double pass rendering avoids overdraw problems between particle systems
138 and other opaque objects. If you can render all the particle systems after
139 the opaque objects, then double pass is not necessary and can be turned off (best choice).
140 If you set the default attributes with setDefaultAttributes, then the particle
141 system will fall into a transparent bin.
142 */
143 inline void setDoublePassRendering(bool v);
144
145 /// Return true if the particle system is frozen.
146 bool getFrozen() const { return _frozen; }
147 inline bool isFrozen() const;
148
149 /** Set or reset the <I>frozen</I> state.
150 When the particle system is frozen, emitters and programs won't do anything on it.
151 */
152 inline void setFrozen(bool v);
153
154 /// Get the number of allocated particles (alive + dead).
155 inline int numParticles() const;
156
157 /// Get the number of dead particles.
158 inline int numDeadParticles() const;
159
160 /// Get whether all particles are dead
161 inline bool areAllParticlesDead() const { return numDeadParticles()==numParticles(); }
162
163 /// Get a pointer to the i-th particle.
164 inline Particle* getParticle(int i);
165
166 /// Get a const pointer to the i-th particle.
167 inline const Particle* getParticle(int i) const;
168
169 /// Create a new particle from the specified template (or the default one if <CODE>ptemplate</CODE> is null).
170 virtual Particle* createParticle(const Particle* ptemplate);
171
172 /// Destroy the i-th particle.
173 inline virtual void destroyParticle(int i);
174
175 /// Reuse the i-th particle.
176 inline virtual void reuseParticle(int i) { _deadparts.push(&(_particles[i])); }
177
178 /// Get the last frame number.
179 inline unsigned int getLastFrameNumber() const;
180
181 /// Get the unique delta time for emitters and updaters to use
182 inline double& getDeltaTime( double currentTime );
183
184 /// Get a reference to the default particle template.
185 inline Particle& getDefaultParticleTemplate();
186
187 /// Get a const reference to the default particle template.
188 inline const Particle& getDefaultParticleTemplate() const;
189
190 /// Set the default particle template (particle is copied).
191 inline void setDefaultParticleTemplate(const Particle& p);
192
193 /// Get whether the particle system can freeze when culled
194 inline bool getFreezeOnCull() const;
195
196 /// Set whether the particle system can freeze when culled (default is true)
197 inline void setFreezeOnCull(bool v);
198
199 /** A useful method to set the most common <CODE>StateAttribute</CODE>'s in one call.
200 If <CODE>texturefile</CODE> is empty, then texturing is turned off.
201 */
202 void setDefaultAttributes(const std::string& texturefile = "", bool emissive_particles = true, bool lighting = false, int texture_unit = 0);
203
204 /** A useful method to set the most common <CODE>StateAttribute</CODE> and use GLSL shaders to draw particles.
205 At present, when enabling shaders in the particle system, user-defined shapes will not be usable.
206 If <CODE>texturefile</CODE> is empty, then texturing is turned off.
207 */
208 void setDefaultAttributesUsingShaders(const std::string& texturefile = "", bool emissive_particles = true, int texture_unit = 0);
209
210 /// (<B>EXPERIMENTAL</B>) Get the level of detail.
211 inline int getLevelOfDetail() const;
212
213 /** (<B>EXPERIMENTAL</B>) Set the level of detail. The total number of particles is divided by the detail value to
214 get the actual number of particles to be drawn. This value must be greater than zero.
215 */
216 inline void setLevelOfDetail(int v);
217
218 enum SortMode
219 {
220 NO_SORT,
221 SORT_FRONT_TO_BACK,
222 SORT_BACK_TO_FRONT
223 };
224
225 /// Get the sort mode.
226 inline SortMode getSortMode() const;
227
228 /** Set the sort mode. It will force resorting the particle list by the Z direction of the view coordinates.
229 This can be used for the purpose of transparent rendering or <CODE>setVisibilityDistance()</CODE>.
230 */
231 inline void setSortMode(SortMode mode);
232
233 /// Get the visibility distance.
234 inline double getVisibilityDistance() const;
235
236 /** Set the visibility distance which allows the particles to be rendered only when depth is inside the distance.
237 When using shaders, it can work well directly; otherwise the sort mode should also be set to pre-compute depth.
238 */
239 inline void setVisibilityDistance(double distance);
240
241 /// Update the particles. Don't call this directly, use a <CODE>ParticleSystemUpdater</CODE> instead.
242 virtual void update(double dt, osg::NodeVisitor& nv);
243
244 virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
245
246 virtual osg::BoundingBox computeBoundingBox() const;
247
248#ifdef OSGPARTICLE_USE_ReadWriteMutex
249 typedef OpenThreads::ReadWriteMutex ReadWriterMutex;
250 typedef OpenThreads::ScopedReadLock ScopedReadLock;
251 typedef OpenThreads::ScopedWriteLock ScopedWriteLock;
252#else
253 typedef OpenThreads::Mutex ReadWriterMutex;
254 typedef OpenThreads::ScopedLock<OpenThreads::Mutex> ScopedReadLock;
255 typedef OpenThreads::ScopedLock<OpenThreads::Mutex> ScopedWriteLock;
256#endif
257
258 ReadWriterMutex* getReadWriteMutex() const { return &_readWriteMutex; }
259
260 /** Resize any per context GLObject buffers to specified size. */
261 virtual void resizeGLObjectBuffers(unsigned int maxSize);
262
263 /** If State is non-zero, this function releases OpenGL objects for
264 * the specified graphics context. Otherwise, releases OpenGL objects
265 * for all graphics contexts. */
266 virtual void releaseGLObjects(osg::State* state=0) const;
267
268 virtual osg::VertexArrayState* createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const;
269
270 void adjustEstimatedMaxNumOfParticles(int delta) { _estimatedMaxNumOfParticles += delta; }
271
272 void setEstimatedMaxNumOfParticles(int num) { _estimatedMaxNumOfParticles = num; }
273 int getEstimatedMaxNumOfParticles() const { return _estimatedMaxNumOfParticles; }
274
275 protected:
276
277 virtual ~ParticleSystem();
278
279 ParticleSystem& operator=(const ParticleSystem&) { return *this; }
280
281 inline void update_bounds(const osg::Vec3& p, float r);
282
283 typedef std::vector<Particle> Particle_vector;
284 typedef std::stack<Particle*> Death_stack;
285
286 Particle_vector _particles;
287 Death_stack _deadparts;
288
289 osg::BoundingBox _def_bbox;
290
291 Alignment _alignment;
292 osg::Vec3 _align_X_axis;
293 osg::Vec3 _align_Y_axis;
294 ParticleScaleReferenceFrame _particleScaleReferenceFrame;
295
296 bool _useVertexArray;
297 bool _useShaders;
298 bool _dirty_uniforms;
299
300 bool _doublepass;
301 bool _frozen;
302
303 osg::Vec3 _bmin;
304 osg::Vec3 _bmax;
305
306 bool _reset_bounds_flag;
307 bool _bounds_computed;
308
309 Particle _def_ptemp;
310 mutable unsigned int _last_frame;
311 mutable bool _dirty_dt;
312 bool _freeze_on_cull;
313
314 double _t0;
315 double _dt;
316
317 int _detail;
318 SortMode _sortMode;
319 double _visibilityDistance;
320
321 mutable ReadWriterMutex _readWriteMutex;
322
323 int _estimatedMaxNumOfParticles;
324
325 struct OSGPARTICLE_EXPORT ArrayData
326 {
327 ArrayData();
328
329 void init();
330 void init3();
331
332 void reserve(unsigned int numVertices);
333 void resize(unsigned int numVertices);
334 void resizeGLObjectBuffers(unsigned int maxSize);
335 void releaseGLObjects(osg::State* state);
336
337 void clear();
338 void dirty();
339
340 void dispatchArrays(osg::State& state);
341 void dispatchPrimitives();
342
343 osg::ref_ptr<osg::BufferObject> vertexBufferObject;
344 osg::ref_ptr<osg::Vec3Array> vertices;
345 osg::ref_ptr<osg::Vec3Array> normals;
346 osg::ref_ptr<osg::Vec4Array> colors;
347 osg::ref_ptr<osg::Vec2Array> texcoords2;
348 osg::ref_ptr<osg::Vec3Array> texcoords3;
349
350 typedef std::pair<GLenum, unsigned int> ModeCount;
351 typedef std::vector<ModeCount> Primitives;
352 Primitives primitives;
353 };
354
355 typedef osg::buffered_object< ArrayData > BufferedArrayData;
356 mutable BufferedArrayData _bufferedArrayData;
357 };
358
359 // INLINE FUNCTIONS
360
361 inline ParticleSystem::Alignment ParticleSystem::getParticleAlignment() const
362 {
363 return _alignment;
364 }
365
366 inline void ParticleSystem::setParticleAlignment(Alignment a)
367 {
368 _alignment = a;
369 }
370
371 inline const osg::Vec3& ParticleSystem::getAlignVectorX() const
372 {
373 return _align_X_axis;
374 }
375
376 inline void ParticleSystem::setAlignVectorX(const osg::Vec3& v)
377 {
378 _align_X_axis = v;
379 }
380
381 inline const osg::Vec3& ParticleSystem::getAlignVectorY() const
382 {
383 return _align_Y_axis;
384 }
385
386 inline void ParticleSystem::setAlignVectorY(const osg::Vec3& v)
387 {
388 _align_Y_axis = v;
389 }
390
391 inline void ParticleSystem::setAlignVectors(const osg::Vec3& X, const osg::Vec3& Y)
392 {
393 _align_X_axis = X;
394 _align_Y_axis = Y;
395 }
396
397 inline bool ParticleSystem::isFrozen() const
398 {
399 return _frozen;
400 }
401
402 inline void ParticleSystem::setFrozen(bool v)
403 {
404 _frozen = v;
405 }
406
407 inline const osg::BoundingBox& ParticleSystem::getDefaultBoundingBox() const
408 {
409 return _def_bbox;
410 }
411
412 inline void ParticleSystem::setDefaultBoundingBox(const osg::BoundingBox& bbox)
413 {
414 _def_bbox = bbox;
415 }
416
417 inline bool ParticleSystem::getDoublePassRendering() const
418 {
419 return _doublepass;
420 }
421
422 inline void ParticleSystem::setDoublePassRendering(bool v)
423 {
424 _doublepass = v;
425 }
426
427 inline int ParticleSystem::numParticles() const
428 {
429 return static_cast<int>(_particles.size());
430 }
431
432 inline int ParticleSystem::numDeadParticles() const
433 {
434 return static_cast<int>(_deadparts.size());
435 }
436
437 inline Particle* ParticleSystem::getParticle(int i)
438 {
439 return &_particles[i];
440 }
441
442 inline const Particle* ParticleSystem::getParticle(int i) const
443 {
444 return &_particles[i];
445 }
446
447 inline void ParticleSystem::destroyParticle(int i)
448 {
449 _particles[i].kill();
450 }
451
452 inline unsigned int ParticleSystem::getLastFrameNumber() const
453 {
454 return _last_frame;
455 }
456
457 inline double& ParticleSystem::getDeltaTime( double currentTime )
458 {
459 if ( _dirty_dt )
460 {
461 _dt = currentTime - _t0;
462 if ( _dt<0.0 ) _dt = 0.0;
463
464 _t0 = currentTime;
465 _dirty_dt = false;
466 }
467 return _dt;
468 }
469
470 inline void ParticleSystem::update_bounds(const osg::Vec3& p, float r)
471 {
472 if (_reset_bounds_flag) {
473 _reset_bounds_flag = false;
474 _bmin = p - osg::Vec3(r,r,r);
475 _bmax = p + osg::Vec3(r,r,r);
476 } else {
477 if (p.x() - r < _bmin.x()) _bmin.x() = p.x() - r;
478 if (p.y() - r < _bmin.y()) _bmin.y() = p.y() - r;
479 if (p.z() - r < _bmin.z()) _bmin.z() = p.z() - r;
480 if (p.x() + r > _bmax.x()) _bmax.x() = p.x() + r;
481 if (p.y() + r > _bmax.y()) _bmax.y() = p.y() + r;
482 if (p.z() + r > _bmax.z()) _bmax.z() = p.z() + r;
483 }
484 if (!_bounds_computed)
485 _bounds_computed = true;
486 }
487
488 inline Particle& ParticleSystem::getDefaultParticleTemplate()
489 {
490 return _def_ptemp;
491 }
492
493 inline const Particle& ParticleSystem::getDefaultParticleTemplate() const
494 {
495 return _def_ptemp;
496 }
497
498 inline void ParticleSystem::setDefaultParticleTemplate(const Particle& p)
499 {
500 _def_ptemp = p;
501 }
502
503 inline bool ParticleSystem::getFreezeOnCull() const
504 {
505 return _freeze_on_cull;
506 }
507
508 inline void ParticleSystem::setFreezeOnCull(bool v)
509 {
510 _freeze_on_cull = v;
511 }
512
513 inline int ParticleSystem::getLevelOfDetail() const
514 {
515 return _detail;
516 }
517
518 inline void ParticleSystem::setLevelOfDetail(int v)
519 {
520 if (v < 1) v = 1;
521 _detail = v;
522 }
523
524 inline ParticleSystem::SortMode ParticleSystem::getSortMode() const
525 {
526 return _sortMode;
527 }
528
529 inline void ParticleSystem::setSortMode(SortMode mode)
530 {
531 _sortMode = mode;
532 }
533
534 inline double ParticleSystem::getVisibilityDistance() const
535 {
536 return _visibilityDistance;
537 }
538
539 inline void ParticleSystem::setVisibilityDistance(double distance)
540 {
541 _visibilityDistance = distance;
542 if (_useShaders) _dirty_uniforms = true;
543 }
544
545}
546
547#endif