diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfda202..e1745aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +- Added `RotateOverTimeModifier` + ### Changed - Batch same-effect instances. This greatly improves performance when using many instances diff --git a/Cargo.toml b/Cargo.toml index 7a85a9d5..2df0ae4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,9 @@ wgpu = { version = "29.0.3", default-features = false, features = [ "fragile-send-sync-non-atomic-wasm", ] } naga = { version = "29.0.3", features = ["wgsl-in"] } -naga_oil = { version = "0.22", default-features = false, features = ["test_shader"] } +naga_oil = { version = "0.22", default-features = false, features = [ + "test_shader", +] } # getrandom requires both 'wasm_js' as a feature AND as a RUSTFLAGS to work on wasm [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -165,6 +167,9 @@ name = "puffs" [[example]] name = "lightning" +[[example]] +name = "rotate_over_time" + [[test]] name = "empty_effect" path = "gpu_tests/empty_effect.rs" diff --git a/examples/rotate_over_time.rs b/examples/rotate_over_time.rs new file mode 100644 index 00000000..336ebb65 --- /dev/null +++ b/examples/rotate_over_time.rs @@ -0,0 +1,103 @@ +//! An example using the [`RotateOverTimeModifier`]. + +use std::f32::consts::FRAC_PI_2; + +use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*}; +use bevy_hanabi::prelude::*; + +mod utils; +use utils::*; + +const DEMO_DESC: &str = include_str!("rotate_over_time.txt"); +const COLOR: Vec4 = Vec4::new(0.7, 0.7, 1.0, 1.0); +const SIZE: Vec3 = Vec3::splat(0.1); + +fn main() -> Result<(), Box> { + let app_exit = utils::DemoApp::new("box") + .with_desc(DEMO_DESC) + .build() + .add_systems(Startup, setup) + .add_systems(Update, rotate_camera) + .run(); + app_exit.into_result() +} + +fn setup( + mut commands: Commands, + mut effects: ResMut>, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.spawn(( + Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y), + Camera3d::default(), + Projection::Perspective(PerspectiveProjection { + fov: 120.0, + ..default() + }), + Tonemapping::None, + )); + + // The ground + commands.spawn(( + Transform::from_xyz(0.0, -0.5, 0.0) + * Transform::from_rotation(Quat::from_rotation_x(-FRAC_PI_2)), + Mesh3d(meshes.add(Rectangle { + half_size: Vec2::splat(2.0), + })), + MeshMaterial3d(materials.add(utils::COLOR_BLUE)), + Name::new("ground"), + )); + + let writer = ExprWriter::new(); + + let init_pos = SetPositionCircleModifier { + center: writer.lit(Vec3::Y * 0.1).expr(), + axis: writer.lit(Vec3::Y).expr(), + radius: writer.lit(1.).expr(), + dimension: ShapeDimension::Volume, + }; + + let init_axis_x = SetAttributeModifier::new(Attribute::AXIS_X, writer.lit(Vec3::X).expr()); + let init_axis_y = SetAttributeModifier::new(Attribute::AXIS_Y, writer.lit(Vec3::Y).expr()); + let init_axis_z = SetAttributeModifier::new(Attribute::AXIS_Z, writer.lit(Vec3::Z).expr()); + + // Particle will complete 3/4 of a rotation around Y axis per second + let rotate_over_time = RotateOverTimeModifier { + rotation: writer + .lit(Vec3::new(0., 270.0f32.to_radians(), 190.0f32.to_radians())) + .expr(), + }; + + let module = writer.finish(); + + let effect = effects.add( + EffectAsset::new(32768, SpawnerSettings::once(64.0.into()), module) + .with_name("rotate_over_time") + // Disable motion integration; in this demo particles don't move. This silences some warning + // about missing the VELOCITY attribute. + .with_motion_integration(MotionIntegration::None) + .with_simulation_space(SimulationSpace::Local) + .init(init_pos) + .init(init_axis_x) + .init(init_axis_y) + .init(init_axis_z) + .update(rotate_over_time) + .render(SetColorModifier::new(COLOR)) + .render(SetSizeModifier { size: SIZE.into() }), + ); + + commands.spawn(( + Transform::from_translation(Vec3::new(0., 1., 0.)), + ParticleEffect::new(effect), + Name::new("Rotate Over Time"), + )); +} + +fn rotate_camera(time: Res