1 /*****************************************************************************
2  * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
3  * @file
4  * @author  Jim E. Brooks  http://www.palomino3d.org
5  * @brief   Medium particles and particle-system.
6  * @remarks This implementation was kept simple.  For now, decided to omit
7  *          implementing a particle updating mechanism and per-particle attributes.
8  *//*
9  * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
10  *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
11  *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
12  *****************************************************************************/
13 
14 //##############################################################################
15 // TODO/FIXME/DEPRECATED:
16 // This particle-system should be eliminated.
17 // It doesn't produce good results for missile trails.
18 // Problems are that point-sprite width is limited by hw
19 // and point-sprite widths aren't scaled (projected) accurately.
20 //##############################################################################
21 
22 #define FX_PARTSYS_MEDIUM_CC 1
23 #include <vector>
24 #include "base/module.hh"
25 using namespace base;
26 #include "math/module.hh"
27 #include "math/funcs_vector.hh"
28 using namespace math;
29 #include "object/module.hh"
30 using namespace object;
31 #include "graph/module.hh"
32 #include "graph/scene_graph.hh"
33 using namespace graph;
34 #include "world/module.hh"
35 using namespace world;
36 #include "fx/module.hh"
37 #include "fx/partsys_medium.hh"
38 #include <osg/Point>
39 #include <osg/PointSprite>
40 
41 namespace fx {
42 
43 INTERN const uint MEDIUM_PARTICLES_LIMIT   = 5000;
44 INTERN const uint MEDIUM_PARTICLES_RESERVE = 5000;
45 
46 ////////////////////////////////////////////////////////////////////////////////
47 /////////////////////////  MediumParticleDrawable  //////////////////////////////
48 ////////////////////////////////////////////////////////////////////////////////
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 /// @brief This is an OSG Drawable object that directly renders point-sprites.
52 ///
53 /// The reason for directly-rendering is to that the size of every point-sprite
54 /// needs to be computed individually.  Issuing glPointSize() for every particle
55 /// is faster and less-cumbersome than composing nodes with various point sizes.
56 ///
57 /// When OSG has called drawImplementation(), OSG has set OpenGL state
58 /// for rendering OpenGL "point sprites".
59 ///
60 class MediumParticleDrawable : public osg::Drawable
61 {
62 private:
63     /*****************************************************************************
64      * ctor subroutine.
65      *****************************************************************************/
Init(void)66     void Init( void )
67     {
68         SET_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
69 
70         // Pre-allocate.
71         mVertexArray.reserve( MEDIUM_PARTICLES_RESERVE );
72 
73         // Disable display list else only the OpenGL calls
74         // of the first particle would be captured.
75         setUseDisplayList( false );
76     }
77 
78 public:
META_Object(Palomino,MediumParticleDrawable)79     META_Object(Palomino,MediumParticleDrawable)  // OSG macro
80 
81     /*****************************************************************************
82      * ctor/dtor.
83      * @param   color
84      *          Applied to all particles.
85      * @param   range
86      *          Distance in world space where particle is scaled-down to mediumest size.
87      * @param   minWidth, maxWidth
88      *          Limits of point size scaled by distance of their vertex from viewpoint.
89      *          This simulates 3D:2D projection.
90      *****************************************************************************/
91     MediumParticleDrawable( const RGBA color, const fp range,  const fp minWidth, const fp maxWidth )
92     :   osg::Drawable(),
93         mColor(color),
94         mRange(range),
95         mRangeInv(1.0f / range),
96         mMinWidth(minWidth),
97         mMaxWidth(maxWidth),
98         mVertexArray(),
99         mBoundingBox()
100     {
101         Init();
102     }
103 
104     // (for OSG)
MediumParticleDrawable(void)105     MediumParticleDrawable( void )
106     :   osg::Drawable(),
107         mColor(),
108         mRange(1000.0f),
109         mRangeInv(1000.0f),
110         mMinWidth(1.0f),
111         mMaxWidth(64.0f),
112         mVertexArray(),
113         mBoundingBox()
114     {
115     }
116 
117     // (unexpected)
MediumParticleDrawable(const MediumParticleDrawable & other,const osg::CopyOp & copyop=osg::CopyOp::SHALLOW_COPY)118     MediumParticleDrawable( const MediumParticleDrawable& other, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY)
119     :   osg::Drawable(),
120         mColor(other.mColor),
121         mRange(other.mRange),
122         mRangeInv(other.mRangeInv),
123         mMinWidth(other.mMinWidth),
124         mMaxWidth(other.mMaxWidth),
125         mVertexArray(other.mVertexArray),  // TOO SLOW
126         mBoundingBox(other.mBoundingBox)
127     {
128     ASSERT(false);  // copying vertex array would be too SLOW, catch if copy ctor is ever called
129         Init();
130     }
131 
~MediumParticleDrawable()132     virtual ~MediumParticleDrawable()
133     {
134         INVALIDATE_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
135     }
136 
137     /*****************************************************************************
138      * Draw array of medium particles.
139      *****************************************************************************/
drawImplementation(osg::RenderInfo & renderInfo) const140     virtual void drawImplementation( osg::RenderInfo& renderInfo ) const
141     {
142     CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
143 
144         const uint vcount = mVertexArray.size();
145         if ( UX( vcount == 0 ) )
146             return;
147 
148         // The inverse of view matrix has the correct eye position.
149         const osg::Vec3  eye  = renderInfo.getCurrentCamera()->getInverseViewMatrix().getTrans();
150         const osg::Vec3* v    = &mVertexArray[0];     // first vertex
151       //const osg::Vec3* vEnd = &mVertexArray[vcount];  // wrong: operator[] will throw exception!
152         const osg::Vec3* vEnd = v + vcount;             // end vertex (end as in STL)
153 
154         // Save current color.
155         GLfloat savedColor[4];
156         glGetFloatv( GL_CURRENT_COLOR, savedColor );
157 
158         // Set color.
159         GLfloat color[4];
160         mColor.Get4f<GLfloat>( color );  // RGBA object --> color[4]
161         glColor4fv( color );
162 
163         // For each vertex.
164         for ( ; v < vEnd; ++v )
165         {
166         ASSERT( not mVertexArray.empty() );
167 
168             const fp dist      = Min<fp>( mRange, Distance(eye,*v) );
169             const fp ratio     = (mRange - dist) * mRangeInv;
170             const fp pointSize = Max<fp>( mMinWidth, std::pow(ratio,4.0f) * mMaxWidth );
171             glPointSize( pointSize );
172             glBegin( GL_POINTS );
173                 glVertex3fv( reinterpret_cast<const GLfloat*>( v ) );
174             glEnd();
175         }
176 
177         // Restore color.
178         glColor4fv( savedColor );
179     }
180 
181     /*****************************************************************************
182      * Compute bound of medium particles.
183      * Which is the fastest?
184      * Bounding all point-sprites in one box or dividing them into multiple boxes (?).
185      *****************************************************************************/
computeBoundingBox() const186     osg::BoundingBox computeBoundingBox() const
187     {
188     CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
189 
190         return mBoundingBox;
191     }
192 
193     /*****************************************************************************
194      * Add particle.
195      *****************************************************************************/
196     void
AttachParticle(const osg::Vec3 & v)197     AttachParticle( const osg::Vec3& v )
198     {
199     CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
200 
201         // Too many particles will slow FPS.
202         if ( EX( mVertexArray.size() < MEDIUM_PARTICLES_LIMIT ) )
203         {
204             // Append vertex.
205             mVertexArray.push_back( v );
206 
207             // Expand bounding box.
208             mBoundingBox.expandBy( v );
209 
210             // Force recomputing bounding box.
211             dirtyBound();
212         }
213     }
214 
215     /*****************************************************************************
216      * @return Vertex count (amount of particles).
217      *****************************************************************************/
218     uint
GetVertexCount(void) const219     GetVertexCount( void ) const
220     {
221     CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
222 
223         return mVertexArray.size();
224     }
225 
226     /*****************************************************************************
227      * Set color of all particles.
228      *****************************************************************************/
229     void
SetColorAll(const RGBA color)230     SetColorAll( const RGBA color )
231     {
232     CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
233 
234         mColor = color;
235     }
236 
237 private:
238     RGBA                   mColor;         ///< color of all point sprites
239     const fp               mRange;         ///< width of point-sprite is minimized at limit of range
240     const fp               mRangeInv;
241     const fp               mMinWidth;      ///< minimum size of point-sprites
242     const fp               mMaxWidth;      ///< maximum size of point-sprites
243     std::vector<osg::Vec3> mVertexArray;   ///< not osg::Vec3Array which lacks swap()
244     osg::BoundingBox       mBoundingBox;   ///< no need for ref_ptr (isn't derived from osg::Referenced)
245     DECLARE_TYPESIG(TYPESIG_MEDIUM_PARTICLE_DRAWABLE);
246 };
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 ///////////////////////  MediumParticleSystem::Args  ////////////////////////////
250 ////////////////////////////////////////////////////////////////////////////////
251 
252 /*****************************************************************************
253  * Args for MediumParticleSystem ctor.
254  * @param   nodeSort
255  *          For state-sorting scene-graph.
256  *          Texture can be specified in stateset of NodeSort.
257  * @param   color
258  *          Of all particles.
259  * @param   geoRange
260  *          Range in 3D space when 2D point sprites are scaled to min,max sizes.
261  *          This simulates a 3D appearance.
262  * @param   minWidth, maxWidth
263  *          min,max of width of point sprites (limited by hardware, 64 pixels max is typical).
264  * Example:
265  * new MediumParticleSystem( MediumParticleSystem::Args( Texture::NewTextureAsStateSet( "smoke.png" ) ) );
266  *****************************************************************************/
Args(const Milliseconds lifetime,const bool timerEnabled,const NodeSort & nodeSort,const RGBA color,const Meter geoRange,const fp minWidth,const fp maxWidth)267 MediumParticleSystem::Args::Args( const Milliseconds lifetime,
268                                   const bool timerEnabled,
269                                   const NodeSort& nodeSort,
270                                   const RGBA color,
271                                   const Meter geoRange,
272                                   const fp minWidth,
273                                   const fp maxWidth )
274 :   ParticleSystem::Args(lifetime,timerEnabled),
275     mNodeSort(nodeSort),
276     mColor(color),
277     mRange(world::conv::Meters2Sim(geoRange)),
278     mMinWidth(minWidth),
279     mMaxWidth(maxWidth)
280 {
281 CHECK_TYPESIG(&nodeSort,TYPESIG_NODE_SORT);
282     // NOP
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
286 ///////////////////////////  MediumParticleSystem  //////////////////////////////
287 ////////////////////////////////////////////////////////////////////////////////
288 
289 /*****************************************************************************
290  * ctor/dtor.
291  *****************************************************************************/
MediumParticleSystem(const Args & args)292 MediumParticleSystem::MediumParticleSystem( const Args& args )
293 :   ParticleSystem(args),
294     // OSG components:
295     mAttachedToSceneGraph(false),
296     mNodeSort(args.mNodeSort),
297     mStateSet(args.mNodeSort.GetStateSet()),
298     mGeode(new osg::Geode),
299     mGeom(new osg::Geometry),
300     mMediumParticleDrawable(new MediumParticleDrawable(args.mColor,args.mRange,args.mMinWidth,args.mMaxWidth))
301 {
302 CHECK_TYPESIG(&args.mNodeSort,TYPESIG_NODE_SORT);
303 
304     SET_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
305 
306     // Compose OSG components:
307     // MediumParticleDrawable is the reason why some node components are omitted.
308     mGeode->setStateSet( mStateSet.get() );
309     mGeode->addDrawable( mMediumParticleDrawable.get() );
310 
311     // Configure point sprite:
312     mStateSet->setAttribute( new osg::Point );
313     mStateSet->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON );
314 
315     // (The caller should have passed a stateset with a texture attached to it)
316 }
317 
~MediumParticleSystem()318 MediumParticleSystem::~MediumParticleSystem()
319 {
320     // Detach from scene-graph.
321     if ( mAttachedToSceneGraph )
322     {
323         GET_SCENE_GRAPH().DetachNode( mGeode.get() );
324     }
325 
326     INVALIDATE_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
327 }
328 
329 /*****************************************************************************
330  * Append MediumParticle.
331  * Time-of-death is absolute (add current time to lifetime).
332  *****************************************************************************/
333 void
AttachParticle(const MediumParticle & mediumParticle)334 MediumParticleSystem::AttachParticle( const MediumParticle& mediumParticle )
335 {
336 CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
337 CHECK_TYPESIG(&mNodeSort,TYPESIG_NODE_SORT);
338 
339     // Lazy-attach to scene-graph.
340     if ( UX( not mAttachedToSceneGraph ) )
341     {
342         mAttachedToSceneGraph = true;
343         GET_SCENE_GRAPH().AttachNode( mGeode.get(), mNodeSort );
344     }
345 
346     // Add vertex to Drawable.
347     mMediumParticleDrawable->AttachParticle( mediumParticle.mPosition );
348 }
349 
350 /*****************************************************************************
351  * @return Amount of particles.
352  *****************************************************************************/
353 uint
GetParticleCount(void)354 MediumParticleSystem::GetParticleCount( void )
355 {
356 CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
357 
358     return mMediumParticleDrawable->GetVertexCount();
359 }
360 
361 /*****************************************************************************
362  * Set color of all particles.
363  *****************************************************************************/
364 void
SetColorAll(const RGBA color)365 MediumParticleSystem::SetColorAll( const RGBA color )
366 {
367 CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
368 
369     return mMediumParticleDrawable->SetColorAll( color );
370 }
371 
372 } // namespace fx
373