diff --git a/.github/example-run/2d/2d.ron b/.github/example-run/2d/2d.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/2d/2d.ron +++ b/.github/example-run/2d/2d.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/activate.ron b/.github/example-run/3d/activate.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/activate.ron +++ b/.github/example-run/3d/activate.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/expr.ron b/.github/example-run/3d/expr.ron index 460e472a..bd092e41 100644 --- a/.github/example-run/3d/expr.ron +++ b/.github/example-run/3d/expr.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(8) + events: [ + (480, AppExit), + ] ) diff --git a/.github/example-run/3d/firework.ron b/.github/example-run/3d/firework.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/firework.ron +++ b/.github/example-run/3d/firework.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/force_field.ron b/.github/example-run/3d/force_field.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/force_field.ron +++ b/.github/example-run/3d/force_field.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/init.ron b/.github/example-run/3d/init.ron index e27cba9f..f50c5ee0 100644 --- a/.github/example-run/3d/init.ron +++ b/.github/example-run/3d/init.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(6) + events: [ + (360, AppExit), + ] ) diff --git a/.github/example-run/3d/lifetime.ron b/.github/example-run/3d/lifetime.ron index 84080909..a51556bd 100644 --- a/.github/example-run/3d/lifetime.ron +++ b/.github/example-run/3d/lifetime.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(7) + events: [ + (420, AppExit), + ] ) diff --git a/.github/example-run/3d/multicam.ron b/.github/example-run/3d/multicam.ron index 99725f9b..241c653e 100644 --- a/.github/example-run/3d/multicam.ron +++ b/.github/example-run/3d/multicam.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(10) + events: [ + (600, AppExit), + ] ) diff --git a/.github/example-run/3d/ordering.ron b/.github/example-run/3d/ordering.ron index 4e9374f3..a8113dad 100644 --- a/.github/example-run/3d/ordering.ron +++ b/.github/example-run/3d/ordering.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/portal.ron b/.github/example-run/3d/portal.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/portal.ron +++ b/.github/example-run/3d/portal.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/random.ron b/.github/example-run/3d/random.ron index 460e472a..bd092e41 100644 --- a/.github/example-run/3d/random.ron +++ b/.github/example-run/3d/random.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(8) + events: [ + (480, AppExit), + ] ) diff --git a/.github/example-run/3d/ribbon.ron b/.github/example-run/3d/ribbon.ron index 4e9374f3..a8113dad 100644 --- a/.github/example-run/3d/ribbon.ron +++ b/.github/example-run/3d/ribbon.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/spawn.ron b/.github/example-run/3d/spawn.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/spawn.ron +++ b/.github/example-run/3d/spawn.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/spawn_on_command.ron b/.github/example-run/3d/spawn_on_command.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3d/spawn_on_command.ron +++ b/.github/example-run/3d/spawn_on_command.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3d/visibility.ron b/.github/example-run/3d/visibility.ron index 4e9374f3..a8113dad 100644 --- a/.github/example-run/3d/visibility.ron +++ b/.github/example-run/3d/visibility.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3dpng/billboard.ron b/.github/example-run/3dpng/billboard.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3dpng/billboard.ron +++ b/.github/example-run/3dpng/billboard.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3dpng/circle.ron b/.github/example-run/3dpng/circle.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3dpng/circle.ron +++ b/.github/example-run/3dpng/circle.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3dpng/gradient.ron b/.github/example-run/3dpng/gradient.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3dpng/gradient.ron +++ b/.github/example-run/3dpng/gradient.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3dpng/instancing.ron b/.github/example-run/3dpng/instancing.ron index 3b70c01d..c0a8ddab 100644 --- a/.github/example-run/3dpng/instancing.ron +++ b/.github/example-run/3dpng/instancing.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/.github/example-run/3dpng/worms.ron b/.github/example-run/3dpng/worms.ron index 4e9374f3..a8113dad 100644 --- a/.github/example-run/3dpng/worms.ron +++ b/.github/example-run/3dpng/worms.ron @@ -1,3 +1,5 @@ ( - exit_after: Some(5) + events: [ + (300, AppExit), + ] ) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff79ca75..a3778e90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `BuiltInOperator::IsAlive` exposing the current state of the `is_alive` particle flag, which represents whether a particle is still alive or will be deallocated at the end of the update pass. +### Changed + +- Compatible with Bevy 0.14. + ### Fixed - Prevented an error from being emitted when a GPU pipeline is still being created. (#341) diff --git a/Cargo.toml b/Cargo.toml index 5c90675f..9db6f92a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_hanabi" -version = "0.11.0" +version = "0.12.0" authors = ["Jerome Humbert "] edition = "2021" description = "Hanabi GPU particle system for the Bevy game engine" @@ -40,7 +40,7 @@ examples_world_inspector = [] [dependencies] bytemuck = { version = "1", features = ["derive"] } -fixedbitset = "0.4" +fixedbitset = "0.5" copyless = "0.1" rand = "0.8" rand_pcg = "0.3" @@ -50,12 +50,12 @@ ron = "0.8" bitflags = "2.3" typetag = "0.2" thiserror = "1.0" -# Same versions as Bevy 0.13 (bevy_render) -naga = "0.19" -naga_oil = { version = "0.13", default-features = false, features = ["test_shader"] } +# Same versions as Bevy 0.14 (bevy_render) +naga = "0.20" +naga_oil = { version = "0.14", default-features = false, features = ["test_shader"] } [dependencies.bevy] -version = "0.13" +version = "0.14.0" default-features = false features = [ "bevy_core_pipeline", "bevy_render", "bevy_asset", "x11" ] @@ -63,16 +63,18 @@ features = [ "bevy_core_pipeline", "bevy_render", "bevy_asset", "x11" ] all-features = true [dev-dependencies] -# Same versions as Bevy 0.13 (bevy_render) -wgpu = "0.19.1" +# Same versions as Bevy 0.14 (bevy_render) +wgpu = "0.20" # For world inspector; required if "examples_world_inspector" is used. -bevy-inspector-egui = "0.23" -bevy_egui = "0.25" -egui = "0.26" +bevy-inspector-egui = "0.25" +bevy_egui = { version = "0.28", default-features = false, features = [ + "manage_clipboard", "open_url" +] } +egui = "0.28" # For procedural texture generation in examples -noise = "0.8" +noise = "0.9" futures = "0.3" diff --git a/README.md b/README.md index 663dfd89..16316d5f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Crate](https://img.shields.io/crates/v/bevy_hanabi.svg)](https://crates.io/crates/bevy_hanabi) [![Build Status](https://github.com/djeedai/bevy_hanabi/actions/workflows/ci.yaml/badge.svg)](https://github.com/djeedai/bevy_hanabi/actions/workflows/ci.yaml) [![Coverage Status](https://coveralls.io/repos/github/djeedai/bevy_hanabi/badge.svg?branch=main)](https://coveralls.io/github/djeedai/bevy_hanabi?branch=main) -[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-v0.13-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) +[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-v0.14-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) 🎆 Hanabi — a GPU particle system for the Bevy game engine. @@ -27,7 +27,7 @@ Add the `bevy_hanabi` dependency to `Cargo.toml`: ```toml [dependencies] -bevy_hanabi = "0.11" +bevy_hanabi = "0.12" ``` See also [Features](#features) below for the list of supported features. @@ -353,8 +353,8 @@ This list contains the major fixed features provided by 🎆 Hanabi. Beyond that - [x] Trails / Ribbons - [x] Camera support - [x] Render layers - - [x] 2D cameras ([`Camera2dBundle`](https://docs.rs/bevy/0.13.0/bevy/core_pipeline/core_2d/struct.Camera2dBundle.html)) only - - [x] 3D cameras ([`Camera3dBundle`](https://docs.rs/bevy/0.13.0/bevy/core_pipeline/core_3d/struct.Camera3dBundle.html)) only + - [x] 2D cameras ([`Camera2dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_2d/struct.Camera2dBundle.html)) only + - [x] 3D cameras ([`Camera3dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_3d/struct.Camera3dBundle.html)) only - [x] Simultaneous dual 2D/3D cameras - [x] Multiple viewports (split screen) - [x] HDR camera and bloom @@ -376,13 +376,13 @@ This list contains the major fixed features provided by 🎆 Hanabi. Beyond that | Feature | Default | Description | |---|:-:|---| -| `2d` | ✔ | Enable rendering through 2D cameras ([`Camera2dBundle`](https://docs.rs/bevy/0.13.0/bevy/core_pipeline/core_2d/struct.Camera2dBundle.html)) | -| `3d` | ✔ | Enable rendering through 3D cameras ([`Camera3dBundle`](https://docs.rs/bevy/0.13.0/bevy/core_pipeline/core_3d/struct.Camera3dBundle.html)) | +| `2d` | ✔ | Enable rendering through 2D cameras ([`Camera2dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_2d/struct.Camera2dBundle.html)) | +| `3d` | ✔ | Enable rendering through 3D cameras ([`Camera3dBundle`](https://docs.rs/bevy/0.14.0/bevy/core_pipeline/core_3d/struct.Camera3dBundle.html)) | For optimization purpose, users of a single type of camera can disable the other type by skipping default features in their `Cargo.toml`. For example to use only the 3D mode: ```toml -bevy_hanabi = { version = "0.11", default-features = false, features = [ "3d" ] } +bevy_hanabi = { version = "0.12", default-features = false, features = [ "3d" ] } ``` ## Compatible Bevy versions @@ -393,6 +393,7 @@ Compatibility of `bevy_hanabi` versions: | `bevy_hanabi` | `bevy` | | :-- | :-- | +| `0.12` | `0.14` | | `0.10`-`0.11` | `0.13` | | `0.8`-`0.9` | `0.12` | | `0.7` | `0.11` | @@ -406,8 +407,8 @@ Compatibility of `bevy_hanabi` versions: 🎆 Hanabi is dual-licensed under either: -* MIT License ([`LICENSE-MIT`](./LICENSE-MIT) or ) -* Apache License, Version 2.0 ([`LICENSE-APACHE2`](./LICENSE-APACHE2) or ) +- MIT License ([`LICENSE-MIT`](./LICENSE-MIT) or ) +- Apache License, Version 2.0 ([`LICENSE-APACHE2`](./LICENSE-APACHE2) or ) at your option. diff --git a/examples/2d.rs b/examples/2d.rs index c8a4ba74..4a7c8252 100644 --- a/examples/2d.rs +++ b/examples/2d.rs @@ -16,6 +16,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -23,13 +25,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,2d=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -49,7 +51,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update_plane)) + .add_systems(Update, (utils::close_on_esc, update_plane)) .run(); Ok(()) diff --git a/examples/activate.rs b/examples/activate.rs index 0ad7bfe1..b4e09f6b 100644 --- a/examples/activate.rs +++ b/examples/activate.rs @@ -22,6 +22,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -29,13 +31,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,activate=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -55,7 +57,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update)) + .add_systems(Update, (utils::close_on_esc, update)) .run(); Ok(()) @@ -93,7 +95,7 @@ fn setup( half_size: Vec2::splat(2.0), }), material: materials.add(StandardMaterial { - base_color: Color::BLUE, + base_color: utils::COLOR_BLUE, unlit: true, ..Default::default() }), diff --git a/examples/billboard.rs b/examples/billboard.rs index 83fdd245..dac1dfdb 100644 --- a/examples/billboard.rs +++ b/examples/billboard.rs @@ -41,6 +41,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -48,13 +50,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,billboard=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -74,7 +76,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, rotate_camera)) + .add_systems(Update, (utils::close_on_esc, rotate_camera)) .run(); Ok(()) @@ -178,7 +180,7 @@ fn setup( mesh: meshes.add(Rectangle { half_size: Vec2::splat(2.0), }), - material: materials.add(Color::BLUE), + material: materials.add(utils::COLOR_BLUE), transform: Transform::from_xyz(0.0, -0.5, 0.0) * Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)), ..Default::default() diff --git a/examples/circle.rs b/examples/circle.rs index 92b481e1..2a6dd07e 100644 --- a/examples/circle.rs +++ b/examples/circle.rs @@ -16,6 +16,7 @@ use bevy_hanabi::prelude::*; use bevy_inspector_egui::quick::WorldInspectorPlugin; mod texutils; +mod utils; use texutils::make_anim_img; @@ -26,13 +27,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,circle=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -46,7 +47,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] @@ -172,7 +173,7 @@ fn setup( mesh: meshes.add(Rectangle { half_size: Vec2::splat(2.0), }), - material: materials.add(Color::BLUE), + material: materials.add(utils::COLOR_BLUE), transform: Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)), ..Default::default() }) @@ -182,7 +183,7 @@ fn setup( commands .spawn(PbrBundle { mesh: meshes.add(Sphere { radius: 1.0 }), - material: materials.add(Color::CYAN), + material: materials.add(utils::COLOR_CYAN), transform: Transform::from_translation(Vec3::Y), ..Default::default() }) diff --git a/examples/expr.rs b/examples/expr.rs index 192ed063..77e13a9e 100644 --- a/examples/expr.rs +++ b/examples/expr.rs @@ -16,6 +16,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut app = App::default(); app.add_plugins( @@ -23,7 +25,7 @@ fn main() -> Result<(), Box> { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,expr=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -33,7 +35,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] diff --git a/examples/firework.rs b/examples/firework.rs index 5213a179..66d85fc2 100644 --- a/examples/firework.rs +++ b/examples/firework.rs @@ -27,6 +27,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut app = App::default(); app.add_plugins( @@ -34,7 +36,7 @@ fn main() -> Result<(), Box> { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,firework=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -44,7 +46,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] diff --git a/examples/force_field.rs b/examples/force_field.rs index 9c7e2c13..5697184f 100644 --- a/examples/force_field.rs +++ b/examples/force_field.rs @@ -24,6 +24,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -31,13 +33,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,force_field=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -64,10 +66,7 @@ fn main() -> Result<(), Box> { ); app.add_systems(Startup, setup) - .add_systems( - Update, - (bevy::window::close_on_esc, spawn_on_click, move_repulsor), - ) + .add_systems(Update, (utils::close_on_esc, spawn_on_click, move_repulsor)) .run(); Ok(()) @@ -213,7 +212,7 @@ fn setup( radius: BALL_RADIUS * 2.0, })), material: materials.add(StandardMaterial { - base_color: Color::YELLOW, + base_color: utils::COLOR_YELLOW, unlit: false, ..Default::default() }), @@ -228,7 +227,7 @@ fn setup( radius: BALL_RADIUS * 1.0, }), material: materials.add(StandardMaterial { - base_color: Color::PURPLE, + base_color: utils::COLOR_PURPLE, unlit: false, ..Default::default() }), @@ -242,9 +241,9 @@ fn setup( commands.spawn(PbrBundle { mesh: meshes.add(Cuboid::new(6., 4., 6.)), material: materials.add(StandardMaterial { - base_color: Color::rgba(0., 0.7, 0., 0.3), + base_color: Color::linear_rgba(0., 0.7, 0., 0.3), unlit: true, - alpha_mode: bevy::pbr::AlphaMode::Blend, + alpha_mode: bevy::prelude::AlphaMode::Blend, ..Default::default() }), ..Default::default() @@ -254,9 +253,9 @@ fn setup( commands.spawn(PbrBundle { mesh: meshes.add(Sphere { radius: 0.6 }), material: materials.add(StandardMaterial { - base_color: Color::rgba(0.7, 0., 0., 0.3), + base_color: Color::linear_rgba(0.7, 0., 0., 0.3), unlit: true, - alpha_mode: bevy::pbr::AlphaMode::Blend, + alpha_mode: bevy::prelude::AlphaMode::Blend, ..Default::default() }), transform: Transform::from_translation(Vec3::new(-2., -1., 0.1)), diff --git a/examples/gradient.rs b/examples/gradient.rs index 61a45db8..e73f36d4 100644 --- a/examples/gradient.rs +++ b/examples/gradient.rs @@ -12,6 +12,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -25,7 +27,7 @@ fn main() -> Result<(), Box> { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,gradient=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -45,7 +47,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update)) + .add_systems(Update, (utils::close_on_esc, update)) .run(); Ok(()) @@ -128,7 +130,7 @@ fn setup( mesh: meshes.add(Cuboid { half_size: Vec3::splat(0.5), }), - material: materials.add(Color::RED), + material: materials.add(utils::COLOR_RED), ..Default::default() }); }); diff --git a/examples/init.rs b/examples/init.rs index b7b95f20..e287a9dc 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -17,6 +17,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + #[derive(Component)] struct RotateSpeed(pub f32); @@ -27,13 +29,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,init=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -53,7 +55,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, rotate_effect)) + .add_systems(Update, (utils::close_on_esc, rotate_effect)) .run(); Ok(()) @@ -152,7 +154,7 @@ fn setup( let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); spawn_effect( &mut commands, diff --git a/examples/instancing.rs b/examples/instancing.rs index 8cbf824c..1d2b570c 100644 --- a/examples/instancing.rs +++ b/examples/instancing.rs @@ -15,6 +15,8 @@ use bevy_hanabi::prelude::*; use bevy_inspector_egui::quick::WorldInspectorPlugin; use rand::Rng; +mod utils; + #[derive(Default, Resource)] struct InstanceManager { effect: Handle, @@ -180,7 +182,7 @@ fn main() { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,instancing=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -197,7 +199,7 @@ fn main() { app.insert_resource(InstanceManager::new(5, 4)) .add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, keyboard_input_system)) + .add_systems(Update, (utils::close_on_esc, keyboard_input_system)) //.add_system(stress_test.after(keyboard_input_system)) .run(); } @@ -233,7 +235,7 @@ fn setup( let mesh = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); let mut gradient = Gradient::new(); gradient.add_key(0.0, Vec4::new(0.0, 0.0, 1.0, 1.0)); diff --git a/examples/lifetime.rs b/examples/lifetime.rs index c04fc4d0..53efcaeb 100644 --- a/examples/lifetime.rs +++ b/examples/lifetime.rs @@ -21,6 +21,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -28,13 +30,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,lifetime=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -48,7 +50,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] @@ -86,7 +88,7 @@ fn setup( let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); let lifetime1 = 12.; let lifetime2 = 3.; diff --git a/examples/multicam.rs b/examples/multicam.rs index f89f5251..7d9818a6 100644 --- a/examples/multicam.rs +++ b/examples/multicam.rs @@ -12,6 +12,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() { let mut app = App::default(); app.add_plugins( @@ -19,7 +21,7 @@ fn main() { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,multicam=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -35,10 +37,7 @@ fn main() { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems( - Update, - (bevy::window::close_on_esc, update_camera_viewports), - ) + .add_systems(Update, (utils::close_on_esc, update_camera_viewports)) .run(); } @@ -57,7 +56,15 @@ fn make_effect(color: Color) -> EffectAsset { let mut color_gradient = Gradient::new(); color_gradient.add_key(0.0, Vec4::splat(1.0)); - color_gradient.add_key(0.4, Vec4::new(color.r(), color.g(), color.b(), 1.0)); + color_gradient.add_key( + 0.4, + Vec4::new( + color.to_linear().red, + color.to_linear().green, + color.to_linear().blue, + 1.0, + ), + ); color_gradient.add_key(1.0, Vec4::splat(0.0)); let writer = ExprWriter::new(); @@ -110,7 +117,7 @@ fn setup( RenderLayers::layer(0), RenderLayers::layer(0).with(2), RenderLayers::layer(1).with(2), - RenderLayers::all(), + RenderLayers::from_layers(&[0, 1, 2, 3]), ]; // Spawn 4 cameras in grid, "4-player couch co-op"-style @@ -139,21 +146,25 @@ fn setup( SplitCamera { pos: UVec2::new(i as u32 % 2, i as u32 / 2), }, - *layer, + layer.clone(), )); } - commands.spawn(DirectionalLightBundle { - directional_light: DirectionalLight { - color: Color::WHITE, - // Crank the illuminance way (too) high to make the reference cube clearly visible - illuminance: 100000., - shadows_enabled: false, + commands.spawn(( + DirectionalLightBundle { + directional_light: DirectionalLight { + color: Color::WHITE, + // Crank the illuminance way (too) high to make the reference cube clearly visible + illuminance: 100000., + shadows_enabled: false, + ..Default::default() + }, + transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 1.7, 2.4, 0.)), ..Default::default() }, - transform: Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 1.7, 2.4, 0.)), - ..Default::default() - }); + // The light affects all the views + RenderLayers::from_layers(&[0, 1, 2, 3]), + )); let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), @@ -161,10 +172,10 @@ fn setup( let plane = meshes.add(Rectangle { half_size: Vec2::splat(200.0), }); - let mat = materials.add(Color::PURPLE); - let ground_mat = materials.add(Color::OLIVE); + let mat = materials.add(utils::COLOR_PURPLE); + let ground_mat = materials.add(utils::COLOR_OLIVE); - let effect1 = effects.add(make_effect(Color::RED)); + let effect1 = effects.add(make_effect(utils::COLOR_RED)); // Ground plane to make it easier to see the different cameras commands.spawn(( @@ -177,7 +188,7 @@ fn setup( ..Default::default() }, Name::new("ground"), - RenderLayers::all(), + RenderLayers::from_layers(&[0, 1, 2, 3]), )); commands @@ -203,7 +214,7 @@ fn setup( )); }); - let effect2 = effects.add(make_effect(Color::GREEN)); + let effect2 = effects.add(make_effect(utils::COLOR_GREEN)); commands .spawn(( @@ -228,7 +239,7 @@ fn setup( )); }); - let effect3 = effects.add(make_effect(Color::BLUE)); + let effect3 = effects.add(make_effect(utils::COLOR_BLUE)); commands .spawn(( diff --git a/examples/ordering.rs b/examples/ordering.rs index 8c71bcf3..5042bcd7 100644 --- a/examples/ordering.rs +++ b/examples/ordering.rs @@ -18,6 +18,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut app = App::default(); app.add_plugins( @@ -25,7 +27,7 @@ fn main() -> Result<(), Box> { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,firework=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -35,7 +37,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] @@ -152,8 +154,8 @@ fn setup( half_size: Vec2 { x: 0.5, y: 0.5 }, })), material: materials.add(StandardMaterial { - base_color: Color::rgba(1., 0., 0., 0.5), - alpha_mode: bevy::pbr::AlphaMode::Blend, + base_color: Color::linear_rgba(1., 0., 0., 0.5), + alpha_mode: bevy::prelude::AlphaMode::Blend, ..default() }), transform: Transform { @@ -171,8 +173,8 @@ fn setup( material: materials.add(StandardMaterial { // Keep the alpha quite high, because the particles are very bright (HDR, value=4.) // so otherwise we can't see the attenuation of the blue box over the white particles. - base_color: Color::rgba(0., 0., 1., 0.95), - alpha_mode: bevy::pbr::AlphaMode::Blend, + base_color: Color::linear_rgba(0., 0., 1., 0.95), + alpha_mode: bevy::prelude::AlphaMode::Blend, ..default() }), transform: Transform { @@ -189,8 +191,8 @@ fn setup( half_size: Vec2 { x: 0.5, y: 0.5 }, })), material: materials.add(StandardMaterial { - base_color: Color::GREEN, - alpha_mode: bevy::pbr::AlphaMode::Opaque, + base_color: utils::COLOR_GREEN, + alpha_mode: bevy::prelude::AlphaMode::Opaque, ..default() }), transform: Transform { diff --git a/examples/portal.rs b/examples/portal.rs index 45615c01..a20af77c 100644 --- a/examples/portal.rs +++ b/examples/portal.rs @@ -20,6 +20,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut app = App::default(); app.add_plugins( @@ -27,7 +29,7 @@ fn main() -> Result<(), Box> { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,portal=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -37,7 +39,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] diff --git a/examples/random.rs b/examples/random.rs index 10a04488..400242b1 100644 --- a/examples/random.rs +++ b/examples/random.rs @@ -11,6 +11,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -18,13 +20,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,random=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -38,7 +40,7 @@ fn main() -> Result<(), Box> { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] @@ -76,7 +78,7 @@ fn setup( let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); let mut gradient = Gradient::new(); gradient.add_key(0.0, Vec4::new(0.0, 0.0, 1.0, 1.0)); diff --git a/examples/ribbon.rs b/examples/ribbon.rs index 56a39886..5c0e1d28 100644 --- a/examples/ribbon.rs +++ b/examples/ribbon.rs @@ -10,6 +10,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + // These determine the shape of the Spirograph: // https://en.wikipedia.org/wiki/Spirograph#Mathematical_basis const K: f32 = 0.64; @@ -30,7 +32,7 @@ fn main() { ..default() })) .add_plugins(HanabiPlugin) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_systems(Startup, setup) .add_systems(Update, move_particle_effect); diff --git a/examples/spawn.rs b/examples/spawn.rs index cc4aabf0..b209331a 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -11,6 +11,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + /// Set this to `true` to enable WGPU downlevel constraints. This is disabled by /// default to prevent the example from failing to start on devices with a /// monitor resolution larger than the maximum resolution imposed by the @@ -30,13 +32,13 @@ fn main() -> Result<(), Box> { } let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,spawn=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -56,7 +58,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update_accel)) + .add_systems(Update, (utils::close_on_esc, update_accel)) .run(); Ok(()) @@ -95,7 +97,7 @@ fn setup( let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); let mut color_gradient1 = Gradient::new(); color_gradient1.add_key(0.0, Vec4::splat(1.0)); diff --git a/examples/spawn_on_command.rs b/examples/spawn_on_command.rs index 67dd12f7..7b4c0b50 100644 --- a/examples/spawn_on_command.rs +++ b/examples/spawn_on_command.rs @@ -21,6 +21,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -28,13 +30,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::linear_rgb(0.1, 0.1, 0.1))) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,spawn_on_command=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -54,7 +56,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update)) + .add_systems(Update, (utils::close_on_esc, update)) .run(); Ok(()) diff --git a/examples/utils/mod.rs b/examples/utils/mod.rs new file mode 100644 index 00000000..3f8de142 --- /dev/null +++ b/examples/utils/mod.rs @@ -0,0 +1,17 @@ +#![allow(unused)] + +use bevy::prelude::*; + +pub fn close_on_esc(mut ev_app_exit: EventWriter, input: Res>) { + if input.just_pressed(KeyCode::Escape) { + ev_app_exit.send(AppExit::Success); + } +} + +pub const COLOR_RED: Color = Color::linear_rgb(1., 0., 0.); +pub const COLOR_GREEN: Color = Color::linear_rgb(0., 1., 0.); +pub const COLOR_BLUE: Color = Color::linear_rgb(0., 0., 1.); +pub const COLOR_YELLOW: Color = Color::linear_rgb(1., 1., 0.); +pub const COLOR_CYAN: Color = Color::linear_rgb(0., 1., 1.); +pub const COLOR_OLIVE: Color = Color::linear_rgb(0.5, 0.5, 0.); +pub const COLOR_PURPLE: Color = Color::linear_rgb(0.5, 0., 0.5); diff --git a/examples/visibility.rs b/examples/visibility.rs index 54dc4cc4..55b162ca 100644 --- a/examples/visibility.rs +++ b/examples/visibility.rs @@ -22,6 +22,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() -> Result<(), Box> { let mut wgpu_settings = WgpuSettings::default(); wgpu_settings @@ -29,13 +31,13 @@ fn main() -> Result<(), Box> { .set(WgpuFeatures::VERTEX_WRITABLE_STORAGE, true); let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins( DefaultPlugins .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,visibility=trace".to_string(), - update_subscriber: None, + ..default() }) .set(RenderPlugin { render_creation: wgpu_settings.into(), @@ -55,7 +57,7 @@ fn main() -> Result<(), Box> { app.add_plugins(WorldInspectorPlugin::default()); app.add_systems(Startup, setup) - .add_systems(Update, (bevy::window::close_on_esc, update)) + .add_systems(Update, (utils::close_on_esc, update)) .run(); Ok(()) @@ -88,7 +90,7 @@ fn setup( let cube = meshes.add(Cuboid { half_size: Vec3::splat(0.5), }); - let mat = materials.add(Color::PURPLE); + let mat = materials.add(utils::COLOR_PURPLE); let mut gradient = Gradient::new(); gradient.add_key(0.0, Vec4::new(1.0, 0.0, 0.0, 1.0)); diff --git a/examples/worms.rs b/examples/worms.rs index 893285c3..31281dc4 100644 --- a/examples/worms.rs +++ b/examples/worms.rs @@ -14,6 +14,8 @@ use bevy_hanabi::prelude::*; #[cfg(feature = "examples_world_inspector")] use bevy_inspector_egui::quick::WorldInspectorPlugin; +mod utils; + fn main() { let mut app = App::default(); app.add_plugins( @@ -21,7 +23,7 @@ fn main() { .set(LogPlugin { level: bevy::log::Level::WARN, filter: "bevy_hanabi=warn,worms=trace".to_string(), - update_subscriber: None, + ..default() }) .set(WindowPlugin { primary_window: Some(Window { @@ -31,7 +33,7 @@ fn main() { ..default() }), ) - .add_systems(Update, bevy::window::close_on_esc) + .add_systems(Update, utils::close_on_esc) .add_plugins(HanabiPlugin); #[cfg(feature = "examples_world_inspector")] diff --git a/gpu_tests/empty_effect.rs b/gpu_tests/empty_effect.rs index 88d350aa..9072b2c2 100644 --- a/gpu_tests/empty_effect.rs +++ b/gpu_tests/empty_effect.rs @@ -8,11 +8,11 @@ struct Frame(pub u32); fn main() -> Result<(), Box> { let mut app = App::default(); - app.insert_resource(ClearColor(Color::DARK_GRAY)) + app.insert_resource(ClearColor(Color::BLACK)) .add_plugins(DefaultPlugins.set(LogPlugin { level: bevy::log::Level::INFO, filter: "bevy_hanabi=debug".to_string(), - update_subscriber: None, + ..default() })) .add_plugins(HanabiPlugin) .init_resource::() @@ -35,6 +35,6 @@ fn timeout(mut frame: ResMut, mut ev_app_exit: EventWriter) { frame.0 += 1; if frame.0 >= 10 { info!("SUCCESS!"); - ev_app_exit.send(AppExit); + ev_app_exit.send(AppExit::Success); } } diff --git a/src/asset.rs b/src/asset.rs index 2ccdf3d3..8a5c911b 100644 --- a/src/asset.rs +++ b/src/asset.rs @@ -3,9 +3,10 @@ use std::ops::Deref; use bevy::{ asset::{io::Reader, Asset, AssetLoader, AsyncReadExt, LoadContext}, reflect::Reflect, - utils::{default, thiserror::Error, BoxedFuture, HashSet}, + utils::{default, HashSet}, }; use serde::{Deserialize, Serialize}; +use thiserror::Error; use crate::{ modifier::{Modifier, RenderModifier}, @@ -94,9 +95,9 @@ pub enum SimulationCondition { /// are multiple alpha blending techniques available, producing different /// results. /// -/// This is very similar to the `bevy::pbr::AlphaMode` of the `bevy_pbr` crate, -/// except that a different set of values is supported which reflects what this -/// library currently supports. +/// This is very similar to the `bevy::prelude::AlphaMode` of the `bevy_pbr` +/// crate, except that a different set of values is supported which reflects +/// what this library currently supports. /// /// The alpha mode only affects the render phase that particles are rendered /// into when rendering 3D views. For 2D views, all particle effects are @@ -725,18 +726,16 @@ impl AssetLoader for EffectAssetLoader { type Error = EffectAssetLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a Self::Settings, - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let custom_asset = ron::de::from_bytes::(&bytes)?; - Ok(custom_asset) - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let custom_asset = ron::de::from_bytes::(&bytes)?; + Ok(custom_asset) } fn extensions(&self) -> &[&str] { diff --git a/src/attributes.rs b/src/attributes.rs index ac594be9..cf485eda 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -127,8 +127,9 @@ use bevy::{ math::{Vec2, Vec3, Vec4}, reflect::{ utility::{GenericTypePathCell, NonGenericTypeInfoCell}, - DynamicStruct, FieldIter, FromReflect, NamedField, Reflect, ReflectMut, ReflectOwned, - ReflectRef, Struct, StructInfo, TypeInfo, TypePath, Typed, + DynamicStruct, FieldIter, FromReflect, FromType, GetTypeRegistration, NamedField, Reflect, + ReflectDeserialize, ReflectFromReflect, ReflectMut, ReflectOwned, ReflectRef, + ReflectSerialize, Struct, StructInfo, TypeInfo, TypePath, TypeRegistration, Typed, }, }; use serde::{Deserialize, Serialize}; @@ -726,6 +727,16 @@ impl Struct for Attribute { } } +impl GetTypeRegistration for Attribute { + fn get_type_registration() -> TypeRegistration { + let mut registration = TypeRegistration::of::(); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); + registration + } +} + impl Reflect for Attribute { fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) @@ -793,6 +804,10 @@ impl Reflect for Attribute { fn reflect_owned(self: Box) -> ReflectOwned { ReflectOwned::Struct(self) } + + fn try_apply(&mut self, _value: &dyn Reflect) -> Result<(), bevy::reflect::ApplyError> { + todo!() + } } impl FromReflect for Attribute { diff --git a/src/gradient.rs b/src/gradient.rs index afa99828..c33d9e52 100644 --- a/src/gradient.rs +++ b/src/gradient.rs @@ -1,9 +1,8 @@ use std::hash::{Hash, Hasher}; use bevy::{ - math::{Quat, Vec2, Vec3, Vec3A, Vec4}, + math::{FloatOrd, Quat, Vec2, Vec3, Vec3A, Vec4}, reflect::{FromReflect, Reflect}, - utils::FloatOrd, }; use serde::{Deserialize, Serialize}; diff --git a/src/graph/expr.rs b/src/graph/expr.rs index d961beb9..b0ba01ad 100644 --- a/src/graph/expr.rs +++ b/src/graph/expr.rs @@ -104,8 +104,9 @@ use std::{cell::RefCell, num::NonZeroU32, rc::Rc}; -use bevy::{reflect::Reflect, utils::thiserror::Error}; +use bevy::reflect::Reflect; use serde::{Deserialize, Serialize}; +use thiserror::Error; use super::Value; use crate::{ diff --git a/src/graph/mod.rs b/src/graph/mod.rs index e4fa1ed4..ec974b0a 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -43,11 +43,10 @@ use std::fmt::Debug; use bevy::{ math::{ - BVec2, BVec3, BVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, UVec2, UVec3, UVec4, Vec2, - Vec3, Vec3A, Vec4, + BVec2, BVec3, BVec4, FloatOrd, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, UVec2, UVec3, UVec4, + Vec2, Vec3, Vec3A, Vec4, }, reflect::Reflect, - utils::FloatOrd, }; use serde::{Deserialize, Serialize}; diff --git a/src/lib.rs b/src/lib.rs index a5c6be18..6f5e6ab6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! _Note: This library makes heavy use of compute shaders to offload work to //! the GPU in a performant way. Support for compute shaders on the `wasm` //! target (WebAssembly) via WebGPU is only available in Bevy in general since -//! the newly-released Bevy v0.11, and is not yet available in this library. +//! the Bevy v0.11, and is not yet available in this library. //! See [#41](https://github.com/djeedai/bevy_hanabi/issues/41) for details on //! progress._ //! @@ -168,12 +168,10 @@ use std::fmt::Write as _; #[cfg(feature = "2d")] -use bevy::utils::FloatOrd; -use bevy::{ - prelude::*, - utils::{thiserror::Error, HashSet}, -}; +use bevy::math::FloatOrd; +use bevy::{prelude::*, utils::HashSet}; use serde::{Deserialize, Serialize}; +use thiserror::Error; mod asset; pub mod attributes; @@ -1478,10 +1476,7 @@ mod tests { }, AssetServerMode, }, - render::{ - deterministic::DeterministicRenderingConfig, - view::{VisibilityPlugin, VisibilitySystems}, - }, + render::view::{VisibilityPlugin, VisibilitySystems}, tasks::{IoTaskPool, TaskPoolBuilder}, }; use naga_oil::compose::{Composer, NagaModuleDescriptor, ShaderDefValue}; @@ -1703,7 +1698,7 @@ else { return c1; } let watch_for_changes = false; let mut builders = app - .world + .world_mut() .get_resource_or_insert_with::(Default::default); let dir = Dir::default(); let dummy_builder = AssetSourceBuilder::default() @@ -1717,7 +1712,6 @@ else { return c1; } // app.add_plugins(DefaultPlugins); app.init_asset::(); app.init_asset::(); - app.init_resource::(); app.add_plugins(VisibilityPlugin); app.init_resource::(); app.insert_resource(Random(new_rng())); @@ -1815,8 +1809,8 @@ else { return c1; } let mut dummy_app = App::new(); dummy_app.init_resource::>(); dummy_app.add_plugins(bevy::render::view::ViewPlugin); - let shaders = dummy_app.world.get_resource::>().unwrap(); - let view_shader = shaders.get(bevy::render::view::VIEW_TYPE_HANDLE).unwrap(); + let shaders = dummy_app.world().get_resource::>().unwrap(); + let view_shader = shaders.get(&bevy::render::view::VIEW_TYPE_HANDLE).unwrap(); let res = composer.add_composable_module(view_shader.into()); assert!(res.is_ok()); @@ -1876,7 +1870,7 @@ else { return c1; } let mut app = make_test_app(); let effect_entity = { - let world = &mut app.world; + let world = app.world_mut(); // Spawn particle effect let entity = world.spawn(ParticleEffectBundle::default()).id(); @@ -1892,7 +1886,7 @@ else { return c1; } // Check { - let world = &mut app.world; + let world = app.world_mut(); let (entity, particle_effect, compiled_particle_effect) = world .query::<(Entity, &ParticleEffect, &CompiledParticleEffect)>() @@ -1920,7 +1914,7 @@ else { return c1; } let mut app = make_test_app(); let (effect_entity, handle) = { - let world = &mut app.world; + let world = app.world_mut(); // Add effect asset let mut assets = world.resource_mut::>(); @@ -1950,7 +1944,7 @@ else { return c1; } // Check { - let world = &mut app.world; + let world = app.world_mut(); let (entity, particle_effect, compiled_particle_effect) = world .query::<(Entity, &ParticleEffect, &CompiledParticleEffect)>() @@ -1968,7 +1962,7 @@ else { return c1; } // Mark as changed without actually changing anything { - let world = &mut app.world; + let world = app.world_mut(); let mut particle_effect = world .query::<&mut ParticleEffect>() @@ -1985,7 +1979,7 @@ else { return c1; } // Check again, nothing changed { - let world = &mut app.world; + let world = app.world_mut(); let (entity, particle_effect, compiled_particle_effect) = world .query::<(Entity, &ParticleEffect, &CompiledParticleEffect)>() @@ -2014,7 +2008,7 @@ else { return c1; } let mut app = make_test_app(); let (effect_entity, handle) = { - let world = &mut app.world; + let world = app.world_mut(); // Add effect asset let mut assets = world.resource_mut::>(); @@ -2060,7 +2054,7 @@ else { return c1; } // Tick once app.update(); - let world = &mut app.world; + let world = app.world_mut(); // Check the state of the components after `tick_spawners()` ran if let Some(test_visibility) = test_case.visibility { diff --git a/src/modifier/clone.rs b/src/modifier/clone.rs index 5f63ad92..7bc9fa93 100644 --- a/src/modifier/clone.rs +++ b/src/modifier/clone.rs @@ -2,7 +2,7 @@ use std::hash::{Hash, Hasher}; -use bevy::{prelude::*, utils::FloatOrd}; +use bevy::{math::FloatOrd, prelude::*}; use serde::{Deserialize, Serialize}; use crate::{ diff --git a/src/modifier/mod.rs b/src/modifier/mod.rs index 88dce25d..b7118d5f 100644 --- a/src/modifier/mod.rs +++ b/src/modifier/mod.rs @@ -926,17 +926,35 @@ fn main() {{ let code = format!( r##" +struct ColorGrading {{ + balance: mat3x3, + saturation: vec3, + contrast: vec3, + gamma: vec3, + gain: vec3, + lift: vec3, + midtone_range: vec2, + exposure: f32, + hue: f32, + post_saturation: f32, +}} + struct View {{ - view_proj: mat4x4, - inverse_view_proj: mat4x4, - view: mat4x4, - inverse_view: mat4x4, - projection: mat4x4, - inverse_projection: mat4x4, + clip_from_world: mat4x4, + unjittered_clip_from_world: mat4x4, + world_from_clip: mat4x4, + world_from_view: mat4x4, + view_from_world: mat4x4, + clip_from_view: mat4x4, + view_from_clip: mat4x4, world_position: vec3, - width: f32, - height: f32, -}}; + exposure: f32, + // viewport(x_origin, y_origin, width, height) + viewport: vec4, + frustum: array, 6>, + color_grading: ColorGrading, + mip_bias: f32, +}} fn frand() -> f32 {{ return 0.0; }} fn get_camera_position_effect_space() -> vec3 {{ return vec3(); }} diff --git a/src/modifier/output.rs b/src/modifier/output.rs index 094aeefd..925531ac 100644 --- a/src/modifier/output.rs +++ b/src/modifier/output.rs @@ -428,7 +428,7 @@ axis_z = cam_rot[2].xyz; let particle_rot_in_cam_space = {}; let particle_rot_in_cam_space_cos = cos(particle_rot_in_cam_space); let particle_rot_in_cam_space_sin = sin(particle_rot_in_cam_space); -let axis_x0 = normalize(cross(view.view[1].xyz, axis_z)); +let axis_x0 = normalize(cross(view.world_from_view[1].xyz, axis_z)); let axis_y0 = cross(axis_z, axis_x0); axis_x = axis_x0 * particle_rot_in_cam_space_cos + axis_y0 * particle_rot_in_cam_space_sin; axis_y = axis_x0 * particle_rot_in_cam_space_sin - axis_y0 * particle_rot_in_cam_space_cos; @@ -437,7 +437,7 @@ axis_y = axis_x0 * particle_rot_in_cam_space_sin - axis_y0 * particle_rot_in_cam ); } else { context.vertex_code += r#"axis_z = normalize(get_camera_position_effect_space() - position); -axis_x = normalize(cross(view.view[1].xyz, axis_z)); +axis_x = normalize(cross(view.world_from_view[1].xyz, axis_z)); axis_y = cross(axis_z, axis_x); "#; } @@ -617,7 +617,7 @@ impl RenderModifier for ScreenSpaceSizeModifier { context.vertex_code += &format!( "let w_cs = transform_position_simulation_to_clip(particle.{0}).w;\n let screen_size_pixels = view.viewport.zw;\n - let projection_scale = vec2(view.projection[0][0], view.projection[1][1]);\n + let projection_scale = vec2(view.clip_from_view[0][0], view.clip_from_view[1][1]);\n size = (size * w_cs * 2.0) / min(screen_size_pixels.x * projection_scale.x, screen_size_pixels.y * projection_scale.y);\n", Attribute::POSITION.name()); } diff --git a/src/plugin.rs b/src/plugin.rs index 7a8055a9..a24ef079 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -9,10 +9,10 @@ use bevy::{ render_phase::DrawFunctions, render_resource::{SpecializedComputePipelines, SpecializedRenderPipelines}, renderer::{RenderAdapterInfo, RenderDevice}, - view::{prepare_view_uniforms, visibility::VisibilitySystems}, + view::{check_visibility, prepare_view_uniforms, visibility::VisibilitySystems}, Render, RenderApp, RenderSet, }, - time::{virtual_time_system, TimeSystem}, + time::{time_system, TimeSystem}, }; use crate::{ @@ -30,7 +30,8 @@ use crate::{ spawn::{self, Random}, tick_spawners, time::effect_simulation_time_system, - update_properties_from_asset, EffectSimulation, ParticleEffect, RemovedEffectsEvent, Spawner, + update_properties_from_asset, CompiledParticleEffect, EffectSimulation, ParticleEffect, + RemovedEffectsEvent, Spawner, }; /// Labels for the Hanabi systems. @@ -169,6 +170,10 @@ impl HanabiPlugin { } } +/// A convenient alias for `With>`, for use with +/// [`bevy_render::view::VisibleEntities`]. +pub type WithCompiledParticleEffect = With; + impl Plugin for HanabiPlugin { fn build(&self, app: &mut App) { // Register asset @@ -190,13 +195,13 @@ impl Plugin for HanabiPlugin { ), ) .configure_sets( - bevy::asset::UpdateAssets, + PreUpdate, EffectSystems::UpdatePropertiesFromAsset.after(bevy::asset::TrackAssets), ) .add_systems( First, effect_simulation_time_system - .after(virtual_time_system) + .after(time_system) .in_set(TimeSystem), ) .add_systems( @@ -206,6 +211,8 @@ impl Plugin for HanabiPlugin { compile_effects.in_set(EffectSystems::CompileEffects), update_properties_from_asset.in_set(EffectSystems::UpdatePropertiesFromAsset), gather_removed_effects.in_set(EffectSystems::GatherRemovedEffects), + check_visibility:: + .in_set(VisibilitySystems::CheckVisibility), ), ); @@ -220,12 +227,12 @@ impl Plugin for HanabiPlugin { fn finish(&self, app: &mut App) { let render_device = app .sub_app(RenderApp) - .world + .world() .resource::() .clone(); let adapter_name = app - .world + .world() .get_resource::() .map(|ai| &ai.name[..]) .unwrap_or(""); @@ -246,8 +253,8 @@ impl Plugin for HanabiPlugin { let common_shader = HanabiPlugin::make_common_shader( render_device.limits().min_storage_buffer_offset_alignment, ); - let mut assets = app.world.resource_mut::>(); - assets.insert(HANABI_COMMON_TEMPLATE_HANDLE, common_shader); + let mut assets = app.world_mut().resource_mut::>(); + assets.insert(&HANABI_COMMON_TEMPLATE_HANDLE, common_shader); } let effects_meta = EffectsMeta::new(render_device.clone()); @@ -303,9 +310,9 @@ impl Plugin for HanabiPlugin { // have been recorded). #[cfg(feature = "2d")] { - let draw_particles = DrawEffects::new(&mut render_app.world); + let draw_particles = DrawEffects::new(render_app.world_mut()); render_app - .world + .world() .get_resource::>() .unwrap() .write() @@ -313,17 +320,17 @@ impl Plugin for HanabiPlugin { } #[cfg(feature = "3d")] { - let draw_particles = DrawEffects::new(&mut render_app.world); + let draw_particles = DrawEffects::new(render_app.world_mut()); render_app - .world + .world() .get_resource::>() .unwrap() .write() .add(draw_particles); - let draw_particles = DrawEffects::new(&mut render_app.world); + let draw_particles = DrawEffects::new(render_app.world_mut()); render_app - .world + .world() .get_resource::>() .unwrap() .write() @@ -333,9 +340,12 @@ impl Plugin for HanabiPlugin { // Add the simulation sub-graph. This render graph runs once per frame no matter // how many cameras/views are active (view-independent). let mut simulate_graph = RenderGraph::default(); - let simulate_node = VfxSimulateNode::new(&mut render_app.world); + let simulate_node = VfxSimulateNode::new(render_app.world_mut()); simulate_graph.add_node(simulate_graph::node::HanabiSimulateNode, simulate_node); - let mut graph = render_app.world.get_resource_mut::().unwrap(); + let mut graph = render_app + .world_mut() + .get_resource_mut::() + .unwrap(); graph.add_sub_graph(simulate_graph::HanabiSimulateGraph, simulate_graph); // Add the simulation driver node which executes the simulation sub-graph. It diff --git a/src/properties.rs b/src/properties.rs index e86b7ef2..19dd3374 100644 --- a/src/properties.rs +++ b/src/properties.rs @@ -48,7 +48,7 @@ //! # use bevy_hanabi::*; //! # use bevy::prelude::*; //! let mut module = Module::default(); -//! module.add_property("my_color", Color::WHITE.as_rgba_u32().into()); +//! module.add_property("my_color", LinearRgba::WHITE.as_u32().into()); //! ``` //! //! Once the module is assigned to an [`EffectAsset`], any instance of that @@ -79,8 +79,8 @@ //! # use bevy::prelude::*; //! fn change_property(mut query: Query<&mut EffectProperties>) { //! let mut effect_properties = query.single_mut(); -//! let color = Color::RED.as_rgba_u32(); -//! // If the current color is not already Color::RED, it will be updated, and +//! let color = LinearRgba::rgb(1., 0., 0.).as_u32(); +//! // If the current color is not already red, it will be updated, and //! // the properties will be re-uploaded to the GPU. //! EffectProperties::set_if_changed(effect_properties, "my_color", color.into()); //! } diff --git a/src/render/aligned_buffer_vec.rs b/src/render/aligned_buffer_vec.rs index 34c08aef..2b584b2a 100644 --- a/src/render/aligned_buffer_vec.rs +++ b/src/render/aligned_buffer_vec.rs @@ -1,7 +1,6 @@ use std::num::NonZeroU64; use bevy::{ - core::{cast_slice, Pod}, log::trace, render::{ render_resource::{ @@ -10,6 +9,7 @@ use bevy::{ renderer::{RenderDevice, RenderQueue}, }, }; +use bytemuck::{cast_slice, Pod}; use copyless::VecHelper; use crate::next_multiple_of; diff --git a/src/render/batch.rs b/src/render/batch.rs index 57750029..2a72c725 100644 --- a/src/render/batch.rs +++ b/src/render/batch.rs @@ -1,7 +1,7 @@ use std::ops::{Index, Range}; #[cfg(feature = "2d")] -use bevy::utils::FloatOrd; +use bevy::math::FloatOrd; use bevy::{ prelude::*, render::render_resource::{Buffer, CachedComputePipelineId}, diff --git a/src/render/buffer_table.rs b/src/render/buffer_table.rs index ebd7afd2..f0dd1ee9 100644 --- a/src/render/buffer_table.rs +++ b/src/render/buffer_table.rs @@ -1,7 +1,6 @@ use std::num::NonZeroU64; use bevy::{ - core::{cast_slice, Pod}, log::trace, render::{ render_resource::{ @@ -11,6 +10,7 @@ use bevy::{ renderer::{RenderDevice, RenderQueue}, }, }; +use bytemuck::{cast_slice, Pod}; use copyless::VecHelper; use crate::next_multiple_of; diff --git a/src/render/mod.rs b/src/render/mod.rs index bf2ca37a..c5a48b05 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -6,12 +6,17 @@ use std::{iter, marker::PhantomData}; #[cfg(feature = "2d")] use bevy::core_pipeline::core_2d::Transparent2d; -#[cfg(feature = "3d")] -use bevy::core_pipeline::core_3d::{AlphaMask3d, Transparent3d}; #[cfg(feature = "2d")] -use bevy::utils::FloatOrd; +use bevy::math::FloatOrd; +#[cfg(feature = "3d")] +use bevy::{ + core_pipeline::{ + core_3d::{AlphaMask3d, Transparent3d}, + prepass::OpaqueNoLightmap3dBinKey, + }, + render::render_phase::{BinnedPhaseItem, ViewBinnedRenderPhases}, +}; use bevy::{ - core::{Pod, Zeroable}, ecs::{ prelude::*, system::{lifetimeless::*, SystemParam, SystemState}, @@ -21,10 +26,13 @@ use bevy::{ render::{ render_asset::RenderAssets, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo}, - render_phase::{Draw, DrawFunctions, PhaseItem, RenderPhase, TrackedRenderPass}, + render_phase::{ + Draw, DrawFunctions, PhaseItemExtraIndex, SortedPhaseItem, TrackedRenderPass, + ViewSortedRenderPhases, + }, render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, - texture::BevyDefault, + texture::{BevyDefault, GpuImage}, view::{ ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities, @@ -34,6 +42,7 @@ use bevy::{ utils::HashMap, }; use bitflags::bitflags; +use bytemuck::{Pod, Zeroable}; use fixedbitset::FixedBitSet; use naga_oil::compose::{Composer, NagaModuleDescriptor}; use rand::random; @@ -41,6 +50,7 @@ use rand::random; use crate::{ asset::EffectAsset, next_multiple_of, + plugin::WithCompiledParticleEffect, render::{ batch::{BatchesInput, EffectDrawBatch}, effect_cache::DispatchBufferIndices, @@ -342,7 +352,6 @@ pub(crate) struct DispatchIndirectPipeline { impl FromWorld for DispatchIndirectPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let storage_alignment = render_device.limits().min_storage_buffer_offset_alignment; @@ -497,6 +506,7 @@ impl FromWorld for DispatchIndirectPipeline { layout: Some(&pipeline_layout), module: &shader_module, entry_point: "main", + compilation_options: default(), }); Self { @@ -517,7 +527,6 @@ pub(crate) struct ParticlesInitPipeline { impl FromWorld for ParticlesInitPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let limits = render_device.limits(); @@ -718,7 +727,6 @@ pub(crate) struct ParticlesUpdatePipeline { impl FromWorld for ParticlesUpdatePipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let limits = render_device.limits(); @@ -917,7 +925,6 @@ pub(crate) struct ParticlesRenderPipeline { impl FromWorld for ParticlesRenderPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.get_resource::().unwrap(); let view_layout = render_device.create_bind_group_layout( @@ -1586,7 +1593,7 @@ pub(crate) fn extract_effects( /// GPU representation of a single vertex of a particle mesh stored in a GPU /// buffer. #[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] +#[derive(Copy, Clone, Pod, Zeroable, ShaderType)] struct GpuParticleVertex { /// Vertex position. pub position: [f32; 3], @@ -2398,8 +2405,9 @@ pub struct QueueEffectsReadOnlyParams<'w, 's> { marker: PhantomData<&'s usize>, } -fn emit_draw( - views: &mut Query<(&mut RenderPhase, &VisibleEntities, &ExtractedView)>, +fn emit_sorted_draw( + views: &Query<(Entity, &VisibleEntities, &ExtractedView)>, + render_phases: &mut ResMut>, view_entities: &mut FixedBitSet, effect_batches: &Query<(Entity, &mut EffectBatches)>, effect_draw_batches: &Query<(Entity, &mut EffectDrawBatch)>, @@ -2409,20 +2417,29 @@ fn emit_draw( msaa_samples: u32, make_phase_item: F, #[cfg(all(feature = "2d", feature = "3d"))] pipeline_mode: PipelineMode, - use_alpha_mask: bool, ) where - T: PhaseItem, + T: SortedPhaseItem, F: Fn(CachedRenderPipelineId, Entity, &EffectDrawBatch, u32, &ExtractedView) -> T, { - for (mut render_phase, visible_entities, view) in views.iter_mut() { - trace!("Process new view (use_alpha_mask={})", use_alpha_mask); + trace!("emit_sorted_draw() {} views", views.iter().len()); + + for (view_entity, visible_entities, view) in views.iter() { + trace!("Process new sorted view"); + + let Some(render_phase) = render_phases.get_mut(&view_entity) else { + continue; + }; { #[cfg(feature = "trace")] let _span = bevy::utils::tracing::info_span!("collect_view_entities").entered(); view_entities.clear(); - view_entities.extend(visible_entities.entities.iter().map(|e| e.index() as usize)); + view_entities.extend( + visible_entities + .iter::() + .map(|e| e.index() as usize), + ); } // For each view, loop over all the effect batches to determine if the effect @@ -2453,7 +2470,8 @@ fn emit_draw( batches.layout_flags, ); - if use_alpha_mask != batches.layout_flags.contains(LayoutFlags::USE_ALPHA_MASK) { + // AlphaMask is a binned draw, so no sorted draw can possibly use it + if batches.layout_flags.contains(LayoutFlags::USE_ALPHA_MASK) { continue; } @@ -2552,23 +2570,178 @@ fn emit_draw( } } +#[cfg(feature = "3d")] +fn emit_binned_draw( + views: &Query<(Entity, &VisibleEntities, &ExtractedView)>, + render_phases: &mut ResMut>, + view_entities: &mut FixedBitSet, + effect_batches: &Query<(Entity, &mut EffectBatches)>, + effect_draw_batches: &Query<(Entity, &mut EffectDrawBatch)>, + read_params: &QueueEffectsReadOnlyParams, + mut specialized_render_pipelines: Mut>, + pipeline_cache: &PipelineCache, + msaa_samples: u32, + make_bin_key: F, + #[cfg(all(feature = "2d", feature = "3d"))] pipeline_mode: PipelineMode, + use_alpha_mask: bool, +) where + T: BinnedPhaseItem, + F: Fn(CachedRenderPipelineId, &EffectDrawBatch, u32, &ExtractedView) -> T::BinKey, +{ + use bevy::render::render_phase::BinnedRenderPhaseType; + + trace!("emit_binned_draw() {} views", views.iter().len()); + + for (view_entity, visible_entities, view) in views.iter() { + trace!( + "Process new binned view (use_alpha_mask={})", + use_alpha_mask + ); + + let Some(render_phase) = render_phases.get_mut(&view_entity) else { + continue; + }; + + { + #[cfg(feature = "trace")] + let _span = bevy::utils::tracing::info_span!("collect_view_entities").entered(); + + view_entities.clear(); + view_entities.extend( + visible_entities + .iter::() + .map(|e| e.index() as usize), + ); + } + + // For each view, loop over all the effect batches to determine if the effect + // needs to be rendered for that view, and enqueue a view-dependent + // batch if so. + for (draw_entity, draw_batch) in effect_draw_batches.iter() { + #[cfg(feature = "trace")] + let _span_draw = bevy::utils::tracing::info_span!("draw_batch").entered(); + + trace!( + "Process draw batch: draw_entity={:?} group_index={} batches_entity={:?}", + draw_entity, + draw_batch.group_index, + draw_batch.batches_entity, + ); + + // Get the EffectBatches this EffectDrawBatch is part of. + let Ok((batches_entity, batches)) = effect_batches.get(draw_batch.batches_entity) + else { + continue; + }; + + trace!( + "-> EffectBaches: entity={:?} buffer_index={} spawner_base={} layout_flags={:?}", + batches_entity, + batches.buffer_index, + batches.spawner_base, + batches.layout_flags, + ); + + if use_alpha_mask != batches.layout_flags.contains(LayoutFlags::USE_ALPHA_MASK) { + continue; + } + + // Check if batch contains any entity visible in the current view. Otherwise we + // can skip the entire batch. Note: This is O(n^2) but (unlike + // the Sprite renderer this is inspired from) we don't expect more than + // a handful of particle effect instances, so would rather not pay the memory + // cost of a FixedBitSet for the sake of an arguable speed-up. + // TODO - Profile to confirm. + #[cfg(feature = "trace")] + let _span_check_vis = bevy::utils::tracing::info_span!("check_visibility").entered(); + let has_visible_entity = batches + .entities + .iter() + .any(|index| view_entities.contains(*index as usize)); + if !has_visible_entity { + trace!("No visible entity for view, not emitting any draw call."); + continue; + } + #[cfg(feature = "trace")] + _span_check_vis.exit(); + + // FIXME - We draw the entire batch, but part of it may not be visible in this + // view! We should re-batch for the current view specifically! + + let local_space_simulation = batches + .layout_flags + .contains(LayoutFlags::LOCAL_SPACE_SIMULATION); + let use_alpha_mask = batches.layout_flags.contains(LayoutFlags::USE_ALPHA_MASK); + let flipbook = batches.layout_flags.contains(LayoutFlags::FLIPBOOK); + let needs_uv = batches.layout_flags.contains(LayoutFlags::NEEDS_UV); + let has_image = batches.layout_flags.contains(LayoutFlags::PARTICLE_TEXTURE); + + // Specialize the render pipeline based on the effect batch + trace!( + "Specializing render pipeline: render_shaders={:?} has_image={:?} use_alpha_mask={:?} flipbook={:?} hdr={}", + batches.render_shaders, + has_image, + use_alpha_mask, + flipbook, + view.hdr + ); + + // Add a draw pass for the effect batch + trace!("Emitting individual draws for batches and groups: group_batches.len()={} batches.render_shaders.len()={}", batches.group_batches.len(), batches.render_shaders.len()); + let render_shader_source = &batches.render_shaders[draw_batch.group_index as usize]; + trace!("Emit for group index #{}", draw_batch.group_index); + + let alpha_mode = batches.alpha_mode; + + #[cfg(feature = "trace")] + let _span_specialize = bevy::utils::tracing::info_span!("specialize").entered(); + let render_pipeline_id = specialized_render_pipelines.specialize( + pipeline_cache, + &read_params.render_pipeline, + ParticleRenderPipelineKey { + shader: render_shader_source.clone(), + particle_layout: batches.particle_layout.clone(), + has_image, + local_space_simulation, + use_alpha_mask, + alpha_mode, + flipbook, + needs_uv, + #[cfg(all(feature = "2d", feature = "3d"))] + pipeline_mode, + msaa_samples, + hdr: view.hdr, + }, + ); + #[cfg(feature = "trace")] + _span_specialize.exit(); + + trace!( + "+ Render pipeline specialized: id={:?} -> group_index={}", + render_pipeline_id, + draw_batch.group_index + ); + trace!( + "+ Add Transparent for batch on draw_entity {:?}: buffer_index={} \ + group_index={} spawner_base={} handle={:?}", + draw_entity, + batches.buffer_index, + draw_batch.group_index, + batches.spawner_base, + batches.handle + ); + render_phase.add( + make_bin_key(render_pipeline_id, draw_batch, draw_batch.group_index, view), + draw_entity, + BinnedRenderPhaseType::NonMesh, + ); + } + } +} + #[allow(clippy::too_many_arguments)] pub(crate) fn queue_effects( - #[cfg(feature = "2d")] mut views_2d: Query<( - &mut RenderPhase, - &VisibleEntities, - &ExtractedView, - )>, - #[cfg(feature = "3d")] mut views_3d: Query<( - &mut RenderPhase, - &VisibleEntities, - &ExtractedView, - )>, - #[cfg(feature = "3d")] mut views_alpha_mask: Query<( - &mut RenderPhase, - &VisibleEntities, - &ExtractedView, - )>, + views: Query<(Entity, &VisibleEntities, &ExtractedView)>, effects_meta: Res, mut specialized_render_pipelines: ResMut>, pipeline_cache: Res, @@ -2579,6 +2752,15 @@ pub(crate) fn queue_effects( read_params: QueueEffectsReadOnlyParams, msaa: Res, mut view_entities: Local, + #[cfg(feature = "2d")] mut transparent_2d_render_phases: ResMut< + ViewSortedRenderPhases, + >, + #[cfg(feature = "3d")] mut transparent_3d_render_phases: ResMut< + ViewSortedRenderPhases, + >, + #[cfg(feature = "3d")] mut alpha_mask_3d_render_phases: ResMut< + ViewBinnedRenderPhases, + >, ) { #[cfg(feature = "trace")] let _span = bevy::utils::tracing::info_span!("hanabi:queue_effects").entered(); @@ -2620,10 +2802,11 @@ pub(crate) fn queue_effects( .unwrap(); // Effects with full alpha blending - if !views_2d.is_empty() { + if !views.is_empty() { trace!("Emit effect draw calls for alpha blended 2D views..."); - emit_draw( - &mut views_2d, + emit_sorted_draw( + &views, + &mut transparent_2d_render_phases, &mut view_entities, &effect_batches, &effect_draw_batches, @@ -2637,11 +2820,10 @@ pub(crate) fn queue_effects( entity, sort_key: draw_batch.z_sort_key_2d, batch_range: 0..1, - dynamic_offset: None, + extra_index: PhaseItemExtraIndex::NONE, }, #[cfg(feature = "3d")] PipelineMode::Camera2d, - false, ); } } @@ -2653,7 +2835,7 @@ pub(crate) fn queue_effects( let _span_draw = bevy::utils::tracing::info_span!("draw_3d").entered(); // Effects with full alpha blending - if !views_3d.is_empty() { + if !views.is_empty() { trace!("Emit effect draw calls for alpha blended 3D views..."); let draw_effects_function_3d = read_params @@ -2662,8 +2844,9 @@ pub(crate) fn queue_effects( .get_id::() .unwrap(); - emit_draw( - &mut views_3d, + emit_sorted_draw( + &views, + &mut transparent_3d_render_phases, &mut view_entities, &effect_batches, &effect_draw_batches, @@ -2679,16 +2862,15 @@ pub(crate) fn queue_effects( .rangefinder3d() .distance_translation(&batch.translation_3d), batch_range: 0..1, - dynamic_offset: None, + extra_index: PhaseItemExtraIndex::NONE, }, #[cfg(feature = "2d")] PipelineMode::Camera3d, - false, ); } // Effects with alpha mask - if !views_alpha_mask.is_empty() { + if !views.is_empty() { #[cfg(feature = "trace")] let _span_draw = bevy::utils::tracing::info_span!("draw_alphamask").entered(); @@ -2700,8 +2882,9 @@ pub(crate) fn queue_effects( .get_id::() .unwrap(); - emit_draw( - &mut views_alpha_mask, + emit_binned_draw( + &views, + &mut alpha_mask_3d_render_phases, &mut view_entities, &effect_batches, &effect_draw_batches, @@ -2709,15 +2892,17 @@ pub(crate) fn queue_effects( specialized_render_pipelines.reborrow(), &pipeline_cache, msaa.samples(), - |id, entity, batch, _group, view| AlphaMask3d { - draw_function: draw_effects_function_alpha_mask, + |id, _batch, _group, _view| OpaqueNoLightmap3dBinKey { pipeline: id, - entity, - distance: view - .rangefinder3d() - .distance_translation(&batch.translation_3d), - batch_range: 0..1, - dynamic_offset: None, + draw_function: draw_effects_function_alpha_mask, + asset_id: AssetId::::default().untyped(), + material_bind_group_id: None, + // }, + // distance: view + // .rangefinder3d() + // .distance_translation(&batch.translation_3d), + // batch_range: 0..1, + //extra_index: PhaseItemExtraIndex::NONE, }, #[cfg(feature = "2d")] PipelineMode::Camera3d, @@ -2771,7 +2956,7 @@ pub(crate) fn prepare_bind_groups( init_pipeline: Res, update_pipeline: Res, render_pipeline: Res, - gpu_images: Res>, + gpu_images: Res>, ) { if effects_meta.spawner_buffer.is_empty() || effects_meta.spawner_buffer.buffer().is_none() { return; @@ -3314,8 +3499,8 @@ impl Draw for DrawEffects { world, pass, view, - item.entity, - item.pipeline, + item.representative_entity, + item.key.pipeline, &mut self.params, ); } diff --git a/src/render/vfx_render.wgsl b/src/render/vfx_render.wgsl index 3cbfcce4..4b01ee4b 100644 --- a/src/render/vfx_render.wgsl +++ b/src/render/vfx_render.wgsl @@ -39,7 +39,7 @@ struct VertexOutput { // #endif fn get_camera_position_effect_space() -> vec3 { - let view_pos = view.view[3].xyz; + let view_pos = view.world_from_view[3].xyz; #ifdef LOCAL_SPACE_SIMULATION let inverse_transform = transpose( mat3x3( @@ -55,7 +55,7 @@ fn get_camera_position_effect_space() -> vec3 { } fn get_camera_rotation_effect_space() -> mat3x3 { - let view_rot = mat3x3(view.view[0].xyz, view.view[1].xyz, view.view[2].xyz); + let view_rot = mat3x3(view.world_from_view[0].xyz, view.world_from_view[1].xyz, view.world_from_view[2].xyz); #ifdef LOCAL_SPACE_SIMULATION let inverse_transform = transpose( mat3x3( @@ -102,7 +102,7 @@ fn transform_position_simulation_to_world(sim_position: vec3) -> vec4 /// The clip space is the final [-1:1]^3 space output from the vertex shader, before /// perspective divide and viewport transform are applied. fn transform_position_simulation_to_clip(sim_position: vec3) -> vec4 { - return view.view_proj * transform_position_simulation_to_world(sim_position); + return view.clip_from_world * transform_position_simulation_to_world(sim_position); } {{RENDER_EXTRA}} diff --git a/src/spawn.rs b/src/spawn.rs index 9943a476..f094e272 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -1,6 +1,6 @@ use std::hash::{Hash, Hasher}; -use bevy::{ecs::system::Resource, prelude::*, reflect::Reflect, utils::FloatOrd}; +use bevy::{ecs::system::Resource, math::FloatOrd, prelude::*, reflect::Reflect}; use rand::{ distributions::{uniform::SampleUniform, Distribution, Uniform}, SeedableRng, @@ -678,10 +678,7 @@ mod test { }, AssetServerMode, }, - render::{ - deterministic::DeterministicRenderingConfig, - view::{VisibilityPlugin, VisibilitySystems}, - }, + render::view::{VisibilityPlugin, VisibilitySystems}, tasks::{IoTaskPool, TaskPoolBuilder}, }; @@ -851,7 +848,7 @@ mod test { let watch_for_changes = false; let mut builders = app - .world + .world_mut() .get_resource_or_insert_with::(Default::default); let dir = Dir::default(); let dummy_builder = AssetSourceBuilder::default() @@ -864,7 +861,6 @@ mod test { app.insert_resource(asset_server); // app.add_plugins(DefaultPlugins); app.init_asset::(); - app.init_resource::(); app.add_plugins(VisibilityPlugin); app.init_resource::>(); app.insert_resource(Random(new_rng())); @@ -908,7 +904,7 @@ mod test { let mut app = make_test_app(); let (effect_entity, handle) = { - let world = &mut app.world; + let world = app.world_mut(); // Add effect asset let mut assets = world.resource_mut::>(); @@ -956,13 +952,13 @@ mod test { // Note that `Time` has this weird behavior where the common quantities like // `Time::delta_seconds()` only update after the *second* update. So we tick the // `Time` twice here to enforce this. - let mut time = app.world.resource_mut::>(); + let mut time = app.world_mut().resource_mut::>(); time.advance_by(Duration::from_millis(16)); time.elapsed() }; app.update(); - let world = &mut app.world; + let world = app.world_mut(); // Check the state of the components after `tick_spawners()` ran if let Some(test_visibility) = test_case.visibility { diff --git a/src/test_utils.rs b/src/test_utils.rs index 24ff8006..b34441b6 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -2,7 +2,7 @@ use std::ops::Sub; use bevy::prelude::{Quat, Vec2, Vec3, Vec4}; #[cfg(feature = "gpu_tests")] -use bevy::render::renderer::{RenderDevice, RenderQueue}; +use bevy::render::renderer::{RenderDevice, RenderQueue, WgpuWrapper}; /// Utility trait to compare floating-point values with a tolerance. pub(crate) trait AbsDiffEq { @@ -185,7 +185,7 @@ impl MockRenderer { // Turn into Bevy objects let device = RenderDevice::from(device); - let queue = RenderQueue(std::sync::Arc::new(queue)); + let queue = RenderQueue(std::sync::Arc::new(WgpuWrapper::new(queue))); Self { instance, diff --git a/src/time.rs b/src/time.rs index b1cafa5f..bfdfedbf 100644 --- a/src/time.rs +++ b/src/time.rs @@ -185,7 +185,7 @@ pub(crate) fn effect_simulation_time_system( mod tests { use std::{thread::sleep, time::Duration}; - use bevy::time::{virtual_time_system, TimePlugin, TimeSystem}; + use bevy::time::{time_system, TimePlugin, TimeSystem}; use super::*; @@ -197,7 +197,7 @@ mod tests { app.add_systems( First, effect_simulation_time_system - .after(virtual_time_system) + .after(time_system) .in_set(TimeSystem), ); @@ -218,35 +218,35 @@ mod tests { // Update with default speed sleep(Duration::from_millis(1)); app.update(); - let real = app.world.resource::>(); - let virt = app.world.resource::>(); - let effect_simulation = app.world.resource::>(); + let real = app.world().resource::>(); + let virt = app.world().resource::>(); + let effect_simulation = app.world().resource::>(); assert!(f32::abs(virt.delta_seconds() - real.delta_seconds()) < EPSILON); assert!(f32::abs(effect_simulation.delta_seconds() - real.delta_seconds()) < EPSILON); // Update with virtual speed 2.0 - app.world + app.world_mut() .resource_mut::>() .set_relative_speed(2.0); sleep(Duration::from_millis(1)); app.update(); - let real = app.world.resource::>(); - let virt = app.world.resource::>(); - let effect_simulation = app.world.resource::>(); + let real = app.world().resource::>(); + let virt = app.world().resource::>(); + let effect_simulation = app.world().resource::>(); assert!(f32::abs(virt.delta_seconds() - 2.0 * real.delta_seconds()) < EPSILON); assert!(f32::abs(effect_simulation.delta_seconds() - 2.0 * real.delta_seconds()) < EPSILON); assert!(f32::abs(virt.effective_speed() - 2.0) < EPSILON); assert!(f32::abs(effect_simulation.effective_speed() - 2.0) < EPSILON); // Update with virtual speed 2.0 and effect speed 3.0 - app.world + app.world_mut() .resource_mut::>() .set_relative_speed(3.0); sleep(Duration::from_millis(1)); app.update(); - let real = app.world.resource::>(); - let virt = app.world.resource::>(); - let effect_simulation = app.world.resource::>(); + let real = app.world().resource::>(); + let virt = app.world().resource::>(); + let effect_simulation = app.world().resource::>(); assert!(f32::abs(virt.delta_seconds() - 2.0 * real.delta_seconds()) < EPSILON); assert!(f32::abs(effect_simulation.delta_seconds() - 6.0 * real.delta_seconds()) < EPSILON); assert!(f32::abs(virt.effective_speed() - 2.0) < EPSILON);