particle_system.cpp

Go to the documentation of this file.
00001 
00002 //            Copyright (C) 2004-2007 by The Allacrost Project
00003 //                         All Rights Reserved
00004 //
00005 // This code is licensed under the GNU GPL version 2. It is free software
00006 // and you may modify it and/or redistribute it under the terms of this license.
00007 // See http://www.gnu.org/copyleft/gpl.html for details.
00009 
00010 #include "particle_system.h"
00011 #include "particle_keyframe.h"
00012 #include "video.h"
00013 
00014 using namespace std;
00015 using namespace hoa_utils;
00016 
00017 namespace hoa_video
00018 {
00019 
00020 namespace private_video
00021 {
00022 
00023 
00024 //-----------------------------------------------------------------------------
00025 // ParticleSystem
00026 //-----------------------------------------------------------------------------
00027 
00028 ParticleSystem::ParticleSystem()
00029 {
00030   _system_def = NULL;
00031   _max_particles = 0;
00032   _num_particles = 0;
00033   _age = 0.0f;
00034   _last_update_time = 0.0f;
00035 
00036   _alive = true;
00037   _stopped = false;
00038 }
00039 
00040 
00041 //-----------------------------------------------------------------------------
00042 // Create: initializes the particle system from the definition
00043 //-----------------------------------------------------------------------------
00044 
00045 bool ParticleSystem::Create(const ParticleSystemDef *sys_def)
00046 {
00047   _system_def = sys_def;
00048   _max_particles = sys_def->max_particles;
00049   _num_particles = 0;
00050 
00051   _particles.resize(_max_particles);
00052   _particle_vertices.resize(_max_particles * 4);
00053   _particle_texcoords.resize(_max_particles * 4);
00054   _particle_colors.resize(_max_particles * 4);
00055 
00056   _alive = true;
00057   _stopped = false;
00058   _age = 0.0f;
00059 
00060   size_t num_frames = sys_def->animation_frame_filenames.size();
00061 
00062   for(size_t j = 0; j < num_frames; ++j)
00063   {
00064     int32 frame_time;
00065     if(j < sys_def->animation_frame_times.size())
00066       frame_time = sys_def->animation_frame_times[j];
00067     else if(sys_def->animation_frame_times.empty())
00068       frame_time = 0;
00069     else
00070       frame_time = sys_def->animation_frame_times.back();
00071 
00072     _animation.AddFrame(sys_def->animation_frame_filenames[j], frame_time);
00073   }
00074 
00075   VideoManager->LoadImage(_animation);
00076   return true;
00077 }
00078 
00079 
00080 
00081 //-----------------------------------------------------------------------------
00082 // Draw: draws the particle system
00083 //-----------------------------------------------------------------------------
00084 
00085 bool ParticleSystem::Draw()
00086 {
00087   if(!_system_def->enabled || _age < _system_def->emitter._start_time)
00088     return true;
00089 
00090   // set blending parameters
00091   if(_system_def->blend_mode == VIDEO_NO_BLEND)
00092   {
00093     glDisable(GL_BLEND);
00094   }
00095   else
00096   {
00097     glEnable(GL_BLEND);
00098 
00099     if(_system_def->blend_mode == VIDEO_BLEND)
00100       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00101     else
00102       glBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive
00103   }
00104 
00105 
00106   if(_system_def->use_stencil)
00107   {
00108     glEnable(GL_STENCIL_TEST);
00109     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
00110     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
00111     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
00112   }
00113   else if(_system_def->modify_stencil)
00114   {
00115     glEnable(GL_STENCIL_TEST);
00116 
00117     if(_system_def->stencil_op == VIDEO_STENCIL_OP_INCREASE)
00118       glStencilOp(GL_INCR, GL_KEEP, GL_KEEP);
00119     else if(_system_def->stencil_op == VIDEO_STENCIL_OP_DECREASE)
00120       glStencilOp(GL_DECR, GL_KEEP, GL_KEEP);
00121     else if(_system_def->stencil_op == VIDEO_STENCIL_OP_ZERO)
00122       glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
00123     else
00124       glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
00125 
00126     glStencilFunc(GL_NEVER, 1, 0xFFFFFFFF);
00127     glEnable(GL_ALPHA_TEST);
00128     glAlphaFunc(GL_GREATER, 0.00f);
00129     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
00130 
00131   }
00132   else
00133   {
00134     glDisable(GL_STENCIL_TEST);
00135     glDisable(GL_ALPHA_TEST);
00136     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
00137   }
00138 
00139   glEnable(GL_TEXTURE_2D);
00140 
00141   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00142   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00143 
00144   StillImage *id = _animation.GetFrame(_animation.GetCurrentFrameIndex());
00145   Image *img = id->_elements[0].image;
00146   VideoManager->_BindTexture(img->texture_sheet->tex_ID);
00147 
00148 
00149   float frame_progress = _animation.GetPercentProgress();
00150 
00151   float u1 = img->u1;
00152   float u2 = img->u2;
00153   float v1 = img->v1;
00154   float v2 = img->v2;
00155 
00156   float img_width  = static_cast<float>(img->width);
00157   float img_height = static_cast<float>(img->height);
00158 
00159   float img_width_half = img_width * 0.5f;
00160   float img_height_half = img_height * 0.5f;
00161 
00162   Color scene_light_modifier;
00163 
00164   bool use_scene_lighting = false;
00165 
00166   if(_system_def->scene_lighting != 0.0f)
00167   {
00168     scene_light_modifier = VideoManager->GetSceneLightingColor();
00169 
00170     if(scene_light_modifier[0] != 1.0f ||
00171        scene_light_modifier[1] != 1.0f ||
00172        scene_light_modifier[2] != 1.0f ||
00173        scene_light_modifier[3] != 1.0f )
00174     {
00175       use_scene_lighting = true;
00176 
00177       if(_system_def->scene_lighting != 1.0f)
00178         scene_light_modifier = Color::white * (1.0f - _system_def->scene_lighting) + scene_light_modifier * (_system_def->scene_lighting);
00179     }
00180   }
00181 
00182 
00183   // fill the vertex array
00184 
00185   if(_system_def->rotation_used)
00186   {
00187     int32 v = 0;
00188 
00189     for(int32 j = 0; j < _num_particles; ++j)
00190     {
00191       float scaled_width_half  = img_width_half * _particles[j].size_x;
00192       float scaled_height_half = img_height_half * _particles[j].size_y;
00193 
00194       float rotation_angle = _particles[j].rotation_angle;
00195 
00196       if(_system_def->rotate_to_velocity)
00197       {
00198         // calculate the angle based on the velocity
00199         rotation_angle += UTILS_HALF_PI + atan2f(_particles[j].combined_velocity_y, _particles[j].combined_velocity_x);
00200 
00201         // calculate the scaling due to speed
00202         if(_system_def->speed_scale_used)
00203         {
00204           // speed is magnitude of velocity
00205           float speed = sqrtf(_particles[j].combined_velocity_x * _particles[j].combined_velocity_x + _particles[j].combined_velocity_y * _particles[j].combined_velocity_y);
00206           float scale_factor = _system_def->speed_scale * speed;
00207 
00208           if(scale_factor < _system_def->min_speed_scale)
00209             scale_factor = _system_def->min_speed_scale;
00210           if(scale_factor > _system_def->max_speed_scale)
00211             scale_factor = _system_def->max_speed_scale;
00212 
00213           scaled_height_half *= scale_factor;
00214         }
00215       }
00216 
00217       // upper-left vertex
00218       _particle_vertices[v]._x = -scaled_width_half;
00219       _particle_vertices[v]._y = -scaled_height_half;
00220       RotatePoint(_particle_vertices[v]._x, _particle_vertices[v]._y, rotation_angle);
00221       _particle_vertices[v]._x += _particles[j].x;
00222       _particle_vertices[v]._y += _particles[j].y;
00223       ++v;
00224 
00225       // upper-right vertex
00226       _particle_vertices[v]._x = scaled_width_half;
00227       _particle_vertices[v]._y = -scaled_height_half;
00228       RotatePoint(_particle_vertices[v]._x, _particle_vertices[v]._y, rotation_angle);
00229       _particle_vertices[v]._x += _particles[j].x;
00230       _particle_vertices[v]._y += _particles[j].y;
00231       ++v;
00232 
00233       // lower-right vertex
00234       _particle_vertices[v]._x = scaled_width_half;
00235       _particle_vertices[v]._y = scaled_height_half;
00236       RotatePoint(_particle_vertices[v]._x, _particle_vertices[v]._y, rotation_angle);
00237       _particle_vertices[v]._x += _particles[j].x;
00238       _particle_vertices[v]._y += _particles[j].y;
00239       ++v;
00240 
00241       // lower-left vertex
00242       _particle_vertices[v]._x = -scaled_width_half;
00243       _particle_vertices[v]._y = scaled_height_half;
00244       RotatePoint(_particle_vertices[v]._x, _particle_vertices[v]._y, rotation_angle);
00245       _particle_vertices[v]._x += _particles[j].x;
00246       _particle_vertices[v]._y += _particles[j].y;
00247       ++v;
00248 
00249 
00250     }
00251   }
00252   else
00253   {
00254     int32 v = 0;
00255 
00256     for(int32 j = 0; j < _num_particles; ++j)
00257     {
00258       float scaled_width_half  = img_width_half * _particles[j].size_x;
00259       float scaled_height_half = img_height_half * _particles[j].size_y;
00260 
00261       // upper-left vertex
00262       _particle_vertices[v]._x = _particles[j].x - scaled_width_half;
00263       _particle_vertices[v]._y = _particles[j].y - scaled_height_half;
00264       ++v;
00265 
00266       // upper-right vertex
00267       _particle_vertices[v]._x = _particles[j].x + scaled_width_half;
00268       _particle_vertices[v]._y = _particles[j].y - scaled_height_half;
00269       ++v;
00270 
00271       // lower-right vertex
00272       _particle_vertices[v]._x = _particles[j].x + scaled_width_half;
00273       _particle_vertices[v]._y = _particles[j].y + scaled_height_half;
00274       ++v;
00275 
00276       // lower-left vertex
00277       _particle_vertices[v]._x = _particles[j].x - scaled_width_half;
00278       _particle_vertices[v]._y = _particles[j].y + scaled_height_half;
00279       ++v;
00280     }
00281   }
00282 
00283   // fill the color array
00284 
00285   int32 c = 0;
00286   for(int32 j = 0; j < _num_particles; ++j)
00287   {
00288     Color color = _particles[j].color;
00289 
00290     if(_system_def->smooth_animation)
00291       color = color * (1.0f - frame_progress);
00292 
00293     if(use_scene_lighting)
00294       color = color * scene_light_modifier;
00295 
00296     _particle_colors[c] = color;
00297     ++c;
00298     _particle_colors[c] = color;
00299     ++c;
00300     _particle_colors[c] = color;
00301     ++c;
00302     _particle_colors[c] = color;
00303     ++c;
00304   }
00305 
00306   // fill the texcoord array
00307 
00308   int32 t = 0;
00309   for(int32 j = 0; j < _num_particles; ++j)
00310   {
00311     // upper-left
00312     _particle_texcoords[t]._t0 = u1;
00313     _particle_texcoords[t]._t1 = v1;
00314     ++t;
00315 
00316     // upper-right
00317     _particle_texcoords[t]._t0 = u2;
00318     _particle_texcoords[t]._t1 = v1;
00319     ++t;
00320 
00321     // lower-right
00322     _particle_texcoords[t]._t0 = u2;
00323     _particle_texcoords[t]._t1 = v2;
00324     ++t;
00325 
00326     // lower-left
00327     _particle_texcoords[t]._t0 = u1;
00328     _particle_texcoords[t]._t1 = v2;
00329     ++t;
00330   }
00331 
00332   glEnableClientState(GL_VERTEX_ARRAY);
00333   glEnableClientState(GL_COLOR_ARRAY);
00334   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00335   glVertexPointer   (2, GL_FLOAT, 0, &_particle_vertices[0]);
00336   glColorPointer    (4, GL_FLOAT, 0, &_particle_colors[0]);
00337   glTexCoordPointer (2, GL_FLOAT, 0, &_particle_texcoords[0]);
00338 
00339   glDrawArrays(GL_QUADS, 0, _num_particles * 4);
00340 
00341   glDisableClientState(GL_VERTEX_ARRAY);
00342 
00343   if(_system_def->smooth_animation)
00344   {
00345     glEnableClientState(GL_VERTEX_ARRAY);
00346 
00347     int findex = _animation.GetCurrentFrameIndex();
00348     findex = (findex + 1) % _animation.GetNumFrames();
00349 
00350     StillImage *id2 = _animation.GetFrame(findex);
00351     Image *img2 = id2->_elements[0].image;
00352     VideoManager->_BindTexture(img2->texture_sheet->tex_ID);
00353 
00354 
00355     u1 = img2->u1;
00356     u2 = img2->u2;
00357     v1 = img2->v1;
00358     v2 = img2->v2;
00359 
00360 
00361     t = 0;
00362     for(int32 j = 0; j < _num_particles; ++j)
00363     {
00364       // upper-left
00365       _particle_texcoords[t]._t0 = u1;
00366       _particle_texcoords[t]._t1 = v1;
00367       ++t;
00368 
00369       // upper-right
00370       _particle_texcoords[t]._t0 = u2;
00371       _particle_texcoords[t]._t1 = v1;
00372       ++t;
00373 
00374       // lower-right
00375       _particle_texcoords[t]._t0 = u2;
00376       _particle_texcoords[t]._t1 = v2;
00377       ++t;
00378 
00379       // lower-left
00380       _particle_texcoords[t]._t0 = u1;
00381       _particle_texcoords[t]._t1 = v2;
00382       ++t;
00383     }
00384 
00385 
00386 
00387     c = 0;
00388     for(int32 j = 0; j < _num_particles; ++j)
00389     {
00390       Color color = _particles[j].color;
00391       color = color * frame_progress;
00392       if(use_scene_lighting)
00393         color = color * scene_light_modifier;
00394 
00395       _particle_colors[c] = color;
00396       ++c;
00397       _particle_colors[c] = color;
00398       ++c;
00399       _particle_colors[c] = color;
00400       ++c;
00401       _particle_colors[c] = color;
00402       ++c;
00403     }
00404 
00405     glVertexPointer   (2, GL_FLOAT, 0, &_particle_vertices[0]);
00406     glColorPointer    (4, GL_FLOAT, 0, &_particle_colors[0]);
00407     glTexCoordPointer (2, GL_FLOAT, 0, &_particle_texcoords[0]);
00408 
00409     glDrawArrays(GL_QUADS, 0, _num_particles * 4);
00410 
00411     glDisableClientState(GL_VERTEX_ARRAY);
00412     glDisableClientState(GL_COLOR_ARRAY);
00413     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
00414   }
00415 
00416   return true;
00417 }
00418 
00419 
00420 //-----------------------------------------------------------------------------
00421 // IsAlive: returns whether the particle system has active particles or not
00422 //-----------------------------------------------------------------------------
00423 
00424 bool ParticleSystem::IsAlive() const
00425 {
00426   return _alive && _system_def->enabled;
00427 }
00428 
00429 
00430 //-----------------------------------------------------------------------------
00431 // IsStopped: returns whether the system has been stopped due to a call to Stop(),
00432 //            meaning that it cannot emit any more particles
00433 //-----------------------------------------------------------------------------
00434 
00435 bool ParticleSystem::IsStopped() const
00436 {
00437   return _stopped;
00438 }
00439 
00440 
00441 //-----------------------------------------------------------------------------
00442 // Update: updates particle positions and properties, and emits/kills particles
00443 //-----------------------------------------------------------------------------
00444 
00445 bool ParticleSystem::Update(float frame_time, const EffectParameters &params)
00446 {
00447   if(!_system_def->enabled)
00448     return true;
00449 
00450   _age += frame_time;
00451 
00452   if(_age < _system_def->emitter._start_time)
00453   {
00454     _last_update_time = _age;
00455     return true;
00456   }
00457 
00458   _animation.Update();
00459 
00460   // update properties of existing particles
00461   _UpdateParticles(frame_time, params);
00462 
00463   // figure out how many particles need to be emitted this frame
00464   int32 num_particles_to_emit = 0;
00465   if(!_stopped)
00466   {
00467     if(_system_def->emitter._emitter_mode == EMITTER_MODE_ALWAYS)
00468     {
00469       num_particles_to_emit = _system_def->max_particles - _num_particles;
00470     }
00471     else if(_system_def->emitter._emitter_mode != EMITTER_MODE_BURST)
00472     {
00473       float time_low  = _last_update_time * _system_def->emitter._emission_rate;
00474       float time_high = _age * _system_def->emitter._emission_rate;
00475 
00476       time_low  = floorf(time_low);
00477       time_high = ceilf(time_high);
00478 
00479       num_particles_to_emit = static_cast<int32>(time_high - time_low) - 1;
00480 
00481       if(num_particles_to_emit + _num_particles > _max_particles)
00482         num_particles_to_emit = _max_particles - _num_particles;
00483     }
00484     else
00485     {
00486       num_particles_to_emit = _system_def->max_particles;
00487     }
00488   }
00489 
00490   // kill expired particles. If there are particles waiting to be emitted, then instead of
00491   // killing, just respawn the expired particle since this is much more efficient
00492   _KillParticles(num_particles_to_emit, params);
00493 
00494   // if there are still any particles waiting to be emitted, emit them
00495   _EmitParticles(num_particles_to_emit, params);
00496 
00497   // stop the particle system immediately if burst is used
00498   if(_system_def->emitter._emitter_mode == EMITTER_MODE_BURST)
00499     Stop();
00500 
00501   // stop the system if it's past its lifetime. Note that the only mode in which
00502   // the system lifetime is applicable is ONE_SHOT mode
00503   if(_system_def->emitter._emitter_mode == EMITTER_MODE_ONE_SHOT)
00504   {
00505     if(_age > _system_def->system_lifetime)
00506       _stopped = true;
00507   }
00508 
00509   // check if the system is dead
00510   if(_num_particles == 0 && _stopped)
00511   {
00512     _alive = false;
00513   }
00514 
00515   _last_update_time = _age;
00516   return true;
00517 }
00518 
00519 
00520 //-----------------------------------------------------------------------------
00521 // GetNumParticles: return the number of active particles
00522 //-----------------------------------------------------------------------------
00523 
00524 int32 ParticleSystem::GetNumParticles() const
00525 {
00526   return _num_particles;
00527 }
00528 
00529 
00530 //-----------------------------------------------------------------------------
00531 // Destroy: destroys the system (when the effect is destroyed)
00532 //-----------------------------------------------------------------------------
00533 
00534 void ParticleSystem::Destroy()
00535 {
00536   _particles.clear();
00537   _particle_vertices.clear();
00538   VideoManager->DeleteImage(_animation);
00539 }
00540 
00541 
00542 //-----------------------------------------------------------------------------
00543 // Stop: ceases particle emission
00544 //-----------------------------------------------------------------------------
00545 
00546 void ParticleSystem::Stop()
00547 {
00548   _stopped = true;
00549 }
00550 
00551 
00552 //-----------------------------------------------------------------------------
00553 // _UpdateParticles: helper function to update the positions and properties
00554 //-----------------------------------------------------------------------------
00555 
00556 void ParticleSystem::_UpdateParticles(float t, const EffectParameters &params)
00557 {
00558   for(int j = 0; j < _num_particles; ++j)
00559   {
00560     // calculate a time for the particle from 0 to 1 since this is what
00561     // the keyframes are based on
00562     float scaled_time = _particles[j].time / _particles[j].lifetime;
00563 
00564     // figure out which keyframe we're on
00565     if(_particles[j].next_keyframe)
00566     {
00567       ParticleKeyframe *old_next = _particles[j].next_keyframe;
00568 
00569       // check if we need to advance the keyframe
00570       if(scaled_time >= _particles[j].next_keyframe->time)
00571       {
00572         // figure out what keyframe we're on
00573         size_t num_keyframes = _system_def->keyframes.size();
00574 
00575         size_t k;
00576         for(k = 0; k < num_keyframes; ++k)
00577         {
00578           if(_system_def->keyframes[k]->time > scaled_time)
00579           {
00580             _particles[j].current_keyframe = _system_def->keyframes[k - 1];
00581             _particles[j].next_keyframe    = _system_def->keyframes[k];
00582             break;
00583           }
00584         }
00585 
00586         // if we didn't find any keyframe whose time is larger than this
00587         // particle's time, then we are on the last one
00588         if(k == num_keyframes)
00589         {
00590           _particles[j].current_keyframe = _system_def->keyframes[k - 1];
00591           _particles[j].next_keyframe = NULL;
00592 
00593           // set all of the keyframed properties to the value stored in the last
00594           // keyframe
00595           _particles[j].color          = _particles[j].current_keyframe->color;
00596           _particles[j].rotation_speed = _particles[j].current_keyframe->rotation_speed;
00597           _particles[j].size_x         = _particles[j].current_keyframe->size_x;
00598           _particles[j].size_y         = _particles[j].current_keyframe->size_y;
00599         }
00600 
00601         // if we skipped ahead only 1 keyframe, then inherit the current variations
00602         // from the next ones
00603         if(_particles[j].current_keyframe == old_next)
00604         {
00605           _particles[j].current_color_variation = _particles[j].next_color_variation;
00606           _particles[j].current_rotation_speed_variation = _particles[j].current_rotation_speed_variation;
00607           _particles[j].current_size_variation_x = _particles[j].next_size_variation_x;
00608           _particles[j].current_size_variation_y = _particles[j].next_size_variation_y;
00609         }
00610         else
00611         {
00612           _particles[j].current_rotation_speed_variation = RandomFloat(-_particles[j].current_keyframe->rotation_speed_variation, _particles[j].current_keyframe->rotation_speed_variation);
00613           for(int32 c = 0; c < 4; ++c)
00614             _particles[j].current_color_variation[c] = RandomFloat(-_particles[j].current_keyframe->color_variation[c], _particles[j].current_keyframe->color_variation[c]);
00615           _particles[j].current_size_variation_x = RandomFloat(-_particles[j].current_keyframe->size_variation_x, _particles[j].current_keyframe->size_variation_x);
00616           _particles[j].current_size_variation_y = RandomFloat(-_particles[j].current_keyframe->size_variation_y, _particles[j].current_keyframe->size_variation_y);
00617         }
00618 
00619         // if there is a next keyframe, generate variations for it
00620         if(_particles[j].next_keyframe)
00621         {
00622           _particles[j].next_rotation_speed_variation = RandomFloat(-_particles[j].next_keyframe->rotation_speed_variation, _particles[j].next_keyframe->rotation_speed_variation);
00623           for(int32 c = 0; c < 4; ++c)
00624             _particles[j].next_color_variation[c] = RandomFloat(-_particles[j].next_keyframe->color_variation[c], _particles[j].next_keyframe->color_variation[c]);
00625           _particles[j].next_size_variation_x = RandomFloat(-_particles[j].next_keyframe->size_variation_x, _particles[j].next_keyframe->size_variation_x);
00626           _particles[j].next_size_variation_y = RandomFloat(-_particles[j].next_keyframe->size_variation_y, _particles[j].next_keyframe->size_variation_y);
00627         }
00628       }
00629     }
00630 
00631 
00632     // if we aren't already at the last keyframe, interpolate to figure out the
00633     // current keyframed properties
00634     if(_particles[j].next_keyframe)
00635     {
00636       // figure out how far we are from the current to the next (0.0 to 1.0)
00637       float a = (scaled_time - _particles[j].current_keyframe->time) / (_particles[j].next_keyframe->time - _particles[j].current_keyframe->time);
00638 
00639       _particles[j].rotation_speed = Lerp(a, _particles[j].current_keyframe->rotation_speed + _particles[j].current_rotation_speed_variation, _particles[j].next_keyframe->rotation_speed + _particles[j].next_rotation_speed_variation);
00640       _particles[j].size_x         = Lerp(a, _particles[j].current_keyframe->size_x + _particles[j].current_size_variation_x, _particles[j].next_keyframe->size_x + _particles[j].next_size_variation_x);
00641       _particles[j].size_y         = Lerp(a, _particles[j].current_keyframe->size_y + _particles[j].current_size_variation_y, _particles[j].next_keyframe->size_y + _particles[j].next_size_variation_y);
00642       _particles[j].color[0]       = Lerp(a, _particles[j].current_keyframe->color[0] + _particles[j].current_color_variation[0], _particles[j].next_keyframe->color[0] + _particles[j].next_color_variation[0]);
00643       _particles[j].color[1]       = Lerp(a, _particles[j].current_keyframe->color[1] + _particles[j].current_color_variation[1], _particles[j].next_keyframe->color[1] + _particles[j].next_color_variation[1]);
00644       _particles[j].color[2]       = Lerp(a, _particles[j].current_keyframe->color[2] + _particles[j].current_color_variation[2], _particles[j].next_keyframe->color[2] + _particles[j].next_color_variation[2]);
00645       _particles[j].color[3]       = Lerp(a, _particles[j].current_keyframe->color[3] + _particles[j].current_color_variation[3], _particles[j].next_keyframe->color[3] + _particles[j].next_color_variation[3]);
00646     }
00647 
00648 
00649     _particles[j].rotation_angle += _particles[j].rotation_speed * _particles[j].rotation_direction * t;
00650 
00651     float wind_velocity_x = _particles[j].wind_velocity_x;
00652     float wind_velocity_y = _particles[j].wind_velocity_y;
00653 
00654     _particles[j].combined_velocity_x = _particles[j].velocity_x + wind_velocity_x;
00655     _particles[j].combined_velocity_y = _particles[j].velocity_y + wind_velocity_y;
00656 
00657     if(_system_def->wave_motion_used && _particles[j].wave_half_amplitude > 0.0f)
00658     {
00659       // find the magnitude of the wave velocity
00660 //      float half_amp = _particles[j].wave_half_amplitude; UNUSED VARIABLE
00661 //      float wcoef = _particles[j].wave_length_coefficient; UNUSED VARIABLE
00662       float wave_speed = _particles[j].wave_half_amplitude * sinf(_particles[j].wave_length_coefficient * _particles[j].time);
00663 
00664       // now the wave velocity is just that wave speed times the particle's tangential vector
00665       float tangent_x = -_particles[j].combined_velocity_y;
00666       float tangent_y = _particles[j].combined_velocity_x;
00667       float speed = sqrtf(tangent_x * tangent_x + tangent_y * tangent_y);
00668       tangent_x /= speed;
00669       tangent_y /= speed;
00670 
00671       float wave_velocity_x = tangent_x * wave_speed;
00672       float wave_velocity_y = tangent_y * wave_speed;
00673 
00674       _particles[j].combined_velocity_x += wave_velocity_x;
00675       _particles[j].combined_velocity_y += wave_velocity_y;
00676     }
00677 
00678     _particles[j].x += (_particles[j].combined_velocity_x) * t;
00679     _particles[j].y += (_particles[j].combined_velocity_y) * t;
00680 
00681 
00682     // client-specified acceleration (dv = a * t)
00683     _particles[j].velocity_x += _particles[j].acceleration_x * t;
00684     _particles[j].velocity_y += _particles[j].acceleration_y * t;
00685 
00686     // radial acceleration: calculate unit vector from emitter center to this particle,
00687     // and scale by the radial acceleration, if there is any
00688 
00689 
00690     bool use_radial     = (_particles[j].radial_acceleration != 0.0f);
00691     bool use_tangential = (_particles[j].tangential_acceleration != 0.0f);
00692 
00693 
00694     if(use_radial || use_tangential)
00695     {
00696       // unit vector from attractor to particle
00697       float attractor_to_particle_x;
00698       float attractor_to_particle_y;
00699 
00700       if(_system_def->user_defined_attractor)
00701       {
00702         attractor_to_particle_x = _particles[j].x - params.attractor_x;
00703         attractor_to_particle_y = _particles[j].y - params.attractor_y;
00704       }
00705       else
00706       {
00707         attractor_to_particle_x = _particles[j].x - _system_def->emitter._center_x;
00708         attractor_to_particle_y = _particles[j].y - _system_def->emitter._center_y;
00709       }
00710 
00711       float distance = sqrtf(attractor_to_particle_x * attractor_to_particle_x + attractor_to_particle_y * attractor_to_particle_y);
00712 
00713       if(distance != 0.0f)
00714       {
00715         attractor_to_particle_x /= distance;
00716         attractor_to_particle_y /= distance;
00717       }
00718 
00719       // radial acceleration
00720       if(use_radial)
00721       {
00722         if(_system_def->attractor_falloff != 0.0f)
00723         {
00724           float attraction = 1.0f - _system_def->attractor_falloff * distance;
00725           if(attraction > 0.0f)
00726           {
00727             _particles[j].velocity_x += attractor_to_particle_x * _particles[j].radial_acceleration * t * attraction;
00728             _particles[j].velocity_y += attractor_to_particle_y * _particles[j].radial_acceleration * t * attraction;
00729           }
00730         }
00731         else
00732         {
00733           _particles[j].velocity_x += attractor_to_particle_x * _particles[j].radial_acceleration * t;
00734           _particles[j].velocity_y += attractor_to_particle_y * _particles[j].radial_acceleration * t;
00735         }
00736       }
00737 
00738       // tangential acceleration
00739       if(use_tangential)
00740       {
00741         // tangent vector is simply perpendicular vector
00742         float tangent_x = -attractor_to_particle_y;
00743         float tangent_y = attractor_to_particle_x;
00744 
00745         _particles[j].velocity_x += tangent_x * _particles[j].tangential_acceleration * t;
00746         _particles[j].velocity_y += tangent_y * _particles[j].tangential_acceleration * t;
00747       }
00748     }
00749 
00750 
00751     // damp the velocity
00752 
00753     if(_particles[j].damping != 1.0f)
00754     {
00755       _particles[j].velocity_x *= powf(_particles[j].damping, t);
00756       _particles[j].velocity_y *= powf(_particles[j].damping, t);
00757     }
00758 
00759     _particles[j].time += t;
00760   }
00761 }
00762 
00763 
00764 //-----------------------------------------------------------------------------
00765 // _KillParticles: helper function to kill expired particles. The num parameter
00766 //                 tells how many particles need to be emitted this frame.
00767 //                 If possible, we try to respawn particles instead of killing
00768 //                 and then emitting, because it is much more efficient.
00769 //-----------------------------------------------------------------------------
00770 
00771 void ParticleSystem::_KillParticles(int32 &num, const EffectParameters &params)
00772 {
00773   // check each active particle to see if it is expired
00774   for(int j = 0; j < _num_particles; ++j)
00775   {
00776     if(_particles[j].time > _particles[j].lifetime)
00777     {
00778       if(num > 0)
00779       {
00780         // if we still have particles to emit, then instead of killing the particle,
00781         // respawn it as a new one
00782         _RespawnParticle(j, params);
00783         --num;
00784       }
00785       else
00786       {
00787         // kill the particle, i.e. move the particle at the end of the array to this
00788         // particle's spot, and decrement _num_particles
00789 
00790         if(j != _num_particles - 1)
00791           _MoveParticle(_num_particles - 1, j);
00792         --_num_particles;
00793       }
00794     }
00795   }
00796 }
00797 
00798 
00799 //-----------------------------------------------------------------------------
00800 // _EmitParticles: helper function, emits new particles
00801 //-----------------------------------------------------------------------------
00802 
00803 void ParticleSystem::_EmitParticles(int32 num, const EffectParameters &params)
00804 {
00805   // respawn 'num' new particles at the end of the array
00806   for(int32 j = 0; j < num; ++j)
00807   {
00808     _RespawnParticle(_num_particles, params);
00809     ++_num_particles;
00810   }
00811 }
00812 
00813 
00814 //-----------------------------------------------------------------------------
00815 // _MoveParticle: helper function, moves the data for a particle from src
00816 //                to dest index in the array
00817 //-----------------------------------------------------------------------------
00818 
00819 void ParticleSystem::_MoveParticle(int32 src, int32 dest)
00820 {
00821   _particles[dest] = _particles[src];
00822 }
00823 
00824 
00825 //-----------------------------------------------------------------------------
00826 // _RespawnParticle: helper function to Update(), does the work of setting up
00827 //                   the properties for a newly spawned particle
00828 //-----------------------------------------------------------------------------
00829 
00830 void ParticleSystem::_RespawnParticle(int32 i, const EffectParameters &params)
00831 {
00832   const ParticleEmitter &emitter = _system_def->emitter;
00833 
00834   switch(emitter._shape)
00835   {
00836     case EMITTER_SHAPE_POINT:
00837     {
00838       _particles[i].x = emitter._x;
00839       _particles[i].y = emitter._y;
00840       break;
00841     }
00842     case EMITTER_SHAPE_LINE:
00843     {
00844       _particles[i].x = RandomFloat(emitter._x, emitter._x2);
00845       _particles[i].y = RandomFloat(emitter._y, emitter._y2);
00846       break;
00847     }
00848     case EMITTER_SHAPE_CIRCLE:
00849     {
00850       float angle = RandomFloat(0.0f, UTILS_2PI);
00851       _particles[i].x = emitter._radius * cosf(angle);
00852       _particles[i].y = emitter._radius * sinf(angle);
00853       break;
00854     }
00855     case EMITTER_SHAPE_FILLED_CIRCLE:
00856     {
00857       float radius_squared = emitter._radius;
00858       radius_squared *= radius_squared;
00859 
00860       // use rejection sampling to choose a point within the circle
00861       // this may need to be replaced by a speedier algorithm later on
00862       do
00863       {
00864         float half_radius = emitter._radius * 0.5f;
00865         _particles[i].x = RandomFloat(-half_radius, half_radius);
00866         _particles[i].y = RandomFloat(-half_radius, half_radius);
00867       } while(_particles[i].x * _particles[i].x +
00868               _particles[i].y * _particles[i].y > radius_squared);
00869 
00870 
00871       break;
00872     }
00873     case EMITTER_SHAPE_FILLED_RECTANGLE:
00874     {
00875       _particles[i].x = RandomFloat(emitter._x, emitter._x2);
00876       _particles[i].y = RandomFloat(emitter._y, emitter._y2);
00877       break;
00878     }
00879     default:
00880       break;
00881   };
00882 
00883 
00884   _particles[i].x += RandomFloat(-emitter._x_variation, emitter._x_variation);
00885   _particles[i].y += RandomFloat(-emitter._y_variation, emitter._y_variation);
00886 
00887   if(params.orientation != 0.0f)
00888     RotatePoint(_particles[i].x, _particles[i].y, params.orientation);
00889 
00890   _particles[i].color = _system_def->keyframes[0]->color;
00891 
00892   _particles[i].rotation_speed  = _system_def->keyframes[0]->rotation_speed;
00893   _particles[i].time            = 0.0f;
00894   _particles[i].size_x            = _system_def->keyframes[0]->size_x;
00895   _particles[i].size_y            = _system_def->keyframes[0]->size_y;
00896 
00897   if(_system_def->random_initial_angle)
00898     _particles[i].rotation_angle = RandomFloat(0.0f, UTILS_2PI);
00899   else
00900     _particles[i].rotation_angle = 0.0f;
00901 
00902   _particles[i].current_keyframe = _system_def->keyframes[0];
00903 
00904   if(_system_def->keyframes.size() > 1)
00905     _particles[i].next_keyframe = _system_def->keyframes[1];
00906   else
00907     _particles[i].next_keyframe = NULL;
00908 
00909   float speed = _system_def->emitter._initial_speed;
00910   speed += RandomFloat(-emitter._initial_speed_variation, emitter._initial_speed_variation);
00911 
00912 
00913   if(_system_def->emitter._spin == EMITTER_SPIN_CLOCKWISE)
00914   {
00915     _particles[i].rotation_direction = 1.0f;
00916   }
00917   else if(_system_def->emitter._spin == EMITTER_SPIN_COUNTERCLOCKWISE)
00918   {
00919     _particles[i].rotation_direction = -1.0f;
00920   }
00921   else
00922   {
00923     _particles[i].rotation_direction = static_cast<float>(2 * (rand()%2)) - 1.0f;
00924   }
00925 
00926   // figure out the orientation
00927 
00928   float angle = 0.0f;
00929 
00930   if(emitter._omnidirectional)
00931   {
00932     angle = RandomFloat(0.0f, UTILS_2PI);
00933   }
00934   else if(emitter._inner_cone == 0.0f && emitter._outer_cone == 0.0f)
00935   {
00936     angle = emitter._orientation + params.orientation;
00937   }
00938 
00939   _particles[i].velocity_x = speed * cosf(angle);
00940   _particles[i].velocity_y = speed * sinf(angle);
00941 
00942   // figure out property variations
00943 
00944   _particles[i].current_size_variation_x  = RandomFloat(-_system_def->keyframes[0]->size_variation_x, _system_def->keyframes[0]->size_variation_x);
00945   _particles[i].current_size_variation_y  = RandomFloat(-_system_def->keyframes[0]->size_variation_y, _system_def->keyframes[0]->size_variation_y);
00946 
00947   for(int32 j = 0; j < 4; ++j)
00948     _particles[i].current_color_variation[j] = RandomFloat(-_system_def->keyframes[0]->color_variation[j], _system_def->keyframes[0]->color_variation[j]);
00949 
00950   _particles[i].current_rotation_speed_variation = RandomFloat(-_system_def->keyframes[0]->rotation_speed_variation, _system_def->keyframes[0]->rotation_speed_variation);
00951 
00952   if(_system_def->keyframes.size() > 1)
00953   {
00954     // figure out the next keyframe's variations
00955     _particles[i].next_size_variation_x  = RandomFloat(-_system_def->keyframes[1]->size_variation_x, _system_def->keyframes[1]->size_variation_x);
00956     _particles[i].next_size_variation_y  = RandomFloat(-_system_def->keyframes[1]->size_variation_y, _system_def->keyframes[1]->size_variation_y);
00957 
00958     for(int32 j = 0; j < 4; ++j)
00959       _particles[i].next_color_variation[j] = RandomFloat(-_system_def->keyframes[1]->color_variation[j], _system_def->keyframes[1]->color_variation[j]);
00960 
00961     _particles[i].next_rotation_speed_variation = RandomFloat(-_system_def->keyframes[1]->rotation_speed_variation, _system_def->keyframes[1]->rotation_speed_variation);
00962   }
00963   else
00964   {
00965     // if there's only 1 keyframe, then apply the variations now
00966     for(int32 j = 0; j < 4; ++j)
00967       _particles[i].color[j] += RandomFloat(-_particles[i].current_color_variation[j], _particles[i].current_color_variation[j]);
00968 
00969     _particles[i].size_x += RandomFloat(-_particles[i].current_size_variation_x, _particles[i].current_size_variation_x);
00970     _particles[i].size_y += RandomFloat(-_particles[i].current_size_variation_y, _particles[i].current_size_variation_y);
00971 
00972     _particles[i].rotation_speed += RandomFloat(-_particles[i].current_rotation_speed_variation, _particles[i].current_rotation_speed_variation);
00973   }
00974 
00975   _particles[i].tangential_acceleration = _system_def->tangential_acceleration;
00976   if(_system_def->tangential_acceleration_variation != 0.0f)
00977     _particles[i].tangential_acceleration += RandomFloat(-_system_def->tangential_acceleration_variation, _system_def->tangential_acceleration_variation);
00978 
00979   _particles[i].radial_acceleration = _system_def->radial_acceleration;
00980   if(_system_def->radial_acceleration_variation != 0.0f)
00981     _particles[i].radial_acceleration += RandomFloat(-_system_def->radial_acceleration_variation, _system_def->radial_acceleration_variation);
00982 
00983   _particles[i].acceleration_x = _system_def->acceleration_x;
00984   if(_system_def->acceleration_variation_x != 0.0f)
00985     _particles[i].acceleration_x += RandomFloat(-_system_def->acceleration_variation_x, _system_def->acceleration_variation_x);
00986 
00987   _particles[i].acceleration_y = _system_def->acceleration_y;
00988   if(_system_def->acceleration_variation_y != 0.0f)
00989     _particles[i].acceleration_y += RandomFloat(-_system_def->acceleration_variation_y, _system_def->acceleration_variation_y);
00990 
00991   _particles[i].wind_velocity_x = _system_def->wind_velocity_x;
00992   if(_system_def->wind_velocity_variation_x != 0.0f)
00993     _particles[i].wind_velocity_x += RandomFloat(-_system_def->wind_velocity_variation_x, _system_def->wind_velocity_variation_x);
00994 
00995   _particles[i].wind_velocity_y = _system_def->wind_velocity_y;
00996   if(_system_def->wind_velocity_variation_y != 0.0f)
00997     _particles[i].wind_velocity_y += RandomFloat(-_system_def->wind_velocity_variation_y, _system_def->wind_velocity_variation_y);
00998 
00999   _particles[i].damping = _system_def->damping;
01000   if(_system_def->damping_variation != 0.0f)
01001     _particles[i].damping += RandomFloat(-_system_def->damping_variation, _system_def->damping_variation);
01002 
01003   if(_system_def->wave_motion_used)
01004   {
01005     _particles[i].wave_length_coefficient = _system_def->wave_length;
01006     if(_system_def->wave_length_variation != 0.0f)
01007       _particles[i].wave_length_coefficient += RandomFloat(-_system_def->wave_length_variation, _system_def->wave_length_variation);
01008 
01009     _particles[i].wave_length_coefficient = UTILS_2PI / _particles[i].wave_length_coefficient;
01010 
01011     _particles[i].wave_half_amplitude = _system_def->wave_amplitude;
01012     if(_system_def->wave_amplitude != 0.0f)
01013       _particles[i].wave_half_amplitude += RandomFloat(-_system_def->wave_amplitude_variation, _system_def->wave_amplitude_variation);
01014     _particles[i].wave_half_amplitude *= 0.5f;
01015   }
01016 
01017   _particles[i].lifetime = _system_def->particle_lifetime + RandomFloat(-_system_def->particle_lifetime_variation, _system_def->particle_lifetime_variation);
01018 }
01019 
01020 
01021 //-----------------------------------------------------------------------------
01022 // GetAge: return the number of seconds since this system was created
01023 //-----------------------------------------------------------------------------
01024 
01025 float ParticleSystem::GetAge() const
01026 {
01027   return _age;
01028 }
01029 
01030 
01031 
01032 }  // namespace private_video
01033 }  // namespace hoa_video

Generated on Fri Jul 6 23:11:21 2007 for Hero of Allacrost by  doxygen 1.5.1