Bevy Enoki
[![License: MIT or Apache 2.0](https://img.shields.io/badge/License-MIT%20or%20Apache2-blue.svg)](./LICENSE)
[![Crate](https://img.shields.io/crates/v/bevy_enoki.svg)](https://crates.io/crates/bevy_enoki)
Enoki - A 2D particle system for the Bevy game engine.
![animation](docs/output.gif)
## Overview
The Enoki particle system is a CPU calculate particle system, that uses GPU Instancing and works well with `wasm`. It provides a `material trait`
that lets you easily implement your own fragment shader for all your VFX needs.
Additionally, spawner configuration are provided via `ron` files, which can be hot reloaded.
The default material allows not only for custom textures, but also sprite sheet animations over the particle lifetime.
🚧 This Plugin just released and is still under heavy development. The particle behavior is very basic and performance could be better.
Expect rapid change. Planned is orbital velocity, attractors and simple physics. When 2d is fleshed out and the best it can be, 3d might happen.
## Compatibility
| bevy | bevy_enoki |
| ---: | ---------: |
| 0.14 | 0.2.2 |
| 0.13 | 0.1 |
## Editor
Check out the new Web based particle editor at [Enoki Particle Editor](https://lommix.com/wasm/particle/index.html).
Easily adjust your effects by importing and exporting the Ron configuration.
## Examples
```shell
cargo run --example material
cargo run --example sprites
cargo run --example dynamic
```
## Usage
Add the `bevy_enoki` dependency to your `Cargo.toml`
```toml
bevy_enoki = "0.1"
```
Add the `EnokiPlugin` to your app
```rust
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(EnokiPlugin)
.run()
```
Create your first particle spawner. [Here is a default effect config](assets/base.particle.ron)
```rust
use bevy_enoki::prelude::*;
fn setup(
mut cmd : Commands,
mut materials: ResMut>,
server : Res,
){
cmd.spawn(Camera2dBundle::default());
//spawning quads
cmd.spawn((
ParticleSpawnerBundle {
// load the effect configuration from a ron file
effect: server.load("fire.particle.ron"),
// the default materiel is just a flat white color
// that gets multiplied by any color curve inside the
// effect definition of the `ron` file.
material: DEFAULT_MATERIAL,
..default()
},
));
// if you want to add a texture, use the inbuild `ColorParticle2dMaterial`
let texture_material = materials.add(
// hframes and vframes define how the sprite sheet is divided for animations,
// if you just want to bind a single texture, leave both at 1.
SpriteParticle2dMaterial::new(server.load("particle.png"), 6, 1),
);
cmd.spawn((
ParticleSpawnerBundle {
effect: server.load("fire.particle.ron"),
material: texture_material,
..default()
},
));
}
```
## Create a custom Material
Just like any other Bevy material, you can define your own
fragment shader.
```rust
#[derive(AsBindGroup, Asset, TypePath, Clone, Default)]
pub struct FireParticleMaterial {
#[texture(0)]
#[sampler(1)]
texture: Handle,
}
impl Particle2dMaterial for FireParticleMaterial {
fn fragment_shader() -> bevy::render::render_resource::ShaderRef {
"custom_material.wgsl".into()
}
}
fn setup(){
App::default()
.add_plugins(DefaultPlugins)
.add_plugins(EnokiPlugin)
.add_plugins(Particle2dMaterialPlugin::::default())
.run()
}
```
Now create the Shader
```wgsl
//assets/custom_material.wgsl
#import bevy_enoki::particle_vertex_out::{ VertexOutput }
@group(1) @binding(0) var texture: texture_2d;
@group(1) @binding(1) var texture_sampler: sampler;
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4 {
var out = in.color
// go wild
return out;
}
```
That's it, now add the Material to your Spawner! These are the values provided by the vertex shader:
```wgsl
struct VertexOutput {
@builtin(position) clip_position: vec4,
@location(0) @interpolate(flat) color: vec4,
@location(1) uv : vec2,
@location(2) lifetime_frac : f32,
@location(3) lifetime_total : f32,
};
```
## The Effect Asset
[Here is a default ron config](assets/base.particle.ron)
```rust
#[derive(Deserialize, Default, Clone, Debug)]
pub enum EmissionShape {
#[default]
Point,
Circle(f32),
}
#[derive(Asset, TypePath, Default, Deserialize, Clone, Debug)]
pub struct Particle2dEffect {
pub spawn_rate: f32,
pub spawn_amount: u32,
pub emission_shape: EmissionShape,
pub lifetime: Rval,
pub linear_speed: Option>,
pub linear_acceleration: Option>,
pub direction: Option>,
pub angular_speed: Option>,
pub angular_acceleration: Option>,
pub scale: Option>,
pub color: Option,
pub gravity_direction: Option>,
pub gravity_speed: Option>,
pub linear_damp: Option>,
pub angular_damp: Option>,
pub scale_curve: Option>,
pub color_curve: Option>,
}
```
This how you create a `Curve`. Currently, Supports `LinearRgba` and `f32`.
`RVal` stands for any Value with a randomness property between 0 - 1.
```rust
let curve = Curve::new()
.with_point(LinearRgba::RED, 0.0, None)
.with_point(LinearRgba::BLUE, 1.0, Some(bevy_enoki::prelude::EaseFunction::SineInOut));
// max 1.0, randomness of 0.1 (0.9 - 1.1)
let rval = Rval::new(1.0, 0.1);
```