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