diff --git a/client/Cargo.lock b/client/Cargo.lock index bd00dbcd..d17c18d4 100644 --- a/client/Cargo.lock +++ b/client/Cargo.lock @@ -220,12 +220,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - [[package]] name = "approx" version = "0.5.1" @@ -770,28 +764,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bevy_hanabi" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fa7d583821f2b0caa80879f88d27746cb307413196a7f47ed6111b9873467f" -dependencies = [ - "anyhow", - "bevy", - "bitflags 2.6.0", - "bytemuck", - "copyless", - "fixedbitset 0.5.7", - "naga", - "naga_oil", - "rand", - "rand_pcg", - "ron", - "serde", - "thiserror", - "typetag", -] - [[package]] name = "bevy_hierarchy" version = "0.14.0" @@ -1665,12 +1637,6 @@ dependencies = [ "const_soft_float", ] -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - [[package]] name = "core-foundation" version = "0.9.4" @@ -2489,12 +2455,6 @@ dependencies = [ "wag_core", ] -[[package]] -name = "inventory" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" - [[package]] name = "io-kit-sys" version = "0.4.1" @@ -3473,15 +3433,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_pcg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" -dependencies = [ - "rand_core", -] - [[package]] name = "range-alloc" version = "0.1.3" @@ -4145,30 +4096,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" -[[package]] -name = "typetag" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661d18414ec032a49ece2d56eee03636e43c4e8d577047ab334c0ba892e29aaf" -dependencies = [ - "erased-serde", - "inventory", - "once_cell", - "serde", - "typetag-impl", -] - -[[package]] -name = "typetag-impl" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.70", -] - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -4622,7 +4549,6 @@ version = "0.1.0" dependencies = [ "bevy", "bevy-inspector-egui", - "bevy_hanabi", "characters", "input_parsing", "player_state", diff --git a/client/Cargo.toml b/client/Cargo.toml index 43e5aaa8..30553771 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -19,7 +19,6 @@ opt-level = 3 [workspace.dependencies] bevy = "0.14" -bevy_hanabi = "0.12" bevy-inspector-egui = "0.25" rand = "0.8" diff --git a/client/lib/Cargo.toml b/client/lib/Cargo.toml index 495dbfa9..874e3125 100644 --- a/client/lib/Cargo.toml +++ b/client/lib/Cargo.toml @@ -6,7 +6,6 @@ authors = ["Eero Häihälä "] [dependencies] bevy = { workspace = true, features = ["mp3"] } -bevy_hanabi = { workspace = true } bevy-inspector-egui = { workspace = true } rand = { workspace = true } strum = { workspace = true } diff --git a/client/lib/src/assets/loaders.rs b/client/lib/src/assets/loaders.rs index 77b1cb53..45ecea83 100644 --- a/client/lib/src/assets/loaders.rs +++ b/client/lib/src/assets/loaders.rs @@ -1,13 +1,13 @@ use bevy::{prelude::*, utils::HashMap}; -use bevy_hanabi::*; use wag_core::{Animation, Icon, Model, SoundEffect, VisualEffect}; use super::{ animations::animation_paths, + materials::{BlockEffectMaterial, ClashSparkMaterial, HitSparkMaterial}, models::model_paths, sounds::{get_sound_paths, Sounds}, - Animations, AssetsLoading, Fonts, Icons, Models, Particles, + Animations, AssetsLoading, Fonts, Icons, Models, Vfx, }; pub fn fonts( @@ -101,116 +101,29 @@ pub fn sounds( ); } -// Typed like this so it can be ignored in unit tests -pub fn particles(mut commands: Commands, effects: Option>>) { - if let Some(mut effects) = effects { - let handles = vec![ - ( - VisualEffect::Block, - block_entity(&mut commands, &mut effects), - ), - (VisualEffect::Hit, hit_entity(&mut commands, &mut effects)), - ( - VisualEffect::Clash, - clash_entity(&mut commands, &mut effects), - ), - ] - .into_iter() - .collect(); - - commands.insert_resource(Particles::new(handles)); - }; -} - -fn block_entity(commands: &mut Commands, effects: &mut Assets) -> Entity { - particle_explosion( - commands, - effects, - "block", - Gradient::constant(Vec4::new(0.1, 0.2, 1.0, 1.0)), - vanishing_size_gradient(Vec2::new(0.1, 0.1), 0.1), - 2.0, - 50.0, - ) -} - -fn hit_entity(commands: &mut Commands, effects: &mut Assets) -> Entity { - particle_explosion( - commands, - effects, - "hit", - Gradient::constant(Vec4::new(1.0, 0.7, 0.0, 1.0)), - vanishing_size_gradient(Vec2::new(0.1, 0.1), 0.2), - 3.0, - 100.0, - ) -} - -fn clash_entity(commands: &mut Commands, effects: &mut Assets) -> Entity { - particle_explosion( - commands, - effects, - "clash", - Gradient::constant(Vec4::new(0.2, 0.3, 0.5, 1.0)), - vanishing_size_gradient(Vec2::new(0.1, 0.1), 0.1), - 8.0, - 30.0, - ) -} - -fn vanishing_size_gradient(start: Vec2, duration: f32) -> Gradient { - let mut size_gradient = Gradient::new(); - size_gradient.add_key(0.0, start); - size_gradient.add_key(duration, Vec2::splat(0.0)); - size_gradient -} - -fn particle_explosion( - commands: &mut Commands, - effects: &mut Assets, - name: &'static str, - color_gradient: Gradient, - size_gradient: Gradient, - speed: f32, - amount: f32, -) -> Entity { - let spawner = Spawner::once(amount.into(), false); - let mut module = Module::default(); - - let position_modifier = SetPositionSphereModifier { - dimension: ShapeDimension::Surface, - radius: module.lit(0.2), - center: module.lit(Vec3::ZERO), - }; - - let velocity_modifier = SetVelocitySphereModifier { - center: module.lit(Vec3::ZERO), - speed: module.lit(speed), - }; - - let lifetime = SetAttributeModifier::new(Attribute::LIFETIME, module.lit(1.0)); - - let gravity = AccelModifier::new(module.lit(Vec3::new(0.0, -2.0, 0.0))); - - let effect = effects.add( - EffectAsset::new(vec![1000], spawner, module) - .init(position_modifier) - .init(velocity_modifier) - .init(lifetime) - .update(gravity) - .render(ColorOverLifetimeModifier { - gradient: color_gradient, - }) - .render(SizeOverLifetimeModifier { - gradient: size_gradient, - ..default() - }), - ); - - commands - .spawn(( - ParticleEffectBundle::new(effect), - Name::new(format!("Particle system '{name}'")), - )) - .id() +pub fn vfx( + mut commands: Commands, + mut meshes: ResMut>, + mut clash_spark_materials: ResMut>, + mut block_effect_materials: ResMut>, + mut hit_spark_materials: ResMut>, +) { + let mesh_handles = vec![ + (VisualEffect::Block, meshes.add(Rectangle::new(1.1, 2.0))), + (VisualEffect::Hit, meshes.add(Rectangle::new(1.5, 1.5))), + (VisualEffect::Clash, meshes.add(Rectangle::new(1.5, 1.5))), + ] + .into_iter() + .collect(); + + let clash_spark_material = clash_spark_materials.add(ClashSparkMaterial::default()); + let block_effect_material = block_effect_materials.add(BlockEffectMaterial::default()); + let hit_spark_material = hit_spark_materials.add(HitSparkMaterial::default()); + + commands.insert_resource(Vfx::new( + mesh_handles, + clash_spark_material, + block_effect_material, + hit_spark_material, + )); } diff --git a/client/lib/src/assets/materials.rs b/client/lib/src/assets/materials.rs new file mode 100644 index 00000000..1e51ad43 --- /dev/null +++ b/client/lib/src/assets/materials.rs @@ -0,0 +1,158 @@ +use bevy::{ + pbr::{ExtendedMaterial, MaterialExtension}, + prelude::*, + render::render_resource::{AsBindGroup, ShaderRef}, +}; +use characters::FlashRequest; + +#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] +pub struct HitSparkMaterial { + #[uniform(0)] + base_color: LinearRgba, + #[uniform(1)] + mid_color: LinearRgba, + #[uniform(2)] + edge_color: LinearRgba, + #[uniform(3)] + start_time: f32, +} +impl HitSparkMaterial { + pub(crate) fn reset(&mut self, time: f32) { + self.start_time = time; + } +} + +impl Default for HitSparkMaterial { + fn default() -> Self { + Self { + edge_color: LinearRgba::rgb(1.0, 0.2, 0.05), + mid_color: LinearRgba::rgb(1.0, 1.0, 0.1), + base_color: LinearRgba::rgb(1.0, 1.0, 1.0), + start_time: 0.0, + } + } +} + +impl Material for HitSparkMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/hit_spark.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + AlphaMode::Blend + } +} + +#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] +pub struct BlockEffectMaterial { + #[uniform(0)] + base_color: LinearRgba, + #[uniform(1)] + edge_color: LinearRgba, + #[uniform(2)] + speed: f32, + #[uniform(3)] + start_time: f32, +} +impl BlockEffectMaterial { + pub(crate) fn reset(&mut self, time: f32) { + self.start_time = time; + } +} +impl Default for BlockEffectMaterial { + fn default() -> Self { + Self { + edge_color: LinearRgba::rgb(0.1, 0.2, 1.0), + base_color: LinearRgba::rgb(1.0, 1.0, 1.0), + speed: 1.5, + start_time: 0.0, + } + } +} + +impl Material for BlockEffectMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/block_effect.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + AlphaMode::Blend + } +} + +#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] +pub struct ClashSparkMaterial { + #[uniform(0)] + base_color: LinearRgba, + #[uniform(1)] + edge_color: LinearRgba, + #[uniform(2)] + speed: f32, + #[uniform(3)] + start_time: f32, +} +impl ClashSparkMaterial { + pub(crate) fn reset(&mut self, time: f32) { + self.start_time = time; + } +} + +impl Default for ClashSparkMaterial { + fn default() -> Self { + Self { + edge_color: LinearRgba::rgb(0.9, 0.1, 0.9), + base_color: LinearRgba::rgb(1.0, 0.5, 1.0), + speed: 1.2, + start_time: 0.0, + } + } +} + +impl Material for ClashSparkMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/clash_spark.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + AlphaMode::Blend + } +} + +// Extended Flash Material +pub type ExtendedFlashMaterial = ExtendedMaterial; + +#[derive(Asset, AsBindGroup, TypePath, Debug, Clone)] +pub struct FlashMaterial { + // Start at a high binding number to ensure bindings don't conflict + // with the base material + #[uniform(100)] + pub color: LinearRgba, + #[uniform(101)] + pub speed: f32, + #[uniform(102)] + pub depth: f32, // How far into the flash to go? 1 means go full monochrome color, 0 means no change + #[uniform(103)] + pub duration: f32, + #[uniform(104)] + pub start_time: f32, +} +impl MaterialExtension for FlashMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/flash_material.wgsl".into() + } + + fn deferred_fragment_shader() -> ShaderRef { + "shaders/flash_material.wgsl".into() + } +} +impl FlashMaterial { + pub fn from_request(request: FlashRequest, time: f32) -> Self { + Self { + color: request.color.into(), + speed: request.speed, + depth: request.depth, + duration: request.duration, + start_time: time, + } + } +} diff --git a/client/lib/src/assets/mod.rs b/client/lib/src/assets/mod.rs index fa2ddd95..daf39008 100644 --- a/client/lib/src/assets/mod.rs +++ b/client/lib/src/assets/mod.rs @@ -2,14 +2,16 @@ use bevy::{prelude::*, utils::HashMap}; mod animations; mod loaders; +mod materials; mod models; -mod particles; mod sounds; +mod vfx; pub use animations::{AnimationHelper, AnimationHelperSetup, Animations}; +pub use materials::{ExtendedFlashMaterial, FlashMaterial}; pub use models::{Models, PlayerModelHook}; -pub use particles::{ParticleRequest, Particles}; pub use sounds::Sounds; +pub use vfx::{Vfx, VfxRequest}; use wag_core::{GameState, Icon}; @@ -29,6 +31,12 @@ pub struct AssetsPlugin; impl Plugin for AssetsPlugin { fn build(&self, app: &mut App) { app.init_resource::() + .add_plugins(( + MaterialPlugin::::default(), + MaterialPlugin::::default(), + MaterialPlugin::::default(), + MaterialPlugin::::default(), + )) .add_systems( PreStartup, ( @@ -37,7 +45,7 @@ impl Plugin for AssetsPlugin { loaders::models, loaders::animations, loaders::sounds, - loaders::particles, + loaders::vfx, ), ) .add_systems( @@ -54,7 +62,7 @@ impl Plugin for AssetsPlugin { ( animations::update_animation, sounds::play_queued, - particles::handle_requests, + vfx::handle_requests, ), ); } diff --git a/client/lib/src/assets/models.rs b/client/lib/src/assets/models.rs index a44e4696..136e3cd0 100644 --- a/client/lib/src/assets/models.rs +++ b/client/lib/src/assets/models.rs @@ -5,7 +5,7 @@ use bevy::{ use characters::FlashRequest; use wag_core::{Joint, Joints, Model}; -use crate::player::{ExtendedFlashMaterial, FlashMaterial}; +use super::{ExtendedFlashMaterial, FlashMaterial}; #[derive(Debug, Resource, Deref, DerefMut)] pub struct Models(pub HashMap>); diff --git a/client/lib/src/assets/particles.rs b/client/lib/src/assets/particles.rs deleted file mode 100644 index e00c5129..00000000 --- a/client/lib/src/assets/particles.rs +++ /dev/null @@ -1,47 +0,0 @@ -use bevy::{prelude::*, utils::HashMap}; -use bevy_hanabi::EffectSpawner; - -use wag_core::VisualEffect; - -#[derive(Debug)] -pub struct ParticleRequest { - pub effect: VisualEffect, - pub position: Vec3, -} - -#[derive(Debug, Resource)] -pub struct Particles { - handles: HashMap, - queue: Vec, -} -impl Particles { - pub fn new(handles: HashMap) -> Particles { - Particles { - handles, - queue: vec![], - } - } - - fn get(&self, key: VisualEffect) -> Entity { - self.handles.get(&key).unwrap().to_owned() - } - - pub fn spawn(&mut self, request: ParticleRequest) { - self.queue.push(request); - } -} - -pub fn handle_requests( - mut transforms: Query<(&mut EffectSpawner, &mut Transform)>, - mut particles: ResMut, -) { - for ParticleRequest { effect, position } in - particles.queue.drain(..).collect::>().into_iter() - { - let emitter = particles.get(effect); - if let Ok((mut spawner, mut tf)) = transforms.get_mut(emitter) { - tf.translation = position; - spawner.reset(); - } - } -} diff --git a/client/lib/src/assets/vfx.rs b/client/lib/src/assets/vfx.rs new file mode 100644 index 00000000..d3955ad4 --- /dev/null +++ b/client/lib/src/assets/vfx.rs @@ -0,0 +1,114 @@ +use bevy::{prelude::*, utils::HashMap}; + +use wag_core::{Clock, VisualEffect}; + +use crate::entity_management::DespawnMarker; + +use super::materials::{BlockEffectMaterial, ClashSparkMaterial, HitSparkMaterial}; + +#[derive(Debug)] +pub struct VfxRequest { + pub effect: VisualEffect, + pub position: Vec3, +} + +#[derive(Debug, Resource)] +pub struct Vfx { + meshes: HashMap>, + queue: Vec, + clash_spark_material: Handle, + block_effect_material: Handle, + hit_spark_material: Handle, +} +impl Vfx { + pub fn new( + meshes: HashMap>, + clash_spark_material: Handle, + block_effect_material: Handle, + hit_spark_material: Handle, + ) -> Vfx { + Vfx { + meshes, + queue: vec![], + hit_spark_material, + clash_spark_material, + block_effect_material, + } + } + + pub fn spawn(&mut self, request: VfxRequest) { + self.queue.push(request); + } +} + +pub fn handle_requests( + mut commands: Commands, + mut particles: ResMut, + clock: Res, + time: Res