Skip to content

Commit

Permalink
Add AccelModifier for gravity-like effect
Browse files Browse the repository at this point in the history
Add a new `UpdateModifier` trait for modifiers applying to the update
phase of the simulation.

Implement that trait for a new `AccelModifier` which applies a constant
acceleration to all particles of an effect, to simulate things like
gravity.
  • Loading branch information
djeedai committed Feb 7, 2022
1 parent 30c80fa commit 22d90b8
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 9 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ See the [`examples/`](https://github.com/djeedai/bevy_hanabi/examples) folder.
- [ ] Random color
- Update
- [x] Motion integration
- [ ] Apply forces
- [ ] Constant
- [x] Apply forces
- [x] Constant (gravity)
- [ ] Bounded (AABB, etc.)
- [x] Lifetime
- [ ] Size change over lifetime
Expand All @@ -63,6 +63,12 @@ See the [`examples/`](https://github.com/djeedai/bevy_hanabi/examples) folder.
- [x] Quad (sprite)
- [x] Textured
- [ ] Generic 3D mesh
- Debug
- [x] GPU debug labels / groups
- [ ] Debug visualization
- [ ] Position magnitude
- [ ] Velocity magnitude
- [ ] Age / lifetime

## Compatible Bevy versions

Expand Down
6 changes: 6 additions & 0 deletions examples/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ fn setup(
}
.with(ColorOverLifetimeModifier {
gradient: gradient1,
})
.update(AccelModifier {
accel: Vec3::new(0., -3., 0.),
}),
);

Expand Down Expand Up @@ -135,6 +138,9 @@ fn setup(
}
.with(ColorOverLifetimeModifier {
gradient: gradient3,
})
.update(AccelModifier {
accel: Vec3::new(0., 5., 0.),
}),
);

Expand Down
19 changes: 18 additions & 1 deletion src/asset.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
use bevy::{
asset::{AssetLoader, Handle, LoadContext, LoadedAsset},
math::Vec3,
reflect::TypeUuid,
render::texture::Image,
utils::BoxedFuture,
};
use serde::{Deserialize, Serialize};

use crate::{Gradient, Modifier, Spawner};
use crate::{Gradient, Modifier, Spawner, UpdateModifier};

#[derive(Default, Clone, Copy)]
pub struct UpdateLayout {
/// Constant accelereation to apply to all particles.
/// Generally used to simulate some kind of gravity.
pub accel: Vec3,
}

#[derive(Default, Clone)]
pub struct RenderLayout {
Expand All @@ -29,6 +37,9 @@ pub struct EffectAsset {
pub spawner: Spawner,
///
#[serde(skip)] // TODO
pub update_layout: UpdateLayout,
///
#[serde(skip)] // TODO
pub render_layout: RenderLayout,
}
///
Expand All @@ -41,6 +52,12 @@ impl EffectAsset {
//self.modifiers.push(Box::new(modifier));
self
}

pub fn update<M: UpdateModifier + Send + Sync + 'static>(mut self, modifier: M) -> Self {
modifier.apply(&mut self.update_layout);
//self.modifiers.push(Box::new(modifier));
self
}
}

#[derive(Default)]
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ mod spawn;
pub use asset::EffectAsset;
pub use bundle::ParticleEffectBundle;
pub use gradient::{Gradient, GradientKey};
pub use modifiers::{ColorOverLifetimeModifier, Modifier, ParticleTextureModifier, RenderModifier};
pub use modifiers::{
ColorOverLifetimeModifier, AccelModifier, Modifier, ParticleTextureModifier, RenderModifier,
UpdateModifier,
};
pub use plugin::HanabiPlugin;
pub use render::EffectCacheId;
pub use spawn::{SpawnCount, SpawnMode, SpawnRate, Spawner, Value};
Expand Down
20 changes: 19 additions & 1 deletion src/modifiers.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use bevy::prelude::*;

use crate::{asset::RenderLayout, gradient::Gradient};
use crate::{
asset::{RenderLayout, UpdateLayout},
gradient::Gradient,
};

pub trait Modifier {
fn apply(&self, render_layout: &mut RenderLayout);
}

pub trait UpdateModifier {
fn apply(&self, update_modifier: &mut UpdateLayout);
}

pub trait RenderModifier: Modifier {}

/// A modifier modulating each particle's color by sampling a texture.
Expand Down Expand Up @@ -33,3 +40,14 @@ impl Modifier for ColorOverLifetimeModifier {
}
}
impl RenderModifier for ColorOverLifetimeModifier {}

#[derive(Default, Clone, Copy)]
pub struct AccelModifier {
pub accel: Vec3,
}

impl UpdateModifier for AccelModifier {
fn apply(&self, layout: &mut UpdateLayout) {
layout.accel = self.accel;
}
}
17 changes: 13 additions & 4 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,14 @@ impl From<SimParams> for SimParamsUniform {
struct SpawnerParams {
/// Origin of the effect. This is either added to emitted particles at spawn time, if the effect simulated
/// in world space, or to all simulated particles if the effect is simulated in local space.
origin: [f32; 3],
origin: Vec3,
/// Number of particles to spawn this frame.
spawn: i32,
/// Global acceleration applied to all particles each frame.
/// TODO - This is NOT a spawner/emitter thing, but is a per-effect one. Rename SpawnerParams?
accel: Vec3,
/// Current number of used particles.
count: i32,
///
__pad: [f32; 3], // FIXME - Add StorageVec<> handling Std430 padding
}

pub struct ParticlesUpdatePipeline {
Expand Down Expand Up @@ -482,6 +483,8 @@ pub struct ExtractedEffect {
pub spawn_count: u32,
/// Global transform of the effect origin.
pub transform: Mat4,
/// Constant acceleration applied to all particles.
pub accel: Vec3,
/// Particles tint to modulate with the texture image.
pub color: Color,
pub rect: Rect,
Expand Down Expand Up @@ -626,6 +629,9 @@ pub(crate) fn extract_effects(
let spawner = effect.spawner(&asset.spawner);
let spawn_count = spawner.tick(dt);

// Extract the acceleration
let accel = asset.update_layout.accel;

// Generate the shader code for the color over lifetime gradient.
// TODO - Move that to a pre-pass, not each frame!
let vertex_modifiers = if let Some(grad) = &asset.render_layout.lifetime_color_gradient
Expand Down Expand Up @@ -658,6 +664,7 @@ pub(crate) fn extract_effects(
spawn_count,
color: Color::RED, //effect.color,
transform: transform.compute_matrix(),
accel,
rect: Rect {
min: Vec2::ZERO,
max: Vec2::new(0.2, 0.2), // effect
Expand Down Expand Up @@ -952,10 +959,12 @@ pub(crate) fn prepare_effects(
trace!("item_size = {}B", slice.item_size);

// Prepare the spawner block for the current slice
// FIXME - This is once per EFFECT/SLICE, not once per BATCH, so indeed this is spawner_BASE, and need an array of them in the compute shader!!!!!!!!!!!!!!
let spawner_params = SpawnerParams {
origin: extracted_effect.transform.col(3).truncate().to_array(),
spawn: extracted_effect.spawn_count as i32,
count: 0,
origin: extracted_effect.transform.col(3).truncate(),
accel: extracted_effect.accel,
..Default::default()
};
trace!("spawner_params = {:?}", spawner_params);
Expand Down
4 changes: 4 additions & 0 deletions src/render/particles_update.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct SimParams {
struct Spawner {
origin: vec3<f32>;
spawn: atomic<i32>;
accel: vec3<f32>;
count: atomic<i32>;
};

Expand Down Expand Up @@ -87,6 +88,9 @@ fn main([[builtin(global_invocation_id)]] global_invocation_id: vec3<u32>) {
}
}

// integration
vVel = vVel + (spawner.accel * sim_params.dt);

// kinematic update
vPos = vPos + (vVel * sim_params.dt);

Expand Down

0 comments on commit 22d90b8

Please sign in to comment.