Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

particleSystem.cxx

Go to the documentation of this file.
00001 // Filename: particleSystem.cxx
00002 // Created by:  charles (14Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include <stdlib.h>
00020 
00021 #include "luse.h"
00022 #include "lmat_ops.h"
00023 #include "clockObject.h"
00024 #include "physicsManager.h"
00025 #include "physicalNode.h"
00026 #include "nearly_zero.h"
00027 #include "transformState.h"
00028 #include "nodePath.h"
00029 
00030 #include "config_particlesystem.h"
00031 #include "particleSystem.h"
00032 #include "particleSystemManager.h"
00033 #include "pointParticleRenderer.h"
00034 #include "pointParticleFactory.h"
00035 #include "sphereSurfaceEmitter.h"
00036 
00037 ////////////////////////////////////////////////////////////////////
00038 //    Function : ParticleSystem
00039 //      Access : Public
00040 // Description : Default Constructor.
00041 ////////////////////////////////////////////////////////////////////
00042 ParticleSystem::
00043 ParticleSystem(int pool_size) :
00044   Physical(pool_size, false)
00045 {
00046   _birth_rate = 0.5f;
00047   _tics_since_birth = _birth_rate;
00048   _litter_size = 1;
00049   _litter_spread = 0;
00050   _living_particles = 0;
00051   _active_system_flag = true;
00052   _local_velocity_flag = true;
00053   _spawn_on_death_flag = false;
00054   _system_grows_older_flag = false;
00055   _system_lifespan = 0.0f;
00056   _i_was_spawned_flag = false;
00057   _particle_pool_size = 0;
00058 
00059   // just in case someone tries to do something that requires the
00060   // use of an emitter, renderer, or factory before they've actually
00061   // assigned one.  This is ok, because assigning them (set_renderer(),
00062   // set_emitter(), etc...) forces them to set themselves up for the
00063   // system, keeping the pool sizes consistent.
00064 
00065   _render_node.clear();
00066   _render_parent = new PandaNode("ParticleSystem default render parent");
00067 
00068   set_emitter(new SphereSurfaceEmitter);
00069 
00070   set_renderer(new PointParticleRenderer);
00071 
00072   //set_factory(new PointParticleFactory);
00073   _factory = new PointParticleFactory;
00074   clear_physics_objects();
00075 
00076   set_pool_size(pool_size);
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //    Function : ParticleSystem
00081 //      Access : Public
00082 // Description : Copy Constructor.
00083 ////////////////////////////////////////////////////////////////////
00084 ParticleSystem::
00085 ParticleSystem(const ParticleSystem& copy) :
00086   Physical(copy),
00087   _system_age(0.0f),
00088   _template_system_flag(false)
00089 {
00090   _birth_rate = copy._birth_rate;
00091   _litter_size = copy._litter_size;
00092   _litter_spread = copy._litter_spread;
00093   _active_system_flag = copy._active_system_flag;
00094   _local_velocity_flag = copy._local_velocity_flag;
00095   _spawn_on_death_flag = copy._spawn_on_death_flag;
00096   _i_was_spawned_flag = copy._i_was_spawned_flag;
00097   _system_grows_older_flag = copy._system_grows_older_flag;
00098   _emitter = copy._emitter;
00099   _renderer = copy._renderer->make_copy();
00100   _factory = copy._factory;
00101 
00102   _render_parent = copy._render_parent;
00103   _render_node = _renderer->get_render_node();
00104   _render_parent->add_child(_render_node);
00105 
00106   _tics_since_birth = _birth_rate;
00107   _system_lifespan = copy._system_lifespan;
00108   _living_particles = 0;
00109 
00110   set_pool_size(copy._particle_pool_size);
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //    Function : ~ParticleSystem
00115 //      Access : Public
00116 // Description : You get the ankles and I'll get the wrists.
00117 ////////////////////////////////////////////////////////////////////
00118 ParticleSystem::
00119 ~ParticleSystem(void) {
00120   set_pool_size(0);
00121 
00122   if (!_template_system_flag) {
00123     _renderer.clear();
00124 
00125     if (!_render_node.is_null())
00126       _render_parent->remove_child(_render_node);
00127   }
00128 }
00129 
00130 ////////////////////////////////////////////////////////////////////
00131 //    Function : birth_particle
00132 //      Access : Private
00133 // Description : A new particle is born.  This doesn't allocate,
00134 //               resets an element from the particle pool.
00135 ////////////////////////////////////////////////////////////////////
00136 bool ParticleSystem::
00137 birth_particle(void) {
00138   int pool_index;
00139 
00140   // make sure there's room for a new particle
00141   if (_living_particles >= _particle_pool_size) {
00142 #ifdef PSDEBUG
00143     if (_living_particles > _particle_pool_size) {
00144       cout << "_living_particles > _particle_pool_size" << endl;
00145     }
00146 #endif
00147     return false;
00148   }
00149 
00150 #ifdef PSDEBUG
00151   if (0 == _free_particle_fifo.size()) {
00152     cout << "Error: _free_particle_fifo is empty, but _living_particles < _particle_pool_size" << endl;
00153     return false;
00154   }
00155 #endif
00156 
00157   pool_index = _free_particle_fifo.back();
00158   _free_particle_fifo.pop_back();
00159 
00160   // get a handle on our particle.
00161   BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p();
00162 
00163   // start filling out the variables.
00164   _factory->populate_particle(bp);
00165 
00166   bp->set_alive(true);
00167   bp->set_active(true);
00168   bp->init();
00169 
00170   // get the location of the new particle.
00171   LPoint3f new_pos, world_pos;
00172   LVector3f new_vel;
00173   GeomNode *render_node;
00174 
00175   _emitter->generate(new_pos, new_vel);
00176   render_node = _renderer->get_render_node();
00177 
00178   // go from birth space to render space
00179   NodePath physical_np(get_physical_node());
00180   NodePath render_np(render_node);
00181   const LMatrix4f &birth_to_render_xform = physical_np.get_mat(render_np);
00182   world_pos = new_pos * birth_to_render_xform;
00183 
00184   //  cout << "New particle at " << world_pos << endl;
00185 
00186   // possibly transform the initial velocity as well.
00187   if (_local_velocity_flag == false)
00188     new_vel = new_vel * birth_to_render_xform;
00189 
00190   bp->set_position_HandOfGod(world_pos/* + (NORMALIZED_RAND() * new_vel)*/);
00191   bp->set_velocity(new_vel);
00192 
00193   _living_particles++;
00194 
00195   // propogate information down to renderer
00196   _renderer->birth_particle(pool_index);
00197 
00198   return true;
00199 }
00200 
00201 ////////////////////////////////////////////////////////////////////
00202 //    Function : birth_litter
00203 //      Access : Private
00204 // Description : spawns a new batch of particles
00205 ////////////////////////////////////////////////////////////////////
00206 void ParticleSystem::
00207 birth_litter(void) {
00208   int litter_size, i;
00209 
00210   litter_size = _litter_size;
00211 
00212   if (_litter_spread != 0)
00213     litter_size += I_SPREAD(_litter_spread);
00214 
00215   for (i = 0; i < litter_size; i++) {
00216     if (birth_particle() == false)
00217       return;
00218   }
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //    Function : spawn_child_system
00223 //      Access : private
00224 // Description : Creates a new particle system based on local
00225 //               template info and adds it to the ps and physics
00226 //               managers
00227 ////////////////////////////////////////////////////////////////////
00228 void ParticleSystem::
00229 spawn_child_system(BaseParticle *bp) {
00230   // first, make sure that the system exists in the graph via a
00231   // physicalnode reference.
00232   PhysicalNode *this_pn = get_physical_node();
00233   if (!this_pn) {
00234     physics_cat.error() << "ParticleSystem::spawn_child_system: "
00235                         << "Spawning system is not in the scene graph,"
00236                         << " aborting." << endl;
00237     return;
00238   }
00239 
00240   if (this_pn->get_num_parents() == 0) {
00241     physics_cat.error() << "ParticleSystem::spawn_child_system: "
00242                         << "PhysicalNode this system is contained in "
00243                         << "has no parent, aborting." << endl;
00244     return;
00245   }
00246 
00247   PandaNode *parent = this_pn->get_parent(0);
00248 
00249   // handle the spawn templates
00250   int new_ps_index = rand() % _spawn_templates.size();
00251   ParticleSystem *ps_template = _spawn_templates[new_ps_index];
00252 
00253   // create a new particle system
00254   PT(ParticleSystem) new_ps = new ParticleSystem(*ps_template);
00255   new_ps->_i_was_spawned_flag = true;
00256 
00257   // first, set up the render node info.
00258   new_ps->_render_parent = _spawn_render_node;
00259   new_ps->_render_node = new_ps->_renderer->get_render_node();
00260   new_ps->_render_parent->add_child(new_ps->_render_node);
00261 
00262   // now set up the new system's PhysicalNode.
00263   PT(PhysicalNode) new_pn = new PhysicalNode("new_pn");
00264   new_pn->add_physical(new_ps);
00265 
00266   // the transform on the new child has to represent the transform
00267   // from the current system up to its parent, and then subsequently
00268   // down to the new child.
00269   parent->add_child(new_pn);
00270 
00271   NodePath parent_np(parent);
00272   NodePath physical_np(get_physical_node());
00273 
00274   const LMatrix4f &old_system_to_parent_xform = physical_np.get_mat(parent_np);
00275 
00276   LMatrix4f child_space_xform = old_system_to_parent_xform *
00277     bp->get_lcs();
00278 
00279   new_pn->set_transform(TransformState::make_mat(child_space_xform));
00280 
00281   // tack the new system onto the managers
00282   _manager->attach_particlesystem(new_ps);
00283   get_physics_manager()->attach_physical(new_ps);
00284 }
00285 
00286 ////////////////////////////////////////////////////////////////////
00287 //    Function : kill_particle
00288 //      Access : Private
00289 // Description : Kills a particle, returns its slot to the empty
00290 //               stack.
00291 ////////////////////////////////////////////////////////////////////
00292 void ParticleSystem::
00293 kill_particle(int pool_index) {
00294   // get a handle on our particle
00295   BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p();
00296 
00297   // create a new system where this one died, maybe.
00298   if (_spawn_on_death_flag == true)
00299     spawn_child_system(bp);
00300 
00301   // tell everyone that it's dead
00302   bp->set_alive(false);
00303   bp->set_active(false);
00304   bp->die();
00305 
00306   _free_particle_fifo.push_back(pool_index);
00307 
00308   // tell renderer
00309   _renderer->kill_particle(pool_index);
00310 
00311   _living_particles--;
00312 }
00313 
00314 ////////////////////////////////////////////////////////////////////
00315 //    Function : resize_pool
00316 //      Access : Private
00317 // Description : Resizes the particle pool
00318 ////////////////////////////////////////////////////////////////////
00319 #ifdef PSDEBUG
00320 #define PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
00321 #endif
00322 void ParticleSystem::
00323 resize_pool(int size) {
00324   int i;
00325   int delta = size - _particle_pool_size;
00326   int po_delta = _particle_pool_size - _physics_objects.size();
00327 
00328 #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
00329   cout << "resizing particle pool from " << _particle_pool_size
00330        << " to " << size << endl;
00331 #endif
00332 
00333   if (_factory.is_null()) {
00334     particlesystem_cat.error() << "ParticleSystem::resize_pool"
00335                                << " called with null _factory." << endl;
00336     return;
00337   }
00338 
00339   if (_renderer.is_null()) {
00340     particlesystem_cat.error() << "ParticleSystem::resize_pool"
00341                                << " called with null _renderer." << endl;
00342     return;
00343   }
00344 
00345   _particle_pool_size = size;
00346 
00347   // make sure the physics_objects array is OK
00348   if (po_delta) {
00349     if (po_delta > 0) {
00350       for (i = 0; i < po_delta; i++)
00351       {
00352         //        int free_index = _physics_objects.size();
00353 
00354         BaseParticle *new_particle = _factory->alloc_particle();
00355         if (new_particle) {
00356           _factory->populate_particle(new_particle);
00357 
00358           _physics_objects.push_back(new_particle);
00359         } else {
00360 #ifdef PSDEBUG
00361           cout << "Error allocating new particle" << endl;
00362           _particle_pool_size--;
00363 #endif
00364         }
00365       }
00366     } else {
00367 #ifdef PSDEBUG
00368       cout << "physics_object array is too large??" << endl;
00369       _particle_pool_size--;
00370 #endif
00371       po_delta = -po_delta;
00372       for (i = 0; i < po_delta; i++) {
00373         int delete_index = _physics_objects.size()-1;
00374         BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
00375         if (bp->get_alive()) {
00376           kill_particle(delete_index);
00377           _free_particle_fifo.pop_back();
00378         } else {
00379           pdeque<int>::iterator i;
00380           i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
00381           if (i != _free_particle_fifo.end()) {
00382             _free_particle_fifo.erase(i);
00383           }
00384         }
00385         _physics_objects.pop_back();
00386       }
00387     }
00388   }
00389 
00390   // disregard no change
00391   if (delta == 0)
00392     return;
00393 
00394   // update the pool
00395   if (delta > 0) {
00396     // add elements
00397     for (i = 0; i < delta; i++)
00398     {
00399       int free_index = _physics_objects.size();
00400 
00401       BaseParticle *new_particle = _factory->alloc_particle();
00402       if (new_particle) {
00403         _factory->populate_particle(new_particle);
00404 
00405         _physics_objects.push_back(new_particle);
00406         _free_particle_fifo.push_back(free_index);
00407       } else {
00408 #ifdef PSDEBUG
00409         cout << "Error allocating new particle" << endl;
00410         _particle_pool_size--;
00411 #endif
00412       }
00413     }
00414   } else {
00415     // subtract elements
00416     delta = -delta;
00417     for (i = 0; i < delta; i++) {
00418       int delete_index = _physics_objects.size()-1;
00419       BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
00420 
00421       if (bp->get_alive()) {
00422 #ifdef PSDEBUG
00423         cout << "WAS ALIVE" << endl;
00424 #endif
00425         kill_particle(delete_index);
00426         _free_particle_fifo.pop_back();
00427       } else {
00428 #ifdef PSDEBUG
00429         cout << "WAS NOT ALIVE" << endl;
00430 #endif
00431         pdeque<int>::iterator i;
00432         i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
00433         if (i != _free_particle_fifo.end()) {
00434           _free_particle_fifo.erase(i);
00435         }
00436 #ifdef PSDEBUG
00437         else {
00438           cout << "particle not found in free FIFO!!!!!!!!" << endl;
00439         }
00440 #endif
00441       }
00442 
00443       _physics_objects.pop_back();
00444     }
00445   }
00446 
00447   _renderer->resize_pool(_particle_pool_size);
00448 
00449 #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
00450   cout << "particle pool resized" << endl;
00451 #endif
00452 }
00453 
00454 //////////////////////////////////////////////////////////////////////
00455 //    Function : update
00456 //      Access : Public
00457 // Description : Updates the particle system.  Call once per frame.
00458 //////////////////////////////////////////////////////////////////////
00459 #ifdef PSDEBUG
00460 //#define PARTICLE_SYSTEM_UPDATE_SENTRIES
00461 #endif
00462 void ParticleSystem::
00463 update(float dt) {
00464   int ttl_updates_left = _living_particles;
00465   int current_index = 0, index_counter = 0;
00466   BaseParticle *bp;
00467   float age;
00468 
00469 #ifdef PSSANITYCHECK
00470   // check up on things
00471   if (sanity_check()) return;
00472 #endif
00473 
00474 #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
00475   cout << "UPDATE: pool size: " << _particle_pool_size
00476        << ", live particles: " << _living_particles << endl;
00477 #endif
00478 
00479   // run through the particle array
00480   while (ttl_updates_left) {
00481     current_index = index_counter;
00482     index_counter++;
00483 
00484 #ifdef PSDEBUG
00485     if (current_index >= _particle_pool_size) {
00486       cout << "ERROR: _living_particles is out of sync (too large)" << endl;
00487       cout << "pool size: " << _particle_pool_size
00488            << ", live particles: " << _living_particles
00489            << ", updates left: " << ttl_updates_left << endl;
00490       break;
00491     }
00492 #endif
00493 
00494     // get the current particle.
00495     bp = (BaseParticle *) _physics_objects[current_index].p();
00496 
00497 #ifdef PSDEBUG
00498     if (!bp) {
00499       cout << "NULL ptr at index " << current_index << endl;
00500       continue;
00501     }
00502 #endif
00503 
00504     if (bp->get_alive() == false)
00505       continue;
00506 
00507     age = bp->get_age() + dt;
00508     bp->set_age(age);
00509 
00510     if (age >= bp->get_lifespan())
00511       kill_particle(current_index);
00512     else
00513       bp->update();
00514 
00515     // break out early if we're lucky
00516     ttl_updates_left--;
00517   }
00518 
00519 
00520   // generate new particles if necessary.
00521   _tics_since_birth += dt;
00522 
00523   while (_tics_since_birth >= _birth_rate) {
00524     birth_litter();
00525     _tics_since_birth -= _birth_rate;
00526   }
00527 
00528 #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
00529   cout << "particle update complete" << endl;
00530 #endif
00531 
00532 }
00533 
00534 #ifdef PSSANITYCHECK
00535 //////////////////////////////////////////////////////////////////////
00536 //    Function : sanity_check
00537 //      Access : Private
00538 // Description : Checks consistency of live particle count, free
00539 //               particle list, etc. returns 0 if everything is normal
00540 //////////////////////////////////////////////////////////////////////
00541 #ifndef NDEBUG
00542 #define PSSCVERBOSE
00543 #endif
00544 
00545 class SC_valuenamepair : public ReferenceCount {
00546 public:
00547   int value;
00548   char *name;
00549   SC_valuenamepair(int v, char *s) : value(v), name(s) {}
00550 };
00551 
00552 // returns 0 if OK, # of errors if not OK
00553 static int check_free_live_total_particles(pvector< PT(SC_valuenamepair) > live_counts,
00554   pvector< PT(SC_valuenamepair) > dead_counts, pvector< PT(SC_valuenamepair) > total_counts,
00555   int print_all = 0) {
00556 
00557   int val = 0;
00558   int l, d, t;
00559 
00560   for(l = 0; l < live_counts.size(); l++) {
00561     for(d = 0; d < dead_counts.size(); d++) {
00562       for(t = 0; t < total_counts.size(); t++) {
00563         int live = live_counts[l]->value;
00564         int dead = dead_counts[d]->value;
00565         int total = total_counts[t]->value;
00566         if ((live + dead) != total) {
00567 #ifdef PSSCVERBOSE
00568           cout << "free/live/total count: "
00569                << live_counts[l]->name << " (" << live << ") + "
00570                << dead_counts[d]->name << " (" << dead << ") = "
00571                << live + dead << ", != "
00572                << total_counts[t]->name << " (" << total << ")"
00573                << endl;
00574 #endif
00575           val++;
00576         }
00577       }
00578     }
00579   }
00580 
00581   return val;
00582 }
00583 
00584 int ParticleSystem::
00585 sanity_check() {
00586   int result = 0;
00587   int i;
00588   BaseParticle *bp;
00589   int pool_size;
00590 
00591   ///////////////////////////////////////////////////////////////////
00592   // check pool size
00593   if (_particle_pool_size != _physics_objects.size()) {
00594 #ifdef PSSCVERBOSE
00595     cout << "_particle_pool_size (" << _particle_pool_size
00596          << ") != particle array size (" << _physics_objects.size() << ")" << endl;
00597 #endif
00598     result++;
00599   }
00600   pool_size = min(_particle_pool_size, _physics_objects.size());
00601   ///////////////////////////////////////////////////////////////////
00602 
00603   ///////////////////////////////////////////////////////////////////
00604   // find out how many particles are REALLY alive and dead
00605   int real_live_particle_count = 0;
00606   int real_dead_particle_count = 0;
00607 
00608   for (i = 0; i < _physics_objects.size(); i++) {
00609     bp = (BaseParticle *) _physics_objects[i].p();
00610     if (true == bp->get_alive()) {
00611       real_live_particle_count++;
00612     } else {
00613       real_dead_particle_count++;
00614     }
00615   }
00616 
00617   if (real_live_particle_count != _living_particles) {
00618 #ifdef PSSCVERBOSE
00619     cout << "manually counted live particle count (" << real_live_particle_count
00620          << ") != _living_particles (" << _living_particles << ")" << endl;
00621 #endif
00622     result++;
00623   }
00624 
00625   if (real_dead_particle_count != _free_particle_fifo.size()) {
00626 #ifdef PSSCVERBOSE
00627     cout << "manually counted dead particle count (" << real_dead_particle_count
00628          << ") != free particle fifo size (" << _free_particle_fifo.size() << ")" << endl;
00629 #endif
00630     result++;
00631   }
00632   ///////////////////////////////////////////////////////////////////
00633 
00634   ///////////////////////////////////////////////////////////////////
00635   // check the free particle pool
00636   for (i = 0; i < _free_particle_fifo.size(); i++) {
00637     int index = _free_particle_fifo[i];
00638 
00639     // check that we're in bounds
00640     if (index >= pool_size) {
00641 #ifdef PSSCVERBOSE
00642       cout << "index from free particle fifo (" << index
00643            << ") is too large; pool size is " << pool_size << endl;
00644 #endif
00645       result++;
00646       continue;
00647     }
00648 
00649     // check that the particle is indeed dead
00650     bp = (BaseParticle *) _physics_objects[index].p();
00651     if (true == bp->get_alive()) {
00652 #ifdef PSSCVERBOSE
00653       cout << "particle " << index << " in free fifo is not dead" << endl;
00654 #endif
00655       result++;
00656     }
00657   }
00658   ///////////////////////////////////////////////////////////////////
00659 
00660   ///////////////////////////////////////////////////////////////////
00661   // check the numbers of free particles, live particles, and total particles
00662   pvector< PT(SC_valuenamepair) > live_counts;
00663   pvector< PT(SC_valuenamepair) > dead_counts;
00664   pvector< PT(SC_valuenamepair) > total_counts;
00665 
00666   live_counts.push_back(new SC_valuenamepair(real_live_particle_count, "real_live_particle_count"));
00667 
00668   dead_counts.push_back(new SC_valuenamepair(real_dead_particle_count, "real_dead_particle_count"));
00669   dead_counts.push_back(new SC_valuenamepair(_free_particle_fifo.size(), "free particle fifo size"));
00670 
00671   total_counts.push_back(new SC_valuenamepair(_particle_pool_size, "_particle_pool_size"));
00672   total_counts.push_back(new SC_valuenamepair(_physics_objects.size(), "actual particle pool size"));
00673 
00674   result += check_free_live_total_particles(live_counts, dead_counts, total_counts);
00675   ///////////////////////////////////////////////////////////////////
00676 
00677   return result;
00678 }
00679 #endif

Generated on Fri Apr 18 00:08:11 2003 for Panda by doxygen1.3