1 /////////////////////////////////////////////////////////////////////////////// 2 // Copyright (C) 2004-2011 by The Allacrost Project 3 // Copyright (C) 2012-2016 by Bertram (Valyria Tear) 4 // All Rights Reserved 5 // 6 // This code is licensed under the GNU GPL version 2. It is free software 7 // and you may modify it and/or redistribute it under the terms of this license. 8 // See http://www.gnu.org/copyleft/gpl.html for details. 9 /////////////////////////////////////////////////////////////////////////////// 10 11 /** *************************************************************************** 12 *** \file particle_system.h 13 *** \author Raj Sharma, roos@allacrost.org 14 *** \author Yohann Ferreira, yohann ferreira orange fr 15 *** \brief Header file for particle system 16 *** 17 *** This file contains two classes: ParticleSystemDef, and ParticleSystem. 18 *** 19 *** ParticleSystemDef is a "definition" class, meaning that it holds information 20 *** about a particle system, like its lifetime, the emitter, and other properties. 21 *** 22 *** ParticleSystem is an "instance" class, meaning that it holds information about 23 *** a particle system which is currently being drawn on screen. 24 *** 25 *** This way, if you have 100 explosions for example, the properties of the 26 *** effect are stored only once, and the only thing that gets repeated 100 times 27 *** is instance-specific stuff like positions of vertices, etc. 28 *** **************************************************************************/ 29 30 #ifndef __PARTICLE_SYSTEM_HEADER__ 31 #define __PARTICLE_SYSTEM_HEADER__ 32 33 #include "particle.h" 34 #include "particle_emitter.h" 35 36 #include "engine/video/image.h" 37 38 namespace vt_mode_manager 39 { 40 41 //! \brief Specifies the stencil operation to use and describes how the stencil buffer is modified 42 enum VIDEO_STENCIL_OP { 43 VIDEO_STENCIL_OP_INVALID = -1, 44 45 //! Set the stencil value to one 46 VIDEO_STENCIL_OP_ZERO = 0, 47 48 //! Set the stencil value to zero 49 VIDEO_STENCIL_OP_ONE = 1, 50 51 //! Increase the stencil value 52 VIDEO_STENCIL_OP_INCREASE = 2, 53 54 //! Decrease the stencil value 55 VIDEO_STENCIL_OP_DECREASE = 3, 56 57 VIDEO_STENCIL_OP_TOTAL = 4 58 }; 59 60 /*!*************************************************************************** 61 * \brief when we change a property of an effect, it affects all of the 62 * systems contained within that effect. So, this structure contains 63 * any relevant parameters that particle systems need to know about. 64 *****************************************************************************/ 65 66 class EffectParameters 67 { 68 public: EffectParameters()69 EffectParameters(): 70 orientation(0.0f), 71 attractor(0.0f, 0.0f) 72 {} 73 74 //! orientation of the effect, called with ParticleEffect::SetOrientation() 75 float orientation; 76 77 //! attraction point, particles gravitate towards this 78 vt_common::Position2D attractor; 79 }; 80 81 82 class ParticleSystemDef 83 { 84 public: ParticleSystemDef()85 ParticleSystemDef(): 86 enabled(false), 87 blend_mode(0), 88 system_lifetime(0.0f), 89 particle_lifetime(0.0f), 90 particle_lifetime_variation(0.0f), 91 max_particles(0), 92 damping(0.0f), 93 damping_variation(0.0f), 94 acceleration(0.0f, 0.0f), 95 acceleration_variation(0.0f, 0.0f), 96 wind_velocity(0.0f, 0.0f), 97 wind_velocity_variation(0.0f, 0.0f), 98 wave_motion_used(false), 99 wave_length(0.0f), 100 wave_length_variation(0.0f), 101 wave_amplitude(0.0f), 102 wave_amplitude_variation(0.0f), 103 tangential_acceleration(0.0f), 104 tangential_acceleration_variation(0.0f), 105 radial_acceleration(0.0f), 106 radial_acceleration_variation(0.0f), 107 user_defined_attractor(false), 108 attractor_falloff(0.0f), 109 rotation_used(false), 110 rotate_to_velocity(false), 111 speed_scale_used(false), 112 speed_scale(0.0f), 113 min_speed_scale(0.0f), 114 max_speed_scale(0.0f), 115 smooth_animation(false), 116 modify_stencil(false), 117 stencil_op(VIDEO_STENCIL_OP_INVALID), 118 use_stencil(false), 119 random_initial_angle(false) 120 {} 121 ~ParticleSystemDef()122 ~ParticleSystemDef() 123 {} 124 125 //! Is this system supposed to be displayed 126 bool enabled; 127 128 //! Each system contains 1 emitter, which mainly determines where to shoot particles out from 129 //! and how fast to shoot them out 130 ParticleEmitter emitter; 131 132 //! Array of keyframes, which specify how particle properties vary over time. This array must 133 //! contain at least 1 keyframe (in that case, the properties are all held constant) 134 std::vector<ParticleKeyframe> keyframes; 135 136 //! How to blend the particles: VIDEO_NO_BLEND, VIDEO_BLEND, or VIDEO_BLEND_ADD 137 //! For most effects, we want VIDEO_BLEND_ADD 138 int32_t blend_mode; 139 140 //! How many seconds the system should live for before it dies out. This is only 141 //! meaningful if our emitter mode is EMITTER_MODE_ONE_SHOT. The other modes 142 //! will simply keep playing until the effect is destroyed or stopped, except for 143 //! EMITTER_MODE_BURST, which spits out a bunch of particles at the beginning and 144 //! then dies as soon as all of those particles die 145 float system_lifetime; 146 147 //! How long each particle should live for before it dies 148 float particle_lifetime; 149 150 //! Random variation added to particle lifetime 151 float particle_lifetime_variation; 152 153 //! Maximum number of particles this system can have at one time 154 int32_t max_particles; 155 156 //! A number below 1.0 (but generally pretty close to 1.0). A damp of .99 means that 157 //! each second, particle velocity drops by 1% 158 float damping; 159 160 //! Random variation added to damping 161 float damping_variation; 162 163 //! constant acceleration, good example is gravity. Note that down is in the positive y 164 //! direction since we are using screen coordinates 165 vt_common::Position2D acceleration; 166 167 vt_common::Position2D acceleration_variation; 168 169 //! wind velocity, more generally any velocity which is added to each particle's velocity 170 vt_common::Position2D wind_velocity; 171 172 //! wind velocity variation 173 vt_common::Position2D wind_velocity_variation; 174 175 //! true if we should use wave motion for this system 176 bool wave_motion_used; 177 178 //! wavelength. For example a wavelength of 5 means that it takes 5 seconds to go from 179 //! one point on the sinusoidal curve to the next 180 float wave_length; 181 182 //! Random variation added to wave length 183 float wave_length_variation; 184 185 //! wave amplitude- the distance from the peak to the bottom of the sinusoidal curve 186 float wave_amplitude; 187 188 //! Random variation added to wave amplitude 189 float wave_amplitude_variation; 190 191 //! tangential acceleration, how particle accelerates tangential to vector from particle 192 //! to center of emitter region. Positive is clockwise. 193 float tangential_acceleration; 194 195 //! tangential acceleration variation 196 float tangential_acceleration_variation; 197 198 //! radial acceleration. Positive means particles accelerate away from the emitter, negative 199 //! means they accelerate back towards it 200 float radial_acceleration; 201 202 //! radial acceleration variation 203 float radial_acceleration_variation; 204 205 //! if true, we use a user defined attractor instead of the emitter position for radial 206 //! acceleration. The user defined attractor is set using ParticleEffect::SetAttractorPoint() 207 bool user_defined_attractor; 208 209 //! how quickly the "pull" of an attractor falls off as a particle gets further away from it. 210 //! For example if falloff is 10^-3, and a particle is 500 pixels away from the attractor, 211 //! then the radial acceleration is lessened by (500 * 10^-3) = .5, or 50% 212 float attractor_falloff; 213 214 //! True if ANY of the keyframes for the particles contain non-zero rotations 215 //! This is used by the Draw() function so it knows whether it needs to factor in 216 //! rotations when determining the particle vertices (since this adds a lot of extra 217 //! computation) 218 bool rotation_used; 219 220 //! True if you want particles to rotate to face the same direction they are going. 221 //! So for example a particle going straight up will not be rotated, but a particle 222 //! that is going 45 degrees northeast will be rotated 45 degrees clockwise 223 bool rotate_to_velocity; 224 225 //! true if speed scaling is used. Note that speed scaling can only be used if 226 //! rotate to velocity is also used. 227 bool speed_scale_used; 228 229 //! this number is multiplied by a particle's speed to come up with a scale of how much 230 //! to stretch a particle in its direction of motion 231 float speed_scale; 232 233 //! if you use speed_scale, this can cause particles to become extremely tiny at low speeds 234 //! this variable allows you to set a minimum bound on the scaling due to speed 235 float min_speed_scale; 236 237 //! if you use speed_scale, this can cause particles to become extremely large at high speeds 238 //! this variable allows you to set a maximum bound on the scaling due to speed 239 float max_speed_scale; 240 241 //! True if alpha blending should be used to create smooth transitions between animation 242 //! frames 243 bool smooth_animation; 244 245 //! True if this system should not actually write to the screen, but instead modify the 246 //! stencil buffer. Every time a pixel passes the alpha test, the stencil buffer will be 247 //! modified, according to _stencil_op 248 bool modify_stencil; 249 250 //! if modify stencil is true, then the operation to use when the alpha test passes 251 //! can either be VIDEO_STENCIL_OP_INCREASE, VIDEO_STENCIL_OP_ONE, VIDEO_STENCIL_OP_ZERO, or 252 //! VIDEO_STENCIL_OP_DECREASE. The stencil test we use is "equal to 1" 253 VIDEO_STENCIL_OP stencil_op; 254 255 //! if this is true, then we only draw in areas where the stencil buffer contains a 1. 256 //! Note that _use_stencil and _modify_stencil cannot both be 1 at the same time 257 bool use_stencil; 258 259 //! true if particles' initial angle should be randomized. If false, then all particles 260 //! have an angle of zero when they spawn 261 bool random_initial_angle; 262 263 //! Array telling how long each animation should last for 264 std::vector<int32_t> animation_frame_times; 265 266 //! Array of filenames for each frame of animation 267 std::vector<std::string> animation_frame_filenames; 268 269 }; // class ParticleSystemDef 270 271 272 273 class ParticleSystem 274 { 275 public: 276 /*! 277 * \brief Constructor 278 */ ParticleSystem(ParticleSystemDef * sys_def)279 explicit ParticleSystem(ParticleSystemDef* sys_def) { 280 _Destroy(); 281 _Create(sys_def); 282 } 283 ~ParticleSystem()284 ~ParticleSystem() { 285 _Destroy(); 286 } 287 288 //! \brief draws the system 289 void Draw(); 290 291 /*! 292 * \brief updates the system 293 * \param frame_time the current frame time 294 * \param params the effect parameters to use for this update (orientation and attractor point) 295 */ 296 void Update(float frame_time, const EffectParameters ¶ms); 297 298 /*! 299 * \brief returns true if system is still alive 300 * \return true if alive, false if dead 301 */ IsAlive()302 bool IsAlive() const { 303 return _alive && _system_def->enabled; 304 } 305 306 /*! 307 * \brief returns true if system has been stopped by a call to Stop() 308 * \return true if stopped, false if still going 309 */ IsStopped()310 bool IsStopped() const { 311 return _stopped; 312 } 313 314 /*! 315 * \brief stops the system, i.e. makes it stop emitting new particles 316 */ Stop()317 void Stop() { 318 _stopped = true; 319 } 320 321 /*! 322 * \brief returns how many particles are alive in this system 323 * \return the number of particles in this system 324 */ GetNumParticles()325 int32_t GetNumParticles() const { 326 return _num_particles; 327 } 328 329 /*! 330 * \brief returns the number of seconds since this system was created 331 * \return the age of the system 332 */ GetAge()333 float GetAge() const { 334 return _age; 335 } 336 337 private: 338 /*! 339 * \brief initializes this particle system as an instance of the 340 * type of particle system specified by the ParticleSystemDef 341 * \param sys_def particle definition to base the system off of 342 * \return success/failure 343 */ 344 bool _Create(ParticleSystemDef *sys_def); 345 346 /*! 347 * \brief destroys the system 348 */ 349 void _Destroy(); 350 351 /*! 352 * \brief helper function to update properties of particles 353 * \param t the current frame time 354 * \param params the effect parameters to use for this update (orientation and attractor point) 355 */ 356 void _UpdateParticles(float t, const EffectParameters ¶ms); 357 358 /*! 359 * \brief helper function to kill off any particles that have died 360 * 361 * \param num_particles this is an optimization that lets us avoid 362 * killing particles. Killing particles is expensive, because 363 * it leaves holes in our array, so we have to shuffle data 364 * around to fill those holes. So instead, say we have 10 365 * particles to kill this frame, and we also want to emit 8 366 * particles. Then, instead of killing 10 particles, we only 367 * kill 2, and for the other 8, we respawn them immediately. 368 * \param params the effect parameters to use for this update (orientation and attractor point) 369 */ 370 void _KillParticles(int32_t &num_particles, const EffectParameters ¶ms); 371 372 /*! 373 * \brief helper function that emits whatever particles still need to be 374 * emitted after calling _KillParticles 375 * \param num_particles the number of particles that need to be emitted 376 * \param params the effect parameters to use for this update (orientation and attractor point) 377 */ 378 void _EmitParticles(int32_t num_particles, const EffectParameters ¶ms); 379 380 /*! 381 * \brief helper function to move a particle from element src to element dest 382 * in the array. This is required any time we kill a particle, because 383 * killing particles leaves a hole in the array 384 * \param src where to move the particle from 385 * \param dest where to move the particle to 386 */ 387 void _MoveParticle(int32_t src, int32_t dest); 388 389 /*! 390 * \brief creates a new particle at element i in the particle array 391 * \param i index of the particle to respawn 392 * \param params the effect parameters to use for this update (orientation and attractor point) 393 */ 394 void _RespawnParticle(int32_t i, const EffectParameters ¶ms); 395 396 //! The system definition, contains information like the emitter properties, lifetime of 397 //! particles, particle keyframes, etc. Basically everything which isn't instance-specific 398 //! Note that this pointer shouldn't be deleted by the particle system, since it's handled by 399 //! the corresponding ParticleEffectDef instance. 400 ParticleSystemDef *_system_def; 401 402 //! Animation for each particle. If it's non-animated, it just has 1 frame 403 vt_video::AnimatedImage _animation; 404 405 //! Number of active particles in this system. (The size of the vectors may be larger, since 406 //! we might set a particle quota for the system which is higher than what's actually there.) 407 int32_t _num_particles; 408 409 //! The array of particle vertices. Note that this array contains FOUR vertices per particle. 410 //! This is used for rendering the particles with OpenGL 411 std::vector<ParticleVertex> _particle_vertices; 412 std::vector<vt_video::Color> _particle_colors; 413 std::vector<ParticleTexCoord> _particle_texcoords; 414 415 //! This array holds everything except positions and colors. The reason we keep positions and 416 //! colors separate is so that they can be efficiently fed to OpenGL for rendering. 417 std::vector<Particle> _particles; 418 419 //! if stopped is true, no new particles should be emitted 420 bool _stopped; 421 422 //! alive gets set to false when the number of active particles drops to zero 423 bool _alive; 424 425 //! age of the system, since it was created 426 float _age; 427 428 //! last time the system was updated (based on the system's age) 429 float _last_update_time; 430 431 }; // class ParticleSystem 432 433 } // namespace vt_mode_manager 434 435 #endif 436