20 entt::meta_factory<EmitterShape::Enum>{}
21 .type(
"EmitterShape"_hs)
26 .data<EmitterShape::Sphere>(
"Sphere"_hs)
31 .data<EmitterShape::Hemisphere>(
"Hemisphere"_hs)
36 .data<EmitterShape::Circle>(
"Circle"_hs)
41 .data<EmitterShape::Box>(
"Box"_hs)
46 .data<EmitterShape::Rect>(
"Rect"_hs)
53 entt::meta_factory<EmitterDirection::Enum>{}
54 .type(
"EmitterDirection"_hs)
59 .data<EmitterDirection::Up>(
"Up"_hs)
64 .data<EmitterDirection::Outward>(
"Outward"_hs)
70 entt::meta_factory<bx::Easing::Enum>{}
76 .data<bx::Easing::Linear>(
"Linear"_hs)
81 .data<bx::Easing::Step>(
"Step"_hs)
86 .data<bx::Easing::SmoothStep>(
"SmoothStep"_hs)
91 .data<bx::Easing::InQuad>(
"InQuad"_hs)
96 .data<bx::Easing::OutQuad>(
"OutQuad"_hs)
101 .data<bx::Easing::InOutQuad>(
"InOutQuad"_hs)
106 .data<bx::Easing::InCubic>(
"InCubic"_hs)
111 .data<bx::Easing::OutCubic>(
"OutCubic"_hs)
116 .data<bx::Easing::InOutCubic>(
"InOutCubic"_hs)
121 .data<bx::Easing::InSine>(
"InSine"_hs)
126 .data<bx::Easing::OutSine>(
"OutSine"_hs)
131 .data<bx::Easing::InOutSine>(
"InOutSine"_hs)
136 .data<bx::Easing::InExpo>(
"InExpo"_hs)
141 .data<bx::Easing::OutExpo>(
"OutExpo"_hs)
146 .data<bx::Easing::InOutExpo>(
"InOutExpo"_hs)
151 .data<bx::Easing::InElastic>(
"InElastic"_hs)
156 .data<bx::Easing::OutElastic>(
"OutElastic"_hs)
161 .data<bx::Easing::InOutElastic>(
"InOutElastic"_hs)
166 .data<bx::Easing::InBack>(
"InBack"_hs)
171 .data<bx::Easing::OutBack>(
"OutBack"_hs)
176 .data<bx::Easing::InOutBack>(
"InOutBack"_hs)
181 .data<bx::Easing::InBounce>(
"InBounce"_hs)
186 .data<bx::Easing::OutBounce>(
"OutBounce"_hs)
191 .data<bx::Easing::InOutBounce>(
"InOutBounce"_hs)
197 entt::meta_factory<SimulationSpace::Enum>{}
198 .type(
"SimulationSpace"_hs)
203 .data<SimulationSpace::World>(
"World"_hs)
208 .data<SimulationSpace::Local>(
"Local"_hs)
214 entt::meta_factory<particle_emitter_component>{}
215 .type(
"particle_emitter_component"_hs)
230 entt::attribute{
"tooltip",
"Controls whether the particle emitter actively spawns and updates particles. Disabled emitters stop emission but existing particles continue to animate."},
232 .data<&particle_emitter_component::set_loop, &particle_emitter_component::is_loop>(
"loop"_hs)
236 entt::attribute{
"tooltip",
"Controls whether the emitter loops continuously (true) or emits only once up to max particles (false). Non-looping emitters stop emitting after reaching max particles."},
238 .data<&particle_emitter_component::set_max_particles, &particle_emitter_component::get_max_particles>(
"max_particles"_hs)
242 entt::attribute{
"tooltip",
"Maximum number of particles that can exist simultaneously. Higher values allow more dense effects but impact performance."},
244 .data<nullptr, &particle_emitter_component::get_num_particles>(
"num_particles"_hs)
248 entt::attribute{
"tooltip",
"Current number of active particles in the system (read-only). Updates in real-time as particles spawn and die."},
250 .data<&particle_emitter_component::set_emission_lifetime, &particle_emitter_component::get_emission_lifetime>(
"emission_lifetime"_hs)
254 entt::attribute{
"tooltip",
"Duration of one complete emission cycle in seconds. Controls how long the emitter takes to spawn all particles before restarting the cycle."},
258 .data<&particle_emitter_component::set_lifetime, &particle_emitter_component::get_lifetime>(
"lifetime"_hs)
262 entt::attribute{
"tooltip",
"How long each particle lives in seconds. Longer lifetimes create more persistent effects."},
266 .data<&particle_emitter_component::set_emission_rate, &particle_emitter_component::get_emission_rate>(
"emission_rate"_hs)
270 entt::attribute{
"tooltip",
"Emission rate in particles per second. Higher values create denser particle effects. 0 = no emission."},
273 .data<&particle_emitter_component::set_temporal_motion, &particle_emitter_component::get_temporal_motion>(
"temporal_motion"_hs)
277 entt::attribute{
"tooltip",
"Controls temporal interpolation for moving emitters. 1.0 = full interpolation (smooth trails), 0.0 = no interpolation (discrete emission)."},
282 .data<&particle_emitter_component::set_gravity_scale, &particle_emitter_component::get_gravity_scale>(
"gravity_scale"_hs)
286 entt::attribute{
"tooltip",
"Multiplier for gravity effect on particles. 0.0 = no gravity, 1.0 = Earth-like gravity, negative values = upward force."},
291 .data<nullptr, &particle_emitter_component::get_world_bounds>(
"world_bounds"_hs)
295 entt::attribute{
"tooltip",
"Bounding box containing all particles in world space. Used for culling and optimization (read-only)."},
297 .data<&particle_emitter_component::set_emission_shape_scale, &particle_emitter_component::get_emission_shape_scale>(
"emission_shape_scale"_hs)
301 entt::attribute{
"tooltip",
"Scale of the emission shape. 1.0 = no scaling, 2.0 = double size, 0.5 = half size."},
303 .data<&particle_emitter_component::set_shape, &particle_emitter_component::get_shape>(
"shape"_hs)
307 entt::attribute{
"tooltip",
"Geometric shape from which particles are spawned. Sphere = 3D ball, Hemisphere = half sphere, Circle = 2D ring, Disc = filled circle, Rect = rectangle."},
310 .data<&particle_emitter_component::set_direction, &particle_emitter_component::get_direction>(
"direction"_hs)
314 entt::attribute{
"tooltip",
"Initial direction particles move when spawned. Up = particles move upward, Outward = particles move away from spawn position."},
317 .data<&particle_emitter_component::set_simulation_space, &particle_emitter_component::get_simulation_space>(
"simulation_space"_hs)
321 entt::attribute{
"tooltip",
"Controls whether particles are simulated in world space or local space."},
324 .data<&particle_emitter_component::set_velocity_gradient, &particle_emitter_component::get_velocity_gradient>(
"velocity_gradient"_hs)
328 entt::attribute{
"tooltip",
"Velocity range gradient over particle lifetime. Controls how particle speed changes from spawn to death."},
333 .data<&particle_emitter_component::set_velocity_damping, &particle_emitter_component::get_velocity_damping>(
"velocity_damping"_hs)
337 entt::attribute{
"tooltip",
"Reduces particle velocity over time. 0.0 = no damping (particles maintain speed), 1.0 = full damping (particles stop immediately)."},
344 .data<&particle_emitter_component::set_position_easing, &particle_emitter_component::get_position_easing>(
"position_easing"_hs)
348 entt::attribute{
"tooltip",
"Curve controlling how particles move from start to end position. Linear = constant speed, EaseIn = slow start, EaseOut = slow end."},
352 .data<&particle_emitter_component::set_force_over_lifetime, &particle_emitter_component::get_force_over_lifetime>(
"force_over_lifetime"_hs)
356 entt::attribute{
"tooltip",
"Additional force applied to particles throughout their lifetime. Use for wind, magnetism, or other environmental effects. Values are in world units."},
360 .data<&particle_emitter_component::set_scale_gradient, &particle_emitter_component::get_scale_gradient>(
"scale_gradient"_hs)
364 entt::attribute{
"tooltip",
"Scale range gradient over particle lifetime. Controls how particle size changes from spawn to death."},
368 .data<&particle_emitter_component::set_size_by_speed_range, &particle_emitter_component::get_size_by_speed_range>(
"size_by_speed_range"_hs)
372 entt::attribute{
"tooltip",
"Size multiplier range based on particle speed. Min = size at slow speed, Max = size at fast speed. Use values like 0.5-2.0 for dramatic effects."},
378 .data<&particle_emitter_component::set_size_by_speed_velocity_range, &particle_emitter_component::get_size_by_speed_velocity_range>(
"size_by_speed_velocity_range"_hs)
382 entt::attribute{
"tooltip",
"Velocity range for size mapping. Particles moving at min speed get min size, particles at max speed get max size."},
388 .data<&particle_emitter_component::set_blend_gradient, &particle_emitter_component::get_blend_gradient>(
"blend_gradient"_hs)
392 entt::attribute{
"tooltip",
"Opacity range gradient over particle lifetime. Controls how particle transparency changes from spawn to death."},
396 .data<&particle_emitter_component::set_blend_multiplier, &particle_emitter_component::get_blend_multiplier>(
"blend_multiplier"_hs)
400 entt::attribute{
"tooltip",
"Global blend multiplier for all particles regardless of lifetime. 0.0 = fully transparent, 1.0 = no change, values > 1.0 = enhanced opacity."},
406 .data<&particle_emitter_component::set_color_gradient, &particle_emitter_component::get_color_gradient>(
"color_gradient"_hs)
410 entt::attribute{
"tooltip",
"Color gradient defining particle color over lifetime. Colors are interpolated smoothly based on gradient keyframes."},
414 .data<&particle_emitter_component::set_color_by_speed_gradient, &particle_emitter_component::get_color_by_speed_gradient>(
"color_by_speed_gradient"_hs)
418 entt::attribute{
"tooltip",
"Color gradient applied based on particle speed. Slow particles use colors from the start of the gradient, fast particles use colors from the end."},
422 .data<&particle_emitter_component::set_color_by_speed_velocity_range, &particle_emitter_component::get_color_by_speed_velocity_range>(
"color_by_speed_velocity_range"_hs)
426 entt::attribute{
"tooltip",
"Velocity range for color mapping. Particles moving at min speed get slow color, particles at max speed get fast color."},
431 .data<&particle_emitter_component::set_lifetime_by_emitter_speed_gradient, &particle_emitter_component::get_lifetime_by_emitter_speed_gradient>(
"lifetime_by_emitter_speed_gradient"_hs)
435 entt::attribute{
"tooltip",
"Lifetime multiplier gradient based on emitter movement speed. Allows particles to live longer/shorter based on how fast the emitter is moving."},
440 .data<&particle_emitter_component::set_lifetime_by_emitter_speed_range, &particle_emitter_component::get_lifetime_by_emitter_speed_range>(
"lifetime_by_emitter_speed_range"_hs)
444 entt::attribute{
"tooltip",
"Emitter speed range for lifetime mapping. Emitters moving at min speed get slow gradient value, emitters at max speed get fast gradient value."},
449 .data<&particle_emitter_component::set_texture, &particle_emitter_component::get_texture>(
"texture"_hs)
453 entt::attribute{
"tooltip",
"Texture asset used to render each particle. Should be a square texture with alpha channel for best results. Common formats: smoke, fire, sparkle, etc."},
460 try_save(ar, ser20::make_nvp(
"enabled", obj.is_enabled()));
461 try_save(ar, ser20::make_nvp(
"shape", obj.get_shape()));
462 try_save(ar, ser20::make_nvp(
"direction", obj.get_direction()));
463 try_save(ar, ser20::make_nvp(
"simulation_space", obj.get_simulation_space()));
464 try_save(ar, ser20::make_nvp(
"max_particles", obj.get_max_particles()));
468 try_save(ar, ser20::make_nvp(
"emission_lifetime", obj.get_emission_lifetime()));
469 try_save(ar, ser20::make_nvp(
"emission_shape_scale", obj.get_emission_shape_scale()));
470 try_save(ar, ser20::make_nvp(
"gravity_scale", obj.get_gravity_scale()));
471 try_save(ar, ser20::make_nvp(
"emission_rate", obj.get_emission_rate()));
472 try_save(ar, ser20::make_nvp(
"temporal_motion", obj.get_temporal_motion()));
473 try_save(ar, ser20::make_nvp(
"velocity_damping", obj.get_velocity_damping()));
474 try_save(ar, ser20::make_nvp(
"force_over_lifetime", obj.get_force_over_lifetime()));
475 try_save(ar, ser20::make_nvp(
"size_by_speed_range", obj.get_size_by_speed_range()));
476 try_save(ar, ser20::make_nvp(
"size_by_speed_velocity_range", obj.get_size_by_speed_velocity_range()));
477 try_save(ar, ser20::make_nvp(
"color_by_speed_gradient", obj.get_color_by_speed_gradient()));
478 try_save(ar, ser20::make_nvp(
"color_by_speed_velocity_range", obj.get_color_by_speed_velocity_range()));
479 try_save(ar, ser20::make_nvp(
"lifetime_by_emitter_speed_gradient", obj.get_lifetime_by_emitter_speed_gradient()));
480 try_save(ar, ser20::make_nvp(
"lifetime_by_emitter_speed_range", obj.get_lifetime_by_emitter_speed_range()));
483 try_save(ar, ser20::make_nvp(
"lifetime", obj.get_lifetime()));
484 try_save(ar, ser20::make_nvp(
"velocity_gradient", obj.get_velocity_gradient()));
485 try_save(ar, ser20::make_nvp(
"scale_gradient", obj.get_scale_gradient()));
486 try_save(ar, ser20::make_nvp(
"blend_gradient", obj.get_blend_gradient()));
487 try_save(ar, ser20::make_nvp(
"blend_multiplier", obj.get_blend_multiplier()));
490 try_save(ar, ser20::make_nvp(
"color_gradient", obj.get_color_gradient()));
493 try_save(ar, ser20::make_nvp(
"position_easing", obj.get_position_easing()));
496 try_save(ar, ser20::make_nvp(
"texture", obj.get_texture()));
499 try_save(ar, ser20::make_nvp(
"loop", obj.is_loop()));
507 if(
try_load(ar, ser20::make_nvp(
"enabled", enabled)))
509 obj.set_enabled(enabled);
516 obj.set_shape(
shape);
518 if(
try_load(ar, ser20::make_nvp(
"direction", direction)))
520 obj.set_direction(direction);
524 if(
try_load(ar, ser20::make_nvp(
"simulation_space", simulation_space)))
526 obj.set_simulation_space(simulation_space);
529 uint32_t max_particles{1024};
530 if(
try_load(ar, ser20::make_nvp(
"max_particles", max_particles)))
532 obj.set_max_particles(max_particles);
536 std::chrono::duration<float> emission_lifetime{2.0f};
537 if(
try_load(ar, ser20::make_nvp(
"emission_lifetime", emission_lifetime)))
539 obj.set_emission_lifetime(emission_lifetime);
542 math::vec3 emission_shape_scale{1.0f, 1.0f, 1.0f};
543 if(
try_load(ar, ser20::make_nvp(
"emission_shape_scale", emission_shape_scale)))
545 obj.set_emission_shape_scale(emission_shape_scale);
548 float gravity_scale{0.0f};
549 if(
try_load(ar, ser20::make_nvp(
"gravity_scale", gravity_scale)))
551 obj.set_gravity_scale(gravity_scale);
554 float emission_rate{50.0f};
555 if(
try_load(ar, ser20::make_nvp(
"emission_rate", emission_rate)))
557 obj.set_emission_rate(emission_rate);
560 float temporal_motion{1.0f};
561 if(
try_load(ar, ser20::make_nvp(
"temporal_motion", temporal_motion)))
563 obj.set_temporal_motion(temporal_motion);
566 float velocity_damping{0.0f};
567 if(
try_load(ar, ser20::make_nvp(
"velocity_damping", velocity_damping)))
569 obj.set_velocity_damping(velocity_damping);
572 math::vec3 force_over_lifetime{0.0f, 0.0f, 0.0f};
573 if(
try_load(ar, ser20::make_nvp(
"force_over_lifetime", force_over_lifetime)))
575 obj.set_force_over_lifetime(force_over_lifetime);
578 frange_t size_by_speed_range{1.0f, 1.0f};
579 if(
try_load(ar, ser20::make_nvp(
"size_by_speed_range", size_by_speed_range)))
581 obj.set_size_by_speed_range(size_by_speed_range);
584 frange_t size_by_speed_velocity_range{0.0f, 10.0f};
585 if(
try_load(ar, ser20::make_nvp(
"size_by_speed_velocity_range", size_by_speed_velocity_range)))
587 obj.set_size_by_speed_velocity_range(size_by_speed_velocity_range);
591 if(
try_load(ar, ser20::make_nvp(
"color_by_speed_gradient", color_by_speed_gradient)))
593 obj.set_color_by_speed_gradient(color_by_speed_gradient);
600 bool has_slow =
try_load(ar, ser20::make_nvp(
"color_by_speed_slow_color", color_by_speed_slow_color));
601 bool has_fast =
try_load(ar, ser20::make_nvp(
"color_by_speed_fast_color", color_by_speed_fast_color));
603 if(has_slow || has_fast)
606 legacy_gradient.
add_point(color_by_speed_slow_color, 0.0f);
607 legacy_gradient.
add_point(color_by_speed_fast_color, 1.0f);
608 obj.set_color_by_speed_gradient(legacy_gradient);
612 frange_t color_by_speed_velocity_range{0.0f, 10.0f};
613 if(
try_load(ar, ser20::make_nvp(
"color_by_speed_velocity_range", color_by_speed_velocity_range)))
615 obj.set_color_by_speed_velocity_range(color_by_speed_velocity_range);
619 if(
try_load(ar, ser20::make_nvp(
"lifetime_by_emitter_speed_gradient", lifetime_by_emitter_speed_gradient)))
621 obj.set_lifetime_by_emitter_speed_gradient(lifetime_by_emitter_speed_gradient);
624 frange_t lifetime_by_emitter_speed_range{0.0f, 10.0f};
625 if(
try_load(ar, ser20::make_nvp(
"lifetime_by_emitter_speed_range", lifetime_by_emitter_speed_range)))
627 obj.set_lifetime_by_emitter_speed_range(lifetime_by_emitter_speed_range);
631 std::chrono::duration<float> lifetime{1.0f};
632 if(
try_load(ar, ser20::make_nvp(
"lifetime", lifetime)))
634 obj.set_lifetime(lifetime);
638 if(
try_load(ar, ser20::make_nvp(
"velocity_gradient", velocity_gradient)))
640 obj.set_velocity_gradient(velocity_gradient);
644 if(
try_load(ar, ser20::make_nvp(
"scale_gradient", scale_gradient)))
646 obj.set_scale_gradient(scale_gradient);
650 if(
try_load(ar, ser20::make_nvp(
"blend_gradient", blend_gradient)))
652 obj.set_blend_gradient(blend_gradient);
655 float blend_multiplier{1.0f};
656 if(
try_load(ar, ser20::make_nvp(
"blend_multiplier", blend_multiplier)))
658 obj.set_blend_multiplier(blend_multiplier);
664 if(
try_load(ar, ser20::make_nvp(
"color_gradient", color_gradient)))
666 obj.set_color_gradient(color_gradient);
670 bx::Easing::Enum position_easing{};
671 if(
try_load(ar, ser20::make_nvp(
"position_easing", position_easing)))
673 obj.set_position_easing(position_easing);
677 bx::Easing::Enum blend_easing{}, scale_easing{};
678 try_load(ar, ser20::make_nvp(
"blend_easing", blend_easing));
679 try_load(ar, ser20::make_nvp(
"scale_easing", scale_easing));
683 if(
try_load(ar, ser20::make_nvp(
"texture", texture)))
685 obj.set_texture(texture);
690 if(
try_load(ar, ser20::make_nvp(
"loop", loop)))
Component that wraps particle system emitter functionality.
void set_enabled(bool enabled)
Sets whether the emitter is enabled.
auto is_enabled() const -> bool
Checks if the emitter is enabled.