Particle Effects

Fire, smoke, glowing, explosion, water fountain, falling leaves, fur, hair and many other fuzzy effects are created procedurally using particle systems. It’s important not to make the assumption that particles must have the size of a ‘particle’ that is very small, particles can have any size and any mass. Particles are physical entities which inherit PhysicsObject.

Panda3D provides a tool which simplifies testing particle systems. The tool is called “Particle Panel” and is written in Python. The link to run the particle panel is in the same folder as the particle sample which comes with Panda3D.

A particle system in Panda3D consists of 6 components:

  1. Factory

  2. Emitter

  3. Physics engine

  4. Renderer

  5. Particle System Controller (Particle System)

  6. Particle System Manager (Optional)

Factory

Particles are generated in this stage using the provided parameters. Panda3D Provides 2 Factories:

  • PointParticleFactory: Generates particles with default rotation.

  • ZspinParticleFactory: Generates particles which spin along their Z-axis. This is kind of an intermediary class- if you’re using a SpriteParticleRenderer and you want your sprites to spin without having them be full-blown oriented (i.e. angry quat math), use this. Note: ‘set_final_angle’ and ‘angular_velocity’ are mutually exclusive APIs  if angular-velocity is specified, final_angle is ignored.

PT(PointParticleFactory) pt_particle_factory = new PointParticleFactory();
pt_particle_factory->set_lifespan_base(0.5);
pt_particle_factory->set_lifespan_spread(0);
pt_particle_factory->set_mass_base(1.0);
pt_particle_factory->set_mass_spread(0);
pt_particle_factory->set_terminal_velocity_base(400);
pt_particle_factory->set_terminal_velocity_spread(0);

Emitter

The emitter is used to emit the particles generated by the factory. The shape of the emitter determins the initial position and motion direction of the particles.

Available Emitters

Each emitter is declared in a separate .h file with the same name as the emitter. For example: ArcEmitter is declared in arcEmitter.h

PT(SphereVolumeEmitter) sphere_emitter = new SphereVolumeEmitter;
sphere_emitter->set_emission_type(SphereVolumeEmitter::ET_RADIATE);
sphere_emitter->set_radius(3.0);
// negative values emit the particles toward the sphere center
sphere_emitter->set_amplitude(1);
sphere_emitter->set_amplitude_spread(0);
sphere_emitter->set_offset_force(LVector3(0, 0, 0));
sphere_emitter->set_explicit_launch_vector(LVector3(1, 0, 0));
sphere_emitter->set_radiate_origin(LPoint3(0, 0, 0));

Renderer

The renderer is used to specify how the particle appears on screen.

Available renderers

Each renderer is declared in a separate .h file with the same name as the renderer. For example: GeomParticleRenderer is declared in geomParticleRenderer.h

PT(PointParticleRenderer) pt_particle_rend = new PointParticleRenderer();
pt_particle_rend->set_alpha_mode(BaseParticleRenderer::PR_ALPHA_OUT);
pt_particle_rend->set_user_alpha(1);
pt_particle_rend->set_point_size(2.0);
pt_particle_rend->set_start_color(LColor(1, 0, 0, 1)); // alpha value is ignored
pt_particle_rend->set_end_color(LColor(1, 1, 0, 1));
pt_particle_rend->set_blend_type(PointParticleRenderer::PointParticleBlendType::PP_BLEND_LIFE);
pt_particle_rend->set_blend_method(BaseParticleRenderer::ParticleRendererBlendMethod::PP_BLEND_LINEAR);
//pt_particle_rend->set_color_blend_mode(ColorBlendAttrib::Mode::M_inv_subtract);
//pt_particle_rend->set_ignore_scale(false);

ParticleSystem

This class is the controller of the particle system.  The main parameters are:

  • Pool Size: the maximum number of particles the factory can generate Birth Rate: number of seconds between particle births, also it is the time after which the first birth occurs. So if the value is 6 the first litter will be born after 6 seconds.

  • Litter Size: number of particles created at each birth. If you want all the particles to be born at the same time, set this value to the value of (Pool Size) Litter Spread: this adds a random value in the range [-spread, +spread] to the value of Litter Size.

  • Lifespan:  umber of seconds the system will live.

  • System Grows Older: this flag must be set for the Lifespan value to be used. It’s very important to remember that particle systems which reach the end of their lifespan are automatically removed from the ParticleSystemManager and if you want to reuse them, they should be added to the ParticleSystemManager again.

PT(ParticleSystem) particle_sys = new ParticleSystem();
particle_sys->set_pool_size(100);
particle_sys->set_birth_rate(0.1);
particle_sys->set_litter_size(10);
particle_sys->set_litter_spread(0);
particle_sys->set_local_velocity_flag(true);
//particle_sys->set_spawn_on_death_flag(true); // this caused an exception!!
particle_sys->set_system_grows_older_flag(true);
particle_sys->set_system_lifespan(3.0);
particle_sys->set_active_system_flag(true);
// use it to advance system age, or start at some age
//particle_sys->set_system_age(5.0);
// system_age is updated only when set_system_grows_older_flag(true);
// get_system_age() returns 0 unless system_grows_older_flag is set

The particle factory, emitter and renderer should be attached to the ParticleSystem and most important the render node should be set.

particle_sys->set_factory(pt_particle_factory);
particle_sys->set_renderer(pt_particle_rend);
particle_sys->set_emitter(sphere_emitter);
// if spawn and render parents should be different
//particle_sys->set_spawn_render_node_path(window->get_render());
particle_sys->set_render_parent(window->get_render());

ParticleSystemManager

This class is responsible for simplifying control of particle systems. Instead of stepping each particle system in the scene they are all added to the ParticleSystemManager and they can be all stepped using a single command or they can be stepped individually. This class is not count referenced so PT() and CPT() should not be used with it. To attach a particle system to the manager:

particle_sys_mgr.attach_particlesystem(particle_sys);

To step all the particle systems:

particle_sys_mgr.do_particles(ClockObject::get_global_clock()->get_dt());

To step a specific particle system:

particle_sys_mgr.do_particles(ClockObject::get_global_clock()->get_dt(), particle_sys);

Note

Particle systems which reach the end of their lifespan are automatically removed from the ParticleSystemManager and if you want to reuse them, they should be added to the ParticleSystemManager again.