Panda3D
 All Classes Functions Variables Enumerations
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  NodePath physical_np = get_physical_node_path();
251  NodePath parent_np = physical_np.get_parent();
252 
253  PandaNode *parent = parent_np.node();
254 
255  // handle the spawn templates
256  int new_ps_index = rand() % _spawn_templates.size();
257  ParticleSystem *ps_template = _spawn_templates[new_ps_index];
258 
259  // create a new particle system
260  PT(ParticleSystem) new_ps = new ParticleSystem(*ps_template);
261  new_ps->_i_was_spawned_flag = true;
262 
263  // first, set up the render node info.
264  new_ps->_render_parent = _spawn_render_node_path;
265  new_ps->_render_node_path = new_ps->_renderer->get_render_node_path();
266  new_ps->_render_node_path.reparent_to(new_ps->_render_parent);
267 
268  // now set up the new system's PhysicalNode.
269  PT(PhysicalNode) new_pn = new PhysicalNode("new_pn");
270  new_pn->add_physical(new_ps);
271 
272  // the transform on the new child has to represent the transform
273  // from the current system up to its parent, and then subsequently
274  // down to the new child.
275  parent->add_child(new_pn);
276 
277  CPT(TransformState) transform = physical_np.get_transform(parent_np);
278  const LMatrix4 &old_system_to_parent_xform = transform->get_mat();
279 
280  LMatrix4 child_space_xform = old_system_to_parent_xform *
281  bp->get_lcs();
282 
283  new_pn->set_transform(TransformState::make_mat(child_space_xform));
284 
285  // tack the new system onto the managers
286  _manager->attach_particlesystem(new_ps);
287  get_physics_manager()->attach_physical(new_ps);
288 }
289 
290 ////////////////////////////////////////////////////////////////////
291 // Function : kill_particle
292 // Access : Private
293 // Description : Kills a particle, returns its slot to the empty
294 // stack.
295 ////////////////////////////////////////////////////////////////////
296 void ParticleSystem::
297 kill_particle(int pool_index) {
298  // get a handle on our particle
299  BaseParticle *bp = (BaseParticle *) _physics_objects[pool_index].p();
300 
301  // create a new system where this one died, maybe.
302  if (_spawn_on_death_flag == true) {
303  spawn_child_system(bp);
304  }
305 
306  // tell everyone that it's dead
307  bp->set_alive(false);
308  bp->set_active(false);
309  bp->die();
310 
311  _free_particle_fifo.push_back(pool_index);
312 
313  // tell renderer
314  _renderer->kill_particle(pool_index);
315 
316  _living_particles--;
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function : resize_pool
321 // Access : Private
322 // Description : Resizes the particle pool
323 ////////////////////////////////////////////////////////////////////
324 #ifdef PSDEBUG
325 #define PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
326 #endif
327 void ParticleSystem::
328 resize_pool(int size) {
329  int i;
330  int delta = size - _particle_pool_size;
331  int po_delta = _particle_pool_size - _physics_objects.size();
332 
333  #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
334  cout << "resizing particle pool from " << _particle_pool_size
335  << " to " << size << endl;
336  #endif
337 
338  if (_factory.is_null()) {
339  particlesystem_cat.error() << "ParticleSystem::resize_pool"
340  << " called with null _factory." << endl;
341  return;
342  }
343 
344  if (_renderer.is_null()) {
345  particlesystem_cat.error() << "ParticleSystem::resize_pool"
346  << " called with null _renderer." << endl;
347  return;
348  }
349 
350  _particle_pool_size = size;
351 
352  // make sure the physics_objects array is OK
353  if (po_delta) {
354  if (po_delta > 0) {
355  for (i = 0; i < po_delta; i++)
356  {
357  // int free_index = _physics_objects.size();
358 
359  BaseParticle *new_particle = _factory->alloc_particle();
360  if (new_particle) {
361  _factory->populate_particle(new_particle);
362 
363  _physics_objects.push_back(new_particle);
364  } else {
365  #ifdef PSDEBUG
366  cout << "Error allocating new particle" << endl;
367  _particle_pool_size--;
368  #endif
369  }
370  }
371  } else {
372  #ifdef PSDEBUG
373  cout << "physics_object array is too large??" << endl;
374  _particle_pool_size--;
375  #endif
376  po_delta = -po_delta;
377  for (i = 0; i < po_delta; i++) {
378  int delete_index = _physics_objects.size()-1;
379  BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
380  if (bp->get_alive()) {
381  kill_particle(delete_index);
382  _free_particle_fifo.pop_back();
383  } else {
385  i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
386  if (i != _free_particle_fifo.end()) {
387  _free_particle_fifo.erase(i);
388  }
389  }
390  _physics_objects.pop_back();
391  }
392  }
393  }
394 
395  // disregard no change
396  if (delta == 0)
397  return;
398 
399  // update the pool
400  if (delta > 0) {
401  // add elements
402  for (i = 0; i < delta; i++)
403  {
404  int free_index = _physics_objects.size();
405 
406  BaseParticle *new_particle = _factory->alloc_particle();
407  if (new_particle) {
408  _factory->populate_particle(new_particle);
409 
410  _physics_objects.push_back(new_particle);
411  _free_particle_fifo.push_back(free_index);
412  } else {
413  #ifdef PSDEBUG
414  cout << "Error allocating new particle" << endl;
415  _particle_pool_size--;
416  #endif
417  }
418  }
419  } else {
420  // subtract elements
421  delta = -delta;
422  for (i = 0; i < delta; i++) {
423  int delete_index = _physics_objects.size()-1;
424  BaseParticle *bp = (BaseParticle *) _physics_objects[delete_index].p();
425 
426  if (bp->get_alive()) {
427  #ifdef PSDEBUG
428  cout << "WAS ALIVE" << endl;
429  #endif
430  kill_particle(delete_index);
431  _free_particle_fifo.pop_back();
432  } else {
433  #ifdef PSDEBUG
434  cout << "WAS NOT ALIVE" << endl;
435  #endif
437  i = find(_free_particle_fifo.begin(), _free_particle_fifo.end(), delete_index);
438  if (i != _free_particle_fifo.end()) {
439  _free_particle_fifo.erase(i);
440  }
441  #ifdef PSDEBUG
442  else {
443  cout << "particle not found in free FIFO!!!!!!!!" << endl;
444  }
445  #endif
446  }
447 
448  _physics_objects.pop_back();
449  }
450  }
451 
452  _renderer->resize_pool(_particle_pool_size);
453 
454  #ifdef PARTICLE_SYSTEM_RESIZE_POOL_SENTRIES
455  cout << "particle pool resized" << endl;
456  #endif
457 }
458 
459 //////////////////////////////////////////////////////////////////////
460 // Function : update
461 // Access : Public
462 // Description : Updates the particle system. Call once per frame.
463 //////////////////////////////////////////////////////////////////////
464 #ifdef PSDEBUG
465 //#define PARTICLE_SYSTEM_UPDATE_SENTRIES
466 #endif
467 void ParticleSystem::
468 update(PN_stdfloat dt) {
469  PStatTimer t1(_update_collector);
470 
471  int ttl_updates_left = _living_particles;
472  int current_index = 0, index_counter = 0;
473  BaseParticle *bp;
474  PN_stdfloat age;
475 
476  #ifdef PSSANITYCHECK
477  // check up on things
478  if (sanity_check()) return;
479  #endif
480 
481  #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
482  cout << "UPDATE: pool size: " << _particle_pool_size
483  << ", live particles: " << _living_particles << endl;
484  #endif
485 
486  // run through the particle array
487  while (ttl_updates_left) {
488  current_index = index_counter;
489  index_counter++;
490 
491  #ifdef PSDEBUG
492  if (current_index >= _particle_pool_size) {
493  cout << "ERROR: _living_particles is out of sync (too large)" << endl;
494  cout << "pool size: " << _particle_pool_size
495  << ", live particles: " << _living_particles
496  << ", updates left: " << ttl_updates_left << endl;
497  break;
498  }
499  #endif
500 
501  // get the current particle.
502  bp = (BaseParticle *) _physics_objects[current_index].p();
503 
504  #ifdef PSDEBUG
505  if (!bp) {
506  cout << "NULL ptr at index " << current_index << endl;
507  continue;
508  }
509  #endif
510 
511  if (bp->get_alive() == false)
512  continue;
513 
514  age = bp->get_age() + dt;
515  bp->set_age(age);
516 
517  //cerr<<"bp->get_position().get_z() returning "<<bp->get_position().get_z()<<endl;
518  if (age >= bp->get_lifespan()) {
519  kill_particle(current_index);
520  } else if (get_floor_z() != -HUGE_VAL
521  && bp->get_position().get_z() <= get_floor_z()) {
522  // ...the particle is going under the floor.
523  // Maybe tell the particle to bounce: bp->bounce()?
524  kill_particle(current_index);
525  } else {
526  bp->update();
527  }
528 
529  // break out early if we're lucky
530  ttl_updates_left--;
531  }
532 
533 
534  // generate new particles if necessary.
535  _tics_since_birth += dt;
536 
537  while (_tics_since_birth >= _cur_birth_rate) {
538  birth_litter();
539  _tics_since_birth -= _cur_birth_rate;
540  }
541 
542  #ifdef PARTICLE_SYSTEM_UPDATE_SENTRIES
543  cout << "particle update complete" << endl;
544  #endif
545 
546 }
547 
548 #ifdef PSSANITYCHECK
549 //////////////////////////////////////////////////////////////////////
550 // Function : sanity_check
551 // Access : Private
552 // Description : Checks consistency of live particle count, free
553 // particle list, etc. returns 0 if everything is normal
554 //////////////////////////////////////////////////////////////////////
555 #ifndef NDEBUG
556 #define PSSCVERBOSE
557 #endif
558 
559 class SC_valuenamepair : public ReferenceCount {
560 public:
561  int value;
562  char *name;
563  SC_valuenamepair(int v, char *s) : value(v), name(s) {}
564 };
565 
566 // returns 0 if OK, # of errors if not OK
567 static int check_free_live_total_particles(pvector< PT(SC_valuenamepair) > live_counts,
568  pvector< PT(SC_valuenamepair) > dead_counts, pvector< PT(SC_valuenamepair) > total_counts,
569  int print_all = 0) {
570 
571  int val = 0;
572  int l, d, t;
573 
574  for(l = 0; l < live_counts.size(); l++) {
575  for(d = 0; d < dead_counts.size(); d++) {
576  for(t = 0; t < total_counts.size(); t++) {
577  int live = live_counts[l]->value;
578  int dead = dead_counts[d]->value;
579  int total = total_counts[t]->value;
580  if ((live + dead) != total) {
581  #ifdef PSSCVERBOSE
582  cout << "free/live/total count: "
583  << live_counts[l]->name << " (" << live << ") + "
584  << dead_counts[d]->name << " (" << dead << ") = "
585  << live + dead << ", != "
586  << total_counts[t]->name << " (" << total << ")"
587  << endl;
588  #endif
589  val++;
590  }
591  }
592  }
593  }
594 
595  return val;
596 }
597 
598 int ParticleSystem::
599 sanity_check() {
600  int result = 0;
601  int i;
602  BaseParticle *bp;
603  int pool_size;
604 
605  ///////////////////////////////////////////////////////////////////
606  // check pool size
607  if (_particle_pool_size != _physics_objects.size()) {
608  #ifdef PSSCVERBOSE
609  cout << "_particle_pool_size (" << _particle_pool_size
610  << ") != particle array size (" << _physics_objects.size() << ")" << endl;
611  #endif
612  result++;
613  }
614  pool_size = min(_particle_pool_size, _physics_objects.size());
615  ///////////////////////////////////////////////////////////////////
616 
617  ///////////////////////////////////////////////////////////////////
618  // find out how many particles are REALLY alive and dead
619  int real_live_particle_count = 0;
620  int real_dead_particle_count = 0;
621 
622  for (i = 0; i < _physics_objects.size(); i++) {
623  bp = (BaseParticle *) _physics_objects[i].p();
624  if (true == bp->get_alive()) {
625  real_live_particle_count++;
626  } else {
627  real_dead_particle_count++;
628  }
629  }
630 
631  if (real_live_particle_count != _living_particles) {
632  #ifdef PSSCVERBOSE
633  cout << "manually counted live particle count (" << real_live_particle_count
634  << ") != _living_particles (" << _living_particles << ")" << endl;
635  #endif
636  result++;
637  }
638 
639  if (real_dead_particle_count != _free_particle_fifo.size()) {
640  #ifdef PSSCVERBOSE
641  cout << "manually counted dead particle count (" << real_dead_particle_count
642  << ") != free particle fifo size (" << _free_particle_fifo.size() << ")" << endl;
643  #endif
644  result++;
645  }
646  ///////////////////////////////////////////////////////////////////
647 
648  ///////////////////////////////////////////////////////////////////
649  // check the free particle pool
650  for (i = 0; i < _free_particle_fifo.size(); i++) {
651  int index = _free_particle_fifo[i];
652 
653  // check that we're in bounds
654  if (index >= pool_size) {
655  #ifdef PSSCVERBOSE
656  cout << "index from free particle fifo (" << index
657  << ") is too large; pool size is " << pool_size << endl;
658  #endif
659  result++;
660  continue;
661  }
662 
663  // check that the particle is indeed dead
664  bp = (BaseParticle *) _physics_objects[index].p();
665  if (true == bp->get_alive()) {
666  #ifdef PSSCVERBOSE
667  cout << "particle " << index << " in free fifo is not dead" << endl;
668  #endif
669  result++;
670  }
671  }
672  ///////////////////////////////////////////////////////////////////
673 
674  ///////////////////////////////////////////////////////////////////
675  // check the numbers of free particles, live particles, and total particles
678  pvector< PT(SC_valuenamepair) > total_counts;
679 
680  live_counts.push_back(new SC_valuenamepair(real_live_particle_count, "real_live_particle_count"));
681 
682  dead_counts.push_back(new SC_valuenamepair(real_dead_particle_count, "real_dead_particle_count"));
683  dead_counts.push_back(new SC_valuenamepair(_free_particle_fifo.size(), "free particle fifo size"));
684 
685  total_counts.push_back(new SC_valuenamepair(_particle_pool_size, "_particle_pool_size"));
686  total_counts.push_back(new SC_valuenamepair(_physics_objects.size(), "actual particle pool size"));
687 
688  result += check_free_live_total_particles(live_counts, dead_counts, total_counts);
689  ///////////////////////////////////////////////////////////////////
690 
691  return result;
692 }
693 #endif
694 
695 ////////////////////////////////////////////////////////////////////
696 // Function : output
697 // Access : Public
698 // Description : Write a string representation of this instance to
699 // <out>.
700 ////////////////////////////////////////////////////////////////////
701 void ParticleSystem::
702 output(ostream &out) const {
703  #ifndef NDEBUG //[
704  out<<"ParticleSystem";
705  #endif //] NDEBUG
706 }
707 
708 ////////////////////////////////////////////////////////////////////
709 // Function : write_free_particle_fifo
710 // Access : Public
711 // Description : Write a string representation of this instance to
712 // <out>.
713 ////////////////////////////////////////////////////////////////////
714 void ParticleSystem::
715 write_free_particle_fifo(ostream &out, int indent) const {
716  #ifndef NDEBUG //[
717  out.width(indent);
718  out<<""<<"_free_particle_fifo ("<<_free_particle_fifo.size()<<" forces)\n";
719  for (pdeque< int >::const_iterator i=_free_particle_fifo.begin();
720  i != _free_particle_fifo.end();
721  ++i) {
722  out.width(indent+2); out<<""; out<<(*i)<<"\n";
723  }
724  #endif //] NDEBUG
725 }
726 
727 ////////////////////////////////////////////////////////////////////
728 // Function : write_spawn_templates
729 // Access : Public
730 // Description : Write a string representation of this instance to
731 // <out>.
732 ////////////////////////////////////////////////////////////////////
733 void ParticleSystem::
734 write_spawn_templates(ostream &out, int indent) const {
735  #ifndef NDEBUG //[
736  out.width(indent);
737  out<<""<<"_spawn_templates ("<<_spawn_templates.size()<<" templates)\n";
738  for (pvector< PT(ParticleSystem) >::const_iterator i=_spawn_templates.begin();
739  i != _spawn_templates.end();
740  ++i) {
741  (*i)->write(out, indent+2);
742  }
743  #endif //] NDEBUG
744 }
745 
746 ////////////////////////////////////////////////////////////////////
747 // Function : write
748 // Access : Public
749 // Description : Write a string representation of this instance to
750 // <out>.
751 ////////////////////////////////////////////////////////////////////
752 void ParticleSystem::
753 write(ostream &out, int indent) const {
754  #ifndef NDEBUG //[
755  out.width(indent); out<<""; out<<"ParticleSystem:\n";
756  out.width(indent+2); out<<""; out<<"_particle_pool_size "<<_particle_pool_size<<"\n";
757  out.width(indent+2); out<<""; out<<"_living_particles "<<_living_particles<<"\n";
758  out.width(indent+2); out<<""; out<<"_tics_since_birth "<<_tics_since_birth<<"\n";
759  out.width(indent+2); out<<""; out<<"_litter_size "<<_litter_size<<"\n";
760  out.width(indent+2); out<<""; out<<"_litter_spread "<<_litter_spread<<"\n";
761  out.width(indent+2); out<<""; out<<"_system_age "<<_system_age<<"\n";
762  out.width(indent+2); out<<""; out<<"_system_lifespan "<<_system_lifespan<<"\n";
763  out.width(indent+2); out<<""; out<<"_factory "<<_factory<<"\n";
764  out.width(indent+2); out<<""; out<<"_emitter "<<_emitter<<"\n";
765  out.width(indent+2); out<<""; out<<"_renderer "<<_renderer<<"\n";
766  out.width(indent+2); out<<""; out<<"_manager "<<_manager<<"\n";
767  out.width(indent+2); out<<""; out<<"_template_system_flag "<<_template_system_flag<<"\n";
768  out.width(indent+2); out<<""; out<<"_render_parent "<<_render_parent<<"\n";
769  out.width(indent+2); out<<""; out<<"_render_node "<<_render_node_path<<"\n";
770  out.width(indent+2); out<<""; out<<"_active_system_flag "<<_active_system_flag<<"\n";
771  out.width(indent+2); out<<""; out<<"_local_velocity_flag "<<_local_velocity_flag<<"\n";
772  out.width(indent+2); out<<""; out<<"_system_grows_older_flag "<<_system_grows_older_flag<<"\n";
773  out.width(indent+2); out<<""; out<<"_spawn_on_death_flag "<<_spawn_on_death_flag<<"\n";
774  out.width(indent+2); out<<""; out<<"_spawn_render_node "<<_spawn_render_node_path<<"\n";
775  out.width(indent+2); out<<""; out<<"_i_was_spawned_flag "<<_i_was_spawned_flag<<"\n";
776  write_free_particle_fifo(out, indent+2);
777  write_spawn_templates(out, indent+2);
778  Physical::write(out, indent+2);
779  #endif //] NDEBUG
780 }
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
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
Describes a curved space in which particles are generated.
~ParticleSystem()
You get the ankles and I&#39;ll get the wrists.
virtual void output(ostream &out) const
Write a string representation of this instance to &lt;out&gt;.
Creates point particles to user specs.
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
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, int indent=0) const
Write a string representation of this instance to &lt;out&gt;.
Simple point/point particle renderer.
Defines a set of physically modeled attributes.
Definition: physical.h:40
ParticleSystem(int pool_size=0)
Default Constructor.
A base class for all things that want to be reference-counted.
An individual, physically-modelable particle abstract base class.
Definition: baseParticle.h:26
virtual void write_spawn_templates(ostream &out, int indent=0) const
Write a string representation of this instance to &lt;out&gt;.
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
void update(PN_stdfloat dt)
Updates the particle system.
virtual void write_free_particle_fifo(ostream &out, int indent=0) const
Write a string representation of this instance to &lt;out&gt;.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
virtual void write(ostream &out=cout, unsigned int indent=0) const
Write a string representation of this instance to &lt;out&gt;.
Definition: physical.cxx:213