#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(rustdoc::redundant_explicit_links)]
#![doc = include_str!("../README.md")]
use self::prelude::{
Particle2dMaterial, ParticleEffectInstance, ParticleSpawnerState, ParticleStore,
};
use crate::sprite::SpriteParticle2dMaterial;
use bevy_app::{App, First, Plugin, PostUpdate, Update};
use bevy_asset::{load_internal_asset, uuid_handle, Asset, AssetApp, AssetEvent, Assets, Handle};
use bevy_camera::{
primitives::Aabb,
visibility::{add_visibility_class, Visibility, VisibilityClass, VisibilitySystems},
};
use bevy_color::LinearRgba;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
schedule::{common_conditions::on_message, IntoScheduleConfigs},
};
use bevy_math::Vec2;
use bevy_reflect::{Reflect, TypePath};
use bevy_render::sync_world::SyncToRenderWorld;
use bevy_shader::Shader;
use bevy_transform::components::Transform;
use color::ColorParticle2dMaterial;
use serde::{Deserialize, Serialize};
use values::Rval;
mod color;
mod curve;
mod loader;
mod material;
mod sprite;
mod update;
mod values;
#[allow(unused)]
pub mod prelude {
pub use super::color::ColorParticle2dMaterial;
pub use super::curve::{LerpThat, MultiCurve };
pub use super::loader::ParticleEffectLoader;
pub use super::material::{Particle2dMaterial, Particle2dMaterialPlugin};
pub use super::sprite::SpriteParticle2dMaterial;
pub use super::update::{OneShot, ParticleEffectInstance, ParticleSpawnerState, ParticleStore};
pub use super::values::{Random, Rval};
pub use super::{
Attractor, EmissionShape, EnokiPlugin, NoAutoAabb, Particle2dEffect, ParticleEffectHandle,
ParticleSpawner,
};
}
pub(crate) const PARTICLE_VERTEX_OUT: Handle<Shader> =
uuid_handle!("8d6bc2d4-7577-4890-a3a4-0faea3a27448");
pub(crate) const PARTICLE_VERTEX: Handle<Shader> =
uuid_handle!("57c98346-305c-461a-8cdc-7b3fac8be0ca");
pub(crate) const PARTICLE_COLOR_FRAG: Handle<Shader> =
uuid_handle!("f60a0cf3-19d3-4425-b6f8-b06bf7ba2f34");
pub(crate) const PARTICLE_SPRITE_FRAG: Handle<Shader> =
uuid_handle!("9b13ccf9-eea1-4515-bdd1-1b4131368f71");
pub struct EnokiPlugin;
impl Plugin for EnokiPlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(
app,
PARTICLE_VERTEX_OUT,
"shaders/particle_vertex_out.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
PARTICLE_VERTEX,
"shaders/particle_vertex.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
PARTICLE_COLOR_FRAG,
"shaders/particle_color_frag.wgsl",
Shader::from_wgsl
);
load_internal_asset!(
app,
PARTICLE_SPRITE_FRAG,
"shaders/particle_sprite_frag.wgsl",
Shader::from_wgsl
);
app.add_plugins(material::Particle2dMaterialPlugin::<SpriteParticle2dMaterial>::default());
app.add_plugins(material::Particle2dMaterialPlugin::<ColorParticle2dMaterial>::default());
app.register_type::<update::ParticleStore>();
app.register_type::<update::ParticleSpawnerState>();
app.register_type::<update::ParticleSpawnerState>();
app.register_type::<update::Particle>();
app.register_type::<ParticleEffectHandle>();
app.init_asset::<Particle2dEffect>();
app.init_asset_loader::<loader::ParticleEffectLoader>();
let _ = app
.world_mut()
.resource_mut::<Assets<ColorParticle2dMaterial>>()
.insert(
&Handle::<ColorParticle2dMaterial>::default(),
ColorParticle2dMaterial::default(),
);
let _ = app
.world_mut()
.resource_mut::<Assets<Particle2dEffect>>()
.insert(
&Handle::<Particle2dEffect>::default(),
Particle2dEffect::default(),
);
app.add_systems(
First,
loader::on_asset_loaded.run_if(on_message::<AssetEvent<Particle2dEffect>>),
);
app.add_systems(
Update,
(
loader::reload_effect,
update::clone_effect,
update::remove_finished_spawner,
update::update_spawner,
),
);
app.add_systems(
PostUpdate,
(
update::calculcate_particle_bounds.in_set(VisibilitySystems::CalculateBounds),
),
);
}
}
#[derive(Component)]
pub struct NoAutoAabb;
#[derive(Clone, Component, Default)]
#[require(VisibilityClass)]
#[component(on_add = add_visibility_class::<RenderParticleTag>)]
pub struct RenderParticleTag;
#[derive(Component, DerefMut, Deref, Clone)]
#[require(
ParticleSpawnerState,
ParticleEffectInstance,
ParticleEffectHandle,
ParticleStore,
Transform,
Visibility,
Aabb,
SyncToRenderWorld,
RenderParticleTag
)]
pub struct ParticleSpawner<T: Particle2dMaterial>(pub Handle<T>);
impl<T: Particle2dMaterial> From<Handle<T>> for ParticleSpawner<T> {
fn from(value: Handle<T>) -> Self {
Self(value)
}
}
impl Default for ParticleSpawner<ColorParticle2dMaterial> {
fn default() -> Self {
ParticleSpawner(Handle::default())
}
}
#[derive(Deserialize, Reflect, Default, Clone, Debug, Serialize, PartialEq)]
#[reflect]
pub enum EmissionShape {
#[default]
Point,
Circle(f32),
}
#[derive(Deserialize, Serialize, Clone, Debug, Reflect)]
pub struct Attractor {
pub position: Vec2,
pub strength: f32,
pub min_distance: f32,
}
#[derive(Component, Reflect, Deref, DerefMut, Default)]
#[reflect]
pub struct ParticleEffectHandle(pub Handle<Particle2dEffect>);
impl From<Handle<Particle2dEffect>> for ParticleEffectHandle {
fn from(value: Handle<Particle2dEffect>) -> Self {
Self(value)
}
}
#[derive(Asset, TypePath, Deserialize, Serialize, Clone, Debug)]
pub struct Particle2dEffect {
pub spawn_rate: f32,
pub spawn_amount: u32,
pub emission_shape: EmissionShape,
pub lifetime: Rval<f32>,
pub linear_speed: Option<Rval<f32>>,
pub linear_acceleration: Option<Rval<f32>>,
pub direction: Option<Rval<Vec2>>,
pub angular_speed: Option<Rval<f32>>,
pub angular_acceleration: Option<Rval<f32>>,
pub scale: Option<Rval<f32>>,
pub color: Option<LinearRgba>,
pub gravity_direction: Option<Rval<Vec2>>,
pub gravity_speed: Option<Rval<f32>>,
pub linear_damp: Option<Rval<f32>>,
pub angular_damp: Option<Rval<f32>>,
pub scale_curve: Option<curve::MultiCurve<f32>>,
pub color_curve: Option<curve::MultiCurve<LinearRgba>>,
pub attractors: Option<Vec<Attractor>>,
pub relative_positioning: Option<bool>,
}
impl Default for Particle2dEffect {
fn default() -> Self {
Self {
spawn_rate: 0.1,
spawn_amount: 1,
emission_shape: EmissionShape::Point,
lifetime: Rval::new(1., 0.0),
linear_speed: Some(Rval(100., 0.1)),
linear_acceleration: None,
direction: Some(Rval(Vec2::Y, 0.1)),
angular_speed: None,
angular_acceleration: None,
scale: Some(Rval(5., 1.)),
color: None,
gravity_direction: None,
gravity_speed: None,
linear_damp: None,
angular_damp: None,
scale_curve: None,
color_curve: None,
attractors: None,
relative_positioning: None,
}
}
}