Panda3D
particleSystem.cxx
1 // Filename: particleSystem.cxx
2 // Created by: charles (14Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include <stdlib.h>
16 
17 #include "luse.h"
18 #include "lmat_ops.h"
19 #include "clockObject.h"
20 #include "physicsManager.h"
21 #include "physicalNode.h"
22 #include "nearly_zero.h"
23 #include "transformState.h"
24 #include "nodePath.h"
25 
26 #include "config_particlesystem.h"
27 #include "particleSystem.h"
28 #include "particleSystemManager.h"
29 #include "pointParticleRenderer.h"
30 #include "pointParticleFactory.h"
31 #include "sphereSurfaceEmitter.h"
32 #include "pStatTimer.h"
33 
34 TypeHandle ParticleSystem::_type_handle;
35 
36 PStatCollector ParticleSystem::_update_collector("App:Particles:Update");
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function : ParticleSystem
40 // Access : Public
41 // Description : Default Constructor.
42 ////////////////////////////////////////////////////////////////////
44 ParticleSystem(int pool_size) :
45  Physical(pool_size, false)
46 {
47  _birth_rate = 0.5f;
48  _cur_birth_rate = _birth_rate;
49  _soft_birth_rate = HUGE_VAL;
50  _tics_since_birth = 0.0;
51  _litter_size = 1;
52  _litter_spread = 0;
53  _living_particles = 0;
54  _active_system_flag = true;
55  _local_velocity_flag = true;
56  _spawn_on_death_flag = false;
57  _system_grows_older_flag = false;
58  _system_lifespan = 0.0f;
59  _i_was_spawned_flag = false;
60  _particle_pool_size = 0;
61  _floor_z = -HUGE_VAL;
62 
63  // just in case someone tries to do something that requires the
64  // use of an emitter, renderer, or factory before they've actually
65  // assigned one. This is ok, because assigning them (set_renderer(),
66  // set_emitter(), etc...) forces them to set themselves up for the
67  // system, keeping the pool sizes consistent.
68 
69  _render_node_path = NodePath();
70  _render_parent = NodePath("ParticleSystem default render parent");
71 
72  set_emitter(new SphereSurfaceEmitter);
73 
74  set_renderer(new PointParticleRenderer);
75 
76  //set_factory(new PointParticleFactory);
77  _factory = new PointParticleFactory;
79 
80  set_pool_size(pool_size);
81 }
82 
83 ////////////////////////////////////////////////////////////////////
84 // Function : ParticleSystem
85 // Access : Public
86 // Description : Copy Constructor.
87 ////////////////////////////////////////////////////////////////////
90  Physical(copy),
91  _system_age(0.0f),
92  _template_system_flag(false)
93 {
94  _birth_rate = copy._birth_rate;
95  _cur_birth_rate = copy._cur_birth_rate;
96  _litter_size = copy._litter_size;
97  _litter_spread = copy._litter_spread;
98  _active_system_flag = copy._active_system_flag;
99  _local_velocity_flag = copy._local_velocity_flag;
100  _spawn_on_death_flag = copy._spawn_on_death_flag;
101  _i_was_spawned_flag = copy._i_was_spawned_flag;
102  _system_grows_older_flag = copy._system_grows_older_flag;
103  _emitter = copy._emitter;
104  _renderer = copy._renderer->make_copy();
105  _factory = copy._factory;
106 
107  _render_parent = copy._render_parent;
108  _render_node_path = _renderer->get_render_node_path();
109  _render_node_path.reparent_to(_render_parent);
110 
111  _tics_since_birth = 0.0;
112  _system_lifespan = copy._system_lifespan;
113  _living_particles = 0;
114 
115  set_pool_size(copy._particle_pool_size);
116 }
117 
118 ////////////////////////////////////////////////////////////////////
119 // Function : ~ParticleSystem
120 // Access : Public
121 // Description : You get the ankles and I'll get the wrists.
122 ////////////////////////////////////////////////////////////////////
125  set_pool_size(0);
126 
127  if (!_template_system_flag) {
128  _renderer.clear();
129  _render_node_path.remove_node();
130  }
131 }
132 
133 ////////////////////////////////////////////////////////////////////
134 // Function : birth_particle
135 // Access : Private
136 // Description : A new particle is born. This doesn't allocate,
137 // resets an element from the particle pool.
138 ////////////////////////////////////////////////////////////////////
139 bool ParticleSystem::
140 birth_particle() {
141  int pool_index;
142 
143  // make sure there's room for a new particle
144  if (_living_particles >= _particle_pool_size) {
145  #ifdef PSDEBUG
146  if (_living_particles > _particle_pool_size) {
147  cout << "_living_particles > _particle_pool_size" << endl;
148  }
149  #endif
150  return false;
151  }
152 
153  #ifdef PSDEBUG
154  if (0 == _free_particle_fifo.size()) {
155  cout << "Error: _free_particle_fifo is empty, but _living_particles < _particle_pool_size" << endl;
156  return false;
157  }
158  #endif
159 
160  pool_index = _free_particle_fifo.back();
161  _free_particle_fifo.pop_back();
162 
163  // get a handle on our particle.
164  BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p();
165 
166  // start filling out the variables.
167  _factory->populate_particle(bp);
168 
169  bp->set_alive(true);
170  bp->set_active(true);
171  bp->init();
172 
173  // get the location of the new particle.
174  LPoint3 new_pos, world_pos;
175  LVector3 new_vel;
176 
177  _emitter->generate(new_pos, new_vel);
178 
179  // go from birth space to render space
180  NodePath physical_np = get_physical_node_path();
181  NodePath render_np = _renderer->get_render_node_path();
182 
183  CPT(TransformState) transform = physical_np.get_transform(render_np);
184  const LMatrix4 &birth_to_render_xform = transform->get_mat();
185  world_pos = new_pos * birth_to_render_xform;
186 
187  // cout << "New particle at " << world_pos << endl;
188 
189  // possibly transform the initial velocity as well.
190  if (_local_velocity_flag == false)
191  new_vel = new_vel * birth_to_render_xform;
192 
193  bp->reset_position(world_pos/* + (NORMALIZED_RAND() * new_vel)*/);
194  bp->set_velocity(new_vel);
195 
196  ++_living_particles;
197 
198  // propogate information down to renderer
199  _renderer->birth_particle(pool_index);
200 
201  return true;
202 }
203 
204 ////////////////////////////////////////////////////////////////////
205 // Function : birth_litter
206 // Access : Private
207 // Description : spawns a new batch of particles
208 ////////////////////////////////////////////////////////////////////
209 void ParticleSystem::
210 birth_litter() {
211  int litter_size, i;
212 
213  litter_size = _litter_size;
214 
215  if (_litter_spread != 0)
216  litter_size += I_SPREAD(_litter_spread);
217 
218  for (i = 0; i < litter_size; ++i) {
219  if (birth_particle() == false)
220  return;
221  }
222 }
223 
224 ////////////////////////////////////////////////////////////////////
225 // Function : spawn_child_system
226 // Access : private
227 // Description : Creates a new particle system based on local
228 // template info and adds it to the ps and physics
229 // managers
230 ////////////////////////////////////////////////////////////////////
231 void ParticleSystem::
232 spawn_child_system(BaseParticle *bp) {
233  // first, make sure that the system exists in the graph via a
234  // physicalnode reference.
235  PhysicalNode *this_pn = get_physical_node();
236  if (!this_pn) {
237  physics_cat.error() << "ParticleSystem::spawn_child_system: "
238  << "Spawning system is not in the scene graph,"
239  << " aborting." << endl;
240  return;
241  }
242 
243  if (this_pn->get_num_parents() == 0) {
244  physics_cat.error() << "ParticleSystem::spawn_child_system: "
245  << "PhysicalNode this system is contained in "
246  << "has no parent, aborting." << endl;
247  return;
248  }
249 
250  if (_spawn_templates.size() == 0) {
251  physics_cat.error() << "ParticleSystem::spawn_child_system: "
252  << "no spawn templates present." << endl;
253  return;
254  }
255 
256  NodePath physical_np = get_physical_node_path();
257  NodePath parent_np = physical_np.get_parent();
258 
259  PandaNode *parent = parent_np.node();
260 
261  // handle the spawn templates
262  int new_ps_index = rand() % _spawn_templates.size();
263  ParticleSystem *ps_template = _spawn_templates[new_ps_index];
264 
265  // create a new particle system
266  PT(ParticleSystem) new_ps = new ParticleSystem(*ps_template);
267  new_ps->_i_was_spawned_flag = true;
268 
269  // first, set up the render node info.
270  new_ps->_render_parent = _spawn_render_node_path;
271  new_ps->_render_node_path = new_ps->_renderer->get_render_node_path();
272  new_ps->_render_node_path.reparent_to(new_ps->_render_parent);
273 
274  // now set up the new system's PhysicalNode.
275  PT(PhysicalNode) new_pn = new PhysicalNode("new_pn");
276  new_pn->add_physical(new_ps);
277 
278  // the transform on the new child has to represent the transform
279  // from the current system up to its parent, and then subsequently
280  // down to the new child.
281  parent->add_child(new_pn);
282 
283  CPT(TransformState) transform = physical_np.get_transform(parent_np);
284  const LMatrix4 &old_system_to_parent_xform = transform->get_mat();
285 
286  LMatrix4 child_space_xform = old_system_to_parent_xform *
287  bp->get_lcs();
288 
289  new_pn->set_transform(TransformState::make_mat(child_space_xform));
290 
291  // tack the new system onto the managers
292  _manager->attach_particlesystem(new_ps);
293  get_physics_manager()->attach_physical(new_ps);
294 }
295 
296 ////////////////////////////////////////////////////////////////////
297 // Function : kill_particle
298 // Access : Private
299 // Description : Kills a particle, returns its slot to the empty
300 // stack.
301 ////////////////////////////////////////////////////////////////////
302 void ParticleSystem::
303 kill_particle(int pool_index) {
304  // get a handle on our particle
305  BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p();
306 
307  // create a new system where this one died, maybe.
308  if (_spawn_on_death_flag == true) {
309  spawn_child_system(bp);
310  }
311 
312  // tell everyone that it's dead
313  bp->set_alive(false);
314  bp->set_active(false);
315  bp->die();
316 
317  _free_particle_fifo.push_back(pool_index);
318 
319  // tell renderer
320  _renderer->kill_particle(pool_index);
321 
322  _living_particles--;
323 }
324 
325 ////////////////////////////////////////////////////////////////////
326 // Function : resize_pool
327 // Access : Private
328 // Description : Resizes the particle pool
329 ////////////////////////////////////////////////////////////////////
330 #ifdef PSDEBUG
331 #define PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
332 #endif
333 void ParticleSystem::
334 resize_pool(int size) {
335  int i;
336  int delta = size - _particle_pool_size;
337  int po_delta = _particle_pool_size - _physics_objects.size();
338 
339  #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
340  cout << "resizing particle pool from " << _particle_pool_size
341  << " to " << size << endl;
342  #endif
343 
344  if (_factory.is_null()) {
345  particlesystem_cat.error() << "ParticleSystem::resize_pool"
346  << " called with null _factory." << endl;
347  return;
348  }
349 
350  if (_renderer.is_null()) {
351  particlesystem_cat.error() << "ParticleSystem::resize_pool"
352  << " called with null _renderer." << endl;
353  return;
354  }
355 
356  _particle_pool_size = size;
357 
358  // make sure the physics_objects array is OK
359  if (po_delta) {
360  if (po_delta > 0) {
361  for (i = 0; i < po_delta; i++)
362  {
363  // int free_index = _physics_objects.size();
364 
365  BaseParticle *new_particle = _factory->alloc_particle();
366  if (new_particle) {
367  _factory->populate_particle(new_particle);
368 
369  _physics_objects.push_back(new_particle);
370  } else {
371  #ifdef PSDEBUG
372  cout << "Error allocating new particle" << endl;
373  _particle_pool_size--;
374  #endif
375  }
376  }
377  } else {
378  #ifdef PSDEBUG
379  cout << "physics_object array is too large??" << endl;
380  _particle_pool_size--;
381  #endif
382  po_delta = -po_delta;
383  for (i = 0; i < po_delta; i++) {
384  int delete_index = _physics_objects.size()-1;
385  BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
386  if (bp->get_alive()) {
387  kill_particle(delete_index);
388  _free_particle_fifo.pop_back();
389  } else {
391  i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
392  if (i != _free_particle_fifo.end()) {
393  _free_particle_fifo.erase(i);
394  }
395  }
396  _physics_objects.pop_back();
397  }
398  }
399  }
400 
401  // disregard no change
402  if (delta == 0)
403  return;
404 
405  // update the pool
406  if (delta > 0) {
407  // add elements
408  for (i = 0; i < delta; i++)
409  {
410  int free_index = _physics_objects.size();
411 
412  BaseParticle *new_particle = _factory->alloc_particle();
413  if (new_particle) {
414  _factory->populate_particle(new_particle);
415 
416  _physics_objects.push_back(new_particle);
417  _free_particle_fifo.push_back(free_index);
418  } else {
419  #ifdef PSDEBUG
420  cout << "Error allocating new particle" << endl;
421  _particle_pool_size--;
422  #endif
423  }
424  }
425  } else {
426  // subtract elements
427  delta = -delta;
428  for (i = 0; i < delta; i++) {
429  int delete_index = _physics_objects.size()-1;
430  BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
431 
432  if (bp->get_alive()) {
433  #ifdef PSDEBUG
434  cout << "WAS ALIVE" << endl;
435  #endif
436  kill_particle(delete_index);
437  _free_particle_fifo.pop_back();
438  } else {
439  #ifdef PSDEBUG
440  cout << "WAS NOT ALIVE" << endl;
441  #endif
443  i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
444  if (i != _free_particle_fifo.end()) {
445  _free_particle_fifo.erase(i);
446  }
447  #ifdef PSDEBUG
448  else {
449  cout << "particle not found in free FIFO!!!!!!!!" << endl;
450  }
451  #endif
452  }
453 
454  _physics_objects.pop_back();
455  }
456  }
457 
458  _renderer->resize_pool(_particle_pool_size);
459 
460  #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
461  cout << "particle pool resized" << endl;
462  #endif
463 }
464 
465 //////////////////////////////////////////////////////////////////////
466 // Function : update
467 // Access : Public
468 // Description : Updates the particle system. Call once per frame.
469 //////////////////////////////////////////////////////////////////////
470 #ifdef PSDEBUG
471 //#define PARTICLE_SYSTEM_UPDATE_SENTRIES
472 #endif
473 void ParticleSystem::
474 update(PN_stdfloat dt) {
475  PStatTimer t1(_update_collector);
476 
477  int ttl_updates_left = _living_particles;
478  int current_index = 0, index_counter = 0;
479  BaseParticle *bp;
480  PN_stdfloat age;
481 
482  #ifdef PSSANITYCHECK
483  // check up on things
484  if (sanity_check()) return;
485  #endif
486 
487  #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
488  cout << "UPDATE: pool size: " << _particle_pool_size
489  << ", live particles: " << _living_particles << endl;
490  #endif
491 
492  // run through the particle array
493  while (ttl_updates_left) {
494  current_index = index_counter;
495  index_counter++;
496 
497  #ifdef PSDEBUG
498  if (current_index >= _particle_pool_size) {
499  cout << "ERROR: _living_particles is out of sync (too large)" << endl;
500  cout << "pool size: " << _particle_pool_size
501  << ", live particles: " << _living_particles
502  << ", updates left: " << ttl_updates_left << endl;
503  break;
504  }
505  #endif
506 
507  // get the current particle.
508  bp = (BaseParticle *) _physics_objects[current_index].p();
509 
510  #ifdef PSDEBUG
511  if (!bp) {
512  cout << "NULL ptr at index " << current_index << endl;
513  continue;
514  }
515  #endif
516 
517  if (bp->get_alive() == false)
518  continue;
519 
520  age = bp->get_age() + dt;
521  bp->set_age(age);
522 
523  //cerr<<"bp->get_position().get_z() returning "<<bp->get_position().get_z()<<endl;
524  if (age >= bp->get_lifespan()) {
525  kill_particle(current_index);
526  } else if (get_floor_z() != -HUGE_VAL
527  && bp->get_position().get_z() <= get_floor_z()) {
528  // ...the particle is going under the floor.
529  // Maybe tell the particle to bounce: bp->bounce()?
530  kill_particle(current_index);
531  } else {
532  bp->update();
533  }
534 
535  // break out early if we're lucky
536  ttl_updates_left--;
537  }
538 
539 
540  // generate new particles if necessary.
541  _tics_since_birth += dt;
542 
543  while (_tics_since_birth >= _cur_birth_rate) {
544  birth_litter();
545  _tics_since_birth -= _cur_birth_rate;
546  }
547 
548  #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
549  cout << "particle update complete" << endl;
550  #endif
551 
552 }
553 
554 #ifdef PSSANITYCHECK
555 //////////////////////////////////////////////////////////////////////
556 // Function : sanity_check
557 // Access : Private
558 // Description : Checks consistency of live particle count, free
559 // particle list, etc. returns 0 if everything is normal
560 //////////////////////////////////////////////////////////////////////
561 #ifndef NDEBUG
562 #define PSSCVERBOSE
563 #endif
564 
565 class SC_valuenamepair : public ReferenceCount {
566 public:
567  int value;
568  char *name;
569  SC_valuenamepair(int v, char *s) : value(v), name(s) {}
570 };
571 
572 // returns 0 if OK, # of errors if not OK
573 static int check_free_live_total_particles(pvector< PT(SC_valuenamepair) > live_counts,
574  pvector< PT(SC_valuenamepair) > dead_counts, pvector< PT(SC_valuenamepair) > total_counts,
575  int print_all = 0) {
576 
577  int val = 0;
578  int l, d, t;
579 
580  for(l = 0; l < live_counts.size(); l++) {
581  for(d = 0; d < dead_counts.size(); d++) {
582  for(t = 0; t < total_counts.size(); t++) {
583  int live = live_counts[l]->value;
584  int dead = dead_counts[d]->value;
585  int total = total_counts[t]->value;
586  if ((live + dead) != total) {
587  #ifdef PSSCVERBOSE
588  cout << "free/live/total count: "
589  << live_counts[l]->name << " (" << live << ") + "
590  << dead_counts[d]->name << " (" << dead << ") = "
591  << live + dead << ", != "
592  << total_counts[t]->name << " (" << total << ")"
593  << endl;
594  #endif
595  val++;
596  }
597  }
598  }
599  }
600 
601  return val;
602 }
603 
604 int ParticleSystem::
605 sanity_check() {
606  int result = 0;
607  int i;
608  BaseParticle *bp;
609  int pool_size;
610 
611  ///////////////////////////////////////////////////////////////////
612  // check pool size
613  if (_particle_pool_size != _physics_objects.size()) {
614  #ifdef PSSCVERBOSE
615  cout << "_particle_pool_size (" << _particle_pool_size
616  << ") != particle array size (" << _physics_objects.size() << ")" << endl;
617  #endif
618  result++;
619  }
620  pool_size = min(_particle_pool_size, _physics_objects.size());
621  ///////////////////////////////////////////////////////////////////
622 
623  ///////////////////////////////////////////////////////////////////
624  // find out how many particles are REALLY alive and dead
625  int real_live_particle_count = 0;
626  int real_dead_particle_count = 0;
627 
628  for (i = 0; i < _physics_objects.size(); i++) {
629  bp = (BaseParticle *) _physics_objects[i].p();
630  if (true == bp->get_alive()) {
631  real_live_particle_count++;
632  } else {
633  real_dead_particle_count++;
634  }
635  }
636 
637  if (real_live_particle_count != _living_particles) {
638  #ifdef PSSCVERBOSE
639  cout << "manually counted live particle count (" << real_live_particle_count
640  << ") != _living_particles (" << _living_particles << ")" << endl;
641  #endif
642  result++;
643  }
644 
645  if (real_dead_particle_count != _free_particle_fifo.size()) {
646  #ifdef PSSCVERBOSE
647  cout << "manually counted dead particle count (" << real_dead_particle_count
648  << ") != free particle fifo size (" << _free_particle_fifo.size() << ")" << endl;
649  #endif
650  result++;
651  }
652  ///////////////////////////////////////////////////////////////////
653 
654  ///////////////////////////////////////////////////////////////////
655  // check the free particle pool
656  for (i = 0; i < _free_particle_fifo.size(); i++) {
657  int index = _free_particle_fifo[i];
658 
659  // check that we're in bounds
660  if (index >= pool_size) {
661  #ifdef PSSCVERBOSE
662  cout << "index from free particle fifo (" << index
663  << ") is too large; pool size is " << pool_size << endl;
664  #endif
665  result++;
666  continue;
667  }
668 
669  // check that the particle is indeed dead
670  bp = (BaseParticle *) _physics_objects[index].p();
671  if (true == bp->get_alive()) {
672  #ifdef PSSCVERBOSE
673  cout << "particle " << index << " in free fifo is not dead" << endl;
674  #endif
675  result++;
676  }
677  }
678  ///////////////////////////////////////////////////////////////////
679 
680  ///////////////////////////////////////////////////////////////////
681  // check the numbers of free particles, live particles, and total particles
684  pvector< PT(SC_valuenamepair) > total_counts;
685 
686  live_counts.push_back(new SC_valuenamepair(real_live_particle_count, "real_live_particle_count"));
687 
688  dead_counts.push_back(new SC_valuenamepair(real_dead_particle_count, "real_dead_particle_count"));
689  dead_counts.push_back(new SC_valuenamepair(_free_particle_fifo.size(), "free particle fifo size"));
690 
691  total_counts.push_back(new SC_valuenamepair(_particle_pool_size, "_particle_pool_size"));
692  total_counts.push_back(new SC_valuenamepair(_physics_objects.size(), "actual particle pool size"));
693 
694  result += check_free_live_total_particles(live_counts, dead_counts, total_counts);
695  ///////////////////////////////////////////////////////////////////
696 
697  return result;
698 }
699 #endif
700 
701 ////////////////////////////////////////////////////////////////////
702 // Function : output
703 // Access : Public
704 // Description : Write a string representation of this instance to
705 // <out>.
706 ////////////////////////////////////////////////////////////////////
707 void ParticleSystem::
708 output(ostream &out) const {
709  #ifndef NDEBUG //[
710  out<<"ParticleSystem";
711  #endif //] NDEBUG
712 }
713 
714 ////////////////////////////////////////////////////////////////////
715 // Function : write_free_particle_fifo
716 // Access : Public
717 // Description : Write a string representation of this instance to
718 // <out>.
719 ////////////////////////////////////////////////////////////////////
720 void ParticleSystem::
721 write_free_particle_fifo(ostream &out, int indent) const {
722  #ifndef NDEBUG //[
723  out.width(indent);
724  out<<""<<"_free_particle_fifo ("<<_free_particle_fifo.size()<<" forces)\n";
725  for (pdeque< int >::const_iterator i=_free_particle_fifo.begin();
726  i != _free_particle_fifo.end();
727  ++i) {
728  out.width(indent+2); out<<""; out<<(*i)<<"\n";
729  }
730  #endif //] NDEBUG
731 }
732 
733 ////////////////////////////////////////////////////////////////////
734 // Function : write_spawn_templates
735 // Access : Public
736 // Description : Write a string representation of this instance to
737 // <out>.
738 ////////////////////////////////////////////////////////////////////
739 void ParticleSystem::
740 write_spawn_templates(ostream &out, int indent) const {
741  #ifndef NDEBUG //[
742  out.width(indent);
743  out<<""<<"_spawn_templates ("<<_spawn_templates.size()<<" templates)\n";
744  for (pvector< PT(ParticleSystem) >::const_iterator i=_spawn_templates.begin();
745  i != _spawn_templates.end();
746  ++i) {
747  (*i)->write(out, indent+2);
748  }
749  #endif //] NDEBUG
750 }
751 
752 ////////////////////////////////////////////////////////////////////
753 // Function : write
754 // Access : Public
755 // Description : Write a string representation of this instance to
756 // <out>.
757 ////////////////////////////////////////////////////////////////////
758 void ParticleSystem::
759 write(ostream &out, int indent) const {
760  #ifndef NDEBUG //[
761  out.width(indent); out<<""; out<<"ParticleSystem:\n";
762  out.width(indent+2); out<<""; out<<"_particle_pool_size "<<_particle_pool_size<<"\n";
763  out.width(indent+2); out<<""; out<<"_living_particles "<<_living_particles<<"\n";
764  out.width(indent+2); out<<""; out<<"_tics_since_birth "<<_tics_since_birth<<"\n";
765  out.width(indent+2); out<<""; out<<"_litter_size "<<_litter_size<<"\n";
766  out.width(indent+2); out<<""; out<<"_litter_spread "<<_litter_spread<<"\n";
767  out.width(indent+2); out<<""; out<<"_system_age "<<_system_age<<"\n";
768  out.width(indent+2); out<<""; out<<"_system_lifespan "<<_system_lifespan<<"\n";
769  out.width(indent+2); out<<""; out<<"_factory "<<_factory<<"\n";
770  out.width(indent+2); out<<""; out<<"_emitter "<<_emitter<<"\n";
771  out.width(indent+2); out<<""; out<<"_renderer "<<_renderer<<"\n";
772  out.width(indent+2); out<<""; out<<"_manager "<<_manager<<"\n";
773  out.width(indent+2); out<<""; out<<"_template_system_flag "<<_template_system_flag<<"\n";
774  out.width(indent+2); out<<""; out<<"_render_parent "<<_render_parent<<"\n";
775  out.width(indent+2); out<<""; out<<"_render_node "<<_render_node_path<<"\n";
776  out.width(indent+2); out<<""; out<<"_active_system_flag "<<_active_system_flag<<"\n";
777  out.width(indent+2); out<<""; out<<"_local_velocity_flag "<<_local_velocity_flag<<"\n";
778  out.width(indent+2); out<<""; out<<"_system_grows_older_flag "<<_system_grows_older_flag<<"\n";
779  out.width(indent+2); out<<""; out<<"_spawn_on_death_flag "<<_spawn_on_death_flag<<"\n";
780  out.width(indent+2); out<<""; out<<"_spawn_render_node "<<_spawn_render_node_path<<"\n";
781  out.width(indent+2); out<<""; out<<"_i_was_spawned_flag "<<_i_was_spawned_flag<<"\n";
782  write_free_particle_fifo(out, indent+2);
783  write_spawn_templates(out, indent+2);
784  Physical::write(out, indent+2);
785  #endif //] NDEBUG
786 }
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
void reset_position(const LPoint3 &pos)
use this to place an object in a completely new position, that has nothing to do with its last positi...
Definition: physicsObject.I:57
virtual void write_spawn_templates(ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
Describes a curved space in which particles are generated.
~ParticleSystem()
You get the ankles and I&#39;ll get the wrists.
void set_velocity(const LVector3 &vel)
Vector velocity assignment.
Definition: physicsObject.I:93
void attach_physical(Physical *p)
Registers a Physical class with the manager.
Creates point particles to user specs.
void clear_physics_objects()
Erases the object list.
Definition: physical.I:45
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is our own Panda specialization on the default STL deque.
Definition: pdeque.h:38
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
Graph node that encapsulated a series of physical objects.
Definition: physicalNode.h:31
void set_active(bool flag)
Process Flag assignment.
A lightweight class that represents a single element that may be timed and/or counted via stats...
Contains and manages a particle system.
void reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node of the NodePath from its current parent and attaches it to the referenced...
Definition: nodePath.cxx:523
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
virtual void write(ostream &out=cout, unsigned int indent=0) const
Write a string representation of this instance to <out>.
Definition: physical.cxx:213
Simple point/point particle renderer.
virtual void output(ostream &out) const
Write a string representation of this instance to <out>.
Defines a set of physically modeled attributes.
Definition: physical.h:40
virtual void write(ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
ParticleSystem(int pool_size=0)
Default Constructor.
int get_num_parents(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of parent nodes this node has.
Definition: pandaNode.I:26
A base class for all things that want to be reference-counted.
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
An individual, physically-modelable particle abstract base class.
Definition: baseParticle.h:26
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
virtual void write_free_particle_fifo(ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
LPoint3 get_position() const
Position Query.
NodePath get_parent(Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath to the parent of the referenced node: that is, this NodePath, shortened by one node.
Definition: nodePath.I:460
void update(PN_stdfloat dt)
Updates the particle system.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:925
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
virtual LMatrix4 get_lcs() const
returns a transform matrix to this object&#39;s local coordinate system.