diff --git a/Cargo.toml b/Cargo.toml index d057f022006a8..476c27e0977be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -330,6 +330,7 @@ bevy_internal = { path = "crates/bevy_internal", version = "0.14.0-dev", default [dev-dependencies] rand = "0.8.0" +rand_chacha = "0.3.1" ron = "0.8.0" flate2 = "1.0" serde = { version = "1", features = ["derive"] } diff --git a/crates/bevy_animation/src/animatable.rs b/crates/bevy_animation/src/animatable.rs index 201b8e69192b7..39de5faa2b6b6 100644 --- a/crates/bevy_animation/src/animatable.rs +++ b/crates/bevy_animation/src/animatable.rs @@ -1,5 +1,5 @@ use crate::util; -use bevy_color::{ClampColor, Laba, LinearRgba, Oklaba, Xyza}; +use bevy_color::{ClampColor, Laba, LinearRgba, Oklaba, Srgba, Xyza}; use bevy_ecs::world::World; use bevy_math::*; use bevy_reflect::Reflect; @@ -96,6 +96,7 @@ impl_float_animatable!(DVec4, f64); impl_color_animatable!(LinearRgba); impl_color_animatable!(Laba); impl_color_animatable!(Oklaba); +impl_color_animatable!(Srgba); impl_color_animatable!(Xyza); // Vec3 is special cased to use Vec3A internally for blending diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 10f567f2f211c..7b57f321e07e1 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -6,6 +6,7 @@ use bevy_ecs::{ common_conditions::run_once as run_once_condition, run_enter_schedule, InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel, }, + system::SystemId, }; use bevy_utils::{intern::Interned, tracing::debug, HashMap, HashSet}; use std::{ @@ -453,6 +454,22 @@ impl App { self } + /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`]. + /// + /// It's possible to register the same systems more than once, they'll be stored separately. + /// + /// This is different from adding systems to a [`Schedule`] with [`App::add_systems`], + /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system. + /// This allows for running systems in a push-based fashion. + /// Using a [`Schedule`] is still preferred for most cases + /// due to its better performance and ability to run non-conflicting systems simultaneously. + pub fn register_system + 'static>( + &mut self, + system: S, + ) -> SystemId { + self.world.register_system(system) + } + /// Configures a collection of system sets in the provided schedule, adding any sets that do not exist. #[track_caller] pub fn configure_sets( diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index d14c325973c77..40bdac7e393b2 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -249,13 +249,6 @@ impl PartialEq for Handle { impl Eq for Handle {} -impl From> for AssetId { - #[inline] - fn from(value: Handle) -> Self { - value.id() - } -} - impl From<&Handle> for AssetId { #[inline] fn from(value: &Handle) -> Self { @@ -263,13 +256,6 @@ impl From<&Handle> for AssetId { } } -impl From> for UntypedAssetId { - #[inline] - fn from(value: Handle) -> Self { - value.id().into() - } -} - impl From<&Handle> for UntypedAssetId { #[inline] fn from(value: &Handle) -> Self { @@ -429,13 +415,6 @@ impl PartialOrd for UntypedHandle { } } -impl From for UntypedAssetId { - #[inline] - fn from(value: UntypedHandle) -> Self { - value.id() - } -} - impl From<&UntypedHandle> for UntypedAssetId { #[inline] fn from(value: &UntypedHandle) -> Self { diff --git a/crates/bevy_asset/src/io/embedded/mod.rs b/crates/bevy_asset/src/io/embedded/mod.rs index b953e1e669f08..d8f85c315cbe6 100644 --- a/crates/bevy_asset/src/io/embedded/mod.rs +++ b/crates/bevy_asset/src/io/embedded/mod.rs @@ -254,7 +254,7 @@ pub fn watched_path(_source_file_path: &'static str, _asset_path: &'static str) macro_rules! load_internal_asset { ($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{ let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); - assets.insert($handle, ($loader)( + assets.insert($handle.id(), ($loader)( include_str!($path_str), std::path::Path::new(file!()) .parent() @@ -266,7 +266,7 @@ macro_rules! load_internal_asset { // we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded ($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{ let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); - assets.insert($handle, ($loader)( + assets.insert($handle.id(), ($loader)( include_str!($path_str), std::path::Path::new(file!()) .parent() @@ -284,7 +284,7 @@ macro_rules! load_internal_binary_asset { ($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{ let mut assets = $app.world.resource_mut::<$crate::Assets<_>>(); assets.insert( - $handle, + $handle.id(), ($loader)( include_bytes!($path_str).as_ref(), std::path::Path::new(file!()) diff --git a/crates/bevy_asset/src/reflect.rs b/crates/bevy_asset/src/reflect.rs index dd95df8525bd6..41d8911270026 100644 --- a/crates/bevy_asset/src/reflect.rs +++ b/crates/bevy_asset/src/reflect.rs @@ -149,7 +149,7 @@ impl FromType for ReflectAsset { let mut assets = world.resource_mut::>(); let value: A = FromReflect::from_reflect(value) .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`"); - assets.insert(handle.typed_debug_checked(), value); + assets.insert(&handle.typed_debug_checked(), value); }, len: |world| { let assets = world.resource::>(); @@ -161,7 +161,7 @@ impl FromType for ReflectAsset { }, remove: |world, handle| { let mut assets = world.resource_mut::>(); - let value = assets.remove(handle.typed_debug_checked()); + let value = assets.remove(&handle.typed_debug_checked()); value.map(|value| Box::new(value) as Box) }, } diff --git a/crates/bevy_color/src/color_ops.rs b/crates/bevy_color/src/color_ops.rs index 460fc985d1b3b..1f39f3254c489 100644 --- a/crates/bevy_color/src/color_ops.rs +++ b/crates/bevy_color/src/color_ops.rs @@ -97,10 +97,18 @@ pub trait ClampColor: Sized { fn is_within_bounds(&self) -> bool; } +/// Utility function for interpolating hue values. This ensures that the interpolation +/// takes the shortest path around the color wheel, and that the result is always between +/// 0 and 360. +pub(crate) fn lerp_hue(a: f32, b: f32, t: f32) -> f32 { + let diff = (b - a + 180.0).rem_euclid(360.) - 180.; + (a + diff * t).rem_euclid(360.0) +} + #[cfg(test)] mod tests { use super::*; - use crate::Hsla; + use crate::{testing::assert_approx_eq, Hsla}; #[test] fn test_rotate_hue() { @@ -113,4 +121,23 @@ mod tests { assert_eq!(hsla.rotate_hue(360.0), hsla); assert_eq!(hsla.rotate_hue(-360.0), hsla); } + + #[test] + fn test_hue_wrap() { + assert_approx_eq!(lerp_hue(10., 20., 0.25), 12.5, 0.001); + assert_approx_eq!(lerp_hue(10., 20., 0.5), 15., 0.001); + assert_approx_eq!(lerp_hue(10., 20., 0.75), 17.5, 0.001); + + assert_approx_eq!(lerp_hue(20., 10., 0.25), 17.5, 0.001); + assert_approx_eq!(lerp_hue(20., 10., 0.5), 15., 0.001); + assert_approx_eq!(lerp_hue(20., 10., 0.75), 12.5, 0.001); + + assert_approx_eq!(lerp_hue(10., 350., 0.25), 5., 0.001); + assert_approx_eq!(lerp_hue(10., 350., 0.5), 0., 0.001); + assert_approx_eq!(lerp_hue(10., 350., 0.75), 355., 0.001); + + assert_approx_eq!(lerp_hue(350., 10., 0.25), 355., 0.001); + assert_approx_eq!(lerp_hue(350., 10., 0.5), 0., 0.001); + assert_approx_eq!(lerp_hue(350., 10., 0.75), 5., 0.001); + } } diff --git a/crates/bevy_color/src/hsla.rs b/crates/bevy_color/src/hsla.rs index 37bc74fab512f..c9db008940a93 100644 --- a/crates/bevy_color/src/hsla.rs +++ b/crates/bevy_color/src/hsla.rs @@ -105,16 +105,8 @@ impl Mix for Hsla { #[inline] fn mix(&self, other: &Self, factor: f32) -> Self { let n_factor = 1.0 - factor; - // TODO: Refactor this into EuclideanModulo::lerp_modulo - let shortest_angle = ((((other.hue - self.hue) % 360.) + 540.) % 360.) - 180.; - let mut hue = self.hue + shortest_angle * factor; - if hue < 0. { - hue += 360.; - } else if hue >= 360. { - hue -= 360.; - } Self { - hue, + hue: crate::color_ops::lerp_hue(self.hue, other.hue, factor), saturation: self.saturation * n_factor + other.saturation * factor, lightness: self.lightness * n_factor + other.lightness * factor, alpha: self.alpha * n_factor + other.alpha * factor, diff --git a/crates/bevy_color/src/hsva.rs b/crates/bevy_color/src/hsva.rs index dd959d266b7ff..4423d047ac5b9 100644 --- a/crates/bevy_color/src/hsva.rs +++ b/crates/bevy_color/src/hsva.rs @@ -1,4 +1,4 @@ -use crate::{Alpha, ClampColor, Hue, Hwba, Lcha, LinearRgba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, ClampColor, Hue, Hwba, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza}; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -69,6 +69,19 @@ impl Default for Hsva { } } +impl Mix for Hsva { + #[inline] + fn mix(&self, other: &Self, factor: f32) -> Self { + let n_factor = 1.0 - factor; + Self { + hue: crate::color_ops::lerp_hue(self.hue, other.hue, factor), + saturation: self.saturation * n_factor + other.saturation * factor, + value: self.value * n_factor + other.value * factor, + alpha: self.alpha * n_factor + other.alpha * factor, + } + } +} + impl Alpha for Hsva { #[inline] fn with_alpha(&self, alpha: f32) -> Self { diff --git a/crates/bevy_color/src/hwba.rs b/crates/bevy_color/src/hwba.rs index a83473d06af01..01221397804f7 100644 --- a/crates/bevy_color/src/hwba.rs +++ b/crates/bevy_color/src/hwba.rs @@ -2,7 +2,7 @@ //! in [_HWB - A More Intuitive Hue-Based Color Model_] by _Smith et al_. //! //! [_HWB - A More Intuitive Hue-Based Color Model_]: https://web.archive.org/web/20240226005220/http://alvyray.com/Papers/CG/HWB_JGTv208.pdf -use crate::{Alpha, ClampColor, Hue, Lcha, LinearRgba, Srgba, StandardColor, Xyza}; +use crate::{Alpha, ClampColor, Hue, Lcha, LinearRgba, Mix, Srgba, StandardColor, Xyza}; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -73,6 +73,19 @@ impl Default for Hwba { } } +impl Mix for Hwba { + #[inline] + fn mix(&self, other: &Self, factor: f32) -> Self { + let n_factor = 1.0 - factor; + Self { + hue: crate::color_ops::lerp_hue(self.hue, other.hue, factor), + whiteness: self.whiteness * n_factor + other.whiteness * factor, + blackness: self.blackness * n_factor + other.blackness * factor, + alpha: self.alpha * n_factor + other.alpha * factor, + } + } +} + impl Alpha for Hwba { #[inline] fn with_alpha(&self, alpha: f32) -> Self { diff --git a/crates/bevy_color/src/srgba.rs b/crates/bevy_color/src/srgba.rs index 606a6e2363ed1..8dee5039bb55a 100644 --- a/crates/bevy_color/src/srgba.rs +++ b/crates/bevy_color/src/srgba.rs @@ -1,7 +1,7 @@ -use std::ops::{Div, Mul}; - use crate::color_difference::EuclideanDistance; -use crate::{Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, Xyza}; +use crate::{ + impl_componentwise_point, Alpha, ClampColor, LinearRgba, Luminance, Mix, StandardColor, Xyza, +}; use bevy_math::Vec4; use bevy_reflect::prelude::*; use serde::{Deserialize, Serialize}; @@ -27,6 +27,8 @@ pub struct Srgba { impl StandardColor for Srgba {} +impl_componentwise_point!(Srgba, [red, green, blue, alpha]); + impl Srgba { // The standard VGA colors, with alpha set to 1.0. // https://en.wikipedia.org/wiki/Web_colors#Basic_colors @@ -389,48 +391,6 @@ pub enum HexColorError { Char(char), } -/// All color channels are scaled directly, -/// but alpha is unchanged. -/// -/// Values are not clamped. -impl Mul for Srgba { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { - Self { - red: self.red * rhs, - green: self.green * rhs, - blue: self.blue * rhs, - alpha: self.alpha, - } - } -} - -impl Mul for f32 { - type Output = Srgba; - - fn mul(self, rhs: Srgba) -> Srgba { - rhs * self - } -} - -/// All color channels are scaled directly, -/// but alpha is unchanged. -/// -/// Values are not clamped. -impl Div for Srgba { - type Output = Self; - - fn div(self, rhs: f32) -> Self { - Self { - red: self.red / rhs, - green: self.green / rhs, - blue: self.blue / rhs, - alpha: self.alpha, - } - } -} - #[cfg(test)] mod tests { use crate::testing::assert_approx_eq; diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index cb581b58b82d6..d5a1c3839efd0 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -79,6 +79,12 @@ pub trait QueryFilter: WorldQuery { /// many elements are being iterated (such as `Iterator::collect()`). const IS_ARCHETYPAL: bool; + /// Returns true if the provided [`Entity`] and [`TableRow`] should be included in the query results. + /// If false, the entity will be skipped. + /// + /// Note that this is called after already restricting the matched [`Table`]s and [`Archetype`]s to the + /// ones that are compatible with the Filter's access. + /// /// # Safety /// /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and @@ -357,7 +363,7 @@ impl Clone for OrFetch<'_, T> { } } -macro_rules! impl_query_filter_tuple { +macro_rules! impl_or_query_filter { ($(($filter: ident, $state: ident)),*) => { #[allow(unused_variables)] #[allow(non_snake_case)] @@ -506,7 +512,7 @@ macro_rules! impl_tuple_query_filter { } all_tuples!(impl_tuple_query_filter, 0, 15, F); -all_tuples!(impl_query_filter_tuple, 0, 15, F, S); +all_tuples!(impl_or_query_filter, 0, 15, F, S); /// A filter on a component that only retains results added after the system last ran. /// @@ -524,7 +530,7 @@ all_tuples!(impl_query_filter_tuple, 0, 15, F, S); /// # Time complexity /// /// `Added` is not [`ArchetypeFilter`], which practically means that -/// if query (with `T` component filter) matches million entities, +/// if the query (with `T` component filter) matches a million entities, /// `Added` filter will iterate over all of them even if none of them were just added. /// /// For example, these two systems are roughly equivalent in terms of performance: diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index cc911c1b95a89..35346be6fa8b5 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -42,7 +42,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { } /// Executes the equivalent of [`Iterator::for_each`] over a contiguous segment - /// from an table. + /// from a table. /// /// # Safety /// - all `rows` must be in `[0, table.entity_count)`. @@ -656,7 +656,7 @@ struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> { archetype_entities: &'w [ArchetypeEntity], fetch: D::Fetch<'w>, filter: F::Fetch<'w>, - // length of the table table or length of the archetype, depending on whether both `D`'s and `F`'s fetches are dense + // length of the table or length of the archetype, depending on whether both `D`'s and `F`'s fetches are dense current_len: usize, // either table row or archetype index, depending on whether both `D`'s and `F`'s fetches are dense current_row: usize, @@ -743,7 +743,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { /// How many values will this cursor return at most? /// - /// Note that if `D::IS_ARCHETYPAL && F::IS_ARCHETYPAL`, the return value + /// Note that if `F::IS_ARCHETYPAL`, the return value /// will be **the exact count of remaining values**. fn max_remaining(&self, tables: &'w Tables, archetypes: &'w Archetypes) -> usize { let remaining_matched: usize = if Self::IS_DENSE { @@ -788,7 +788,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { } // SAFETY: set_table was called prior. - // `current_row` is a table row in range of the current table, because if it was not, then the if above would have been executed. + // `current_row` is a table row in range of the current table, because if it was not, then the above would have been executed. let entity = unsafe { self.table_entities.get_unchecked(self.current_row) }; let row = TableRow::from_usize(self.current_row); if !F::filter_fetch(&mut self.filter, *entity, row) { @@ -799,7 +799,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // SAFETY: // - set_table was called prior. // - `current_row` must be a table row in range of the current table, - // because if it was not, then the if above would have been executed. + // because if it was not, then the above would have been executed. // - fetch is only called once for each `entity`. let item = unsafe { D::fetch(&mut self.fetch, *entity, row) }; diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 75da60c8d50db..57bad73e7b8a2 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -22,6 +22,17 @@ use super::{ }; /// Provides scoped access to a [`World`] state according to a given [`QueryData`] and [`QueryFilter`]. +/// +/// This data is cached between system runs, and is used to: +/// - store metadata about which [`Table`] or [`Archetype`] are matched by the query. "Matched" means +/// that the query will iterate over the data in the matched table/archetype. +/// - cache the [`State`] needed to compute the [`Fetch`] struct used to retrieve data +/// from a specific [`Table`] or [`Archetype`] +/// - build iterators that can iterate over the query results +/// +/// [`State`]: crate::query::world_query::WorldQuery::State +/// [`Fetch`]: crate::query::world_query::WorldQuery::Fetch +/// [`Table`]: crate::storage::Table #[repr(C)] // SAFETY NOTE: // Do not add any new fields that use the `D` or `F` generic parameters as this may @@ -29,8 +40,12 @@ use super::{ pub struct QueryState { world_id: WorldId, pub(crate) archetype_generation: ArchetypeGeneration, + /// Metadata about the [`Table`](crate::storage::Table)s matched by this query. pub(crate) matched_tables: FixedBitSet, + /// Metadata about the [`Archetype`]s matched by this query. pub(crate) matched_archetypes: FixedBitSet, + /// [`FilteredAccess`] computed by combining the `D` and `F` access. Used to check which other queries + /// this query can run in parallel with. pub(crate) component_access: FilteredAccess, // NOTE: we maintain both a TableId bitset and a vec because iterating the vec is faster pub(crate) matched_table_ids: Vec, @@ -330,6 +345,11 @@ impl QueryState { } } + /// Process the given [`Archetype`] to update internal metadata about the [`Table`](crate::storage::Table)s + /// and [`Archetype`]s that are matched by this query. + /// + /// Returns `true` if the given `archetype` matches the query. Otherwise, returns `false`. + /// If there is no match, then there is no need to update the query's [`FilteredAccess`]. fn new_archetype_internal(&mut self, archetype: &Archetype) -> bool { if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id)) && F::matches_component_set(&self.filter_state, &|id| archetype.contains(id)) diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index 9d0d0d829ef4b..19c6a3254b129 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -120,6 +120,8 @@ pub unsafe trait WorldQuery { ) -> Self::Item<'w>; /// Adds any component accesses used by this [`WorldQuery`] to `access`. + /// + /// Used to check which queries are disjoint and can run in parallel // This does not have a default body of `{}` because 99% of cases need to add accesses // and forgetting to do so would be unsound. fn update_component_access(state: &Self::State, access: &mut FilteredAccess); @@ -132,6 +134,9 @@ pub unsafe trait WorldQuery { fn get_state(world: &World) -> Option; /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. + /// + /// Used to check which [`Archetype`]s can be skipped by the query + /// (if none of the [`Component`](crate::component::Component)s match) fn matches_component_set( state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool, diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index acf5e39f683fa..06294b6fe1e24 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -1,6 +1,6 @@ mod parallel_scope; -use super::{Deferred, Resource}; +use super::{Deferred, IntoSystem, RegisterSystem, Resource}; use crate::{ self as bevy_ecs, bundle::Bundle, @@ -520,6 +520,72 @@ impl<'w, 's> Commands<'w, 's> { .push(RunSystemWithInput::new_with_input(id, input)); } + /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`]. + /// + /// It's possible to register the same systems more than once, they'll be stored separately. + /// + /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule), + /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system. + /// This allows for running systems in a push-based fashion. + /// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases + /// due to its better performance and ability to run non-conflicting systems simultaneously. + /// + /// If you want to prevent Commands from registering the same system multiple times, consider using [`Local`](crate::system::Local) + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::{prelude::*, world::CommandQueue, system::SystemId}; + /// + /// #[derive(Resource)] + /// struct Counter(i32); + /// + /// fn register_system(mut local_system: Local>, mut commands: Commands) { + /// if let Some(system) = *local_system { + /// commands.run_system(system); + /// } else { + /// *local_system = Some(commands.register_one_shot_system(increment_counter)); + /// } + /// } + /// + /// fn increment_counter(mut value: ResMut) { + /// value.0 += 1; + /// } + /// + /// # let mut world = World::default(); + /// # world.insert_resource(Counter(0)); + /// # let mut queue_1 = CommandQueue::default(); + /// # let systemid = { + /// # let mut commands = Commands::new(&mut queue_1, &world); + /// # commands.register_one_shot_system(increment_counter) + /// # }; + /// # let mut queue_2 = CommandQueue::default(); + /// # { + /// # let mut commands = Commands::new(&mut queue_2, &world); + /// # commands.run_system(systemid); + /// # } + /// # queue_1.append(&mut queue_2); + /// # queue_1.apply(&mut world); + /// # assert_eq!(1, world.resource::().0); + /// # bevy_ecs::system::assert_is_system(register_system); + /// ``` + pub fn register_one_shot_system< + I: 'static + Send, + O: 'static + Send, + M, + S: IntoSystem + 'static, + >( + &mut self, + system: S, + ) -> SystemId { + let entity = self.spawn_empty().id(); + self.queue.push(RegisterSystem::new(system, entity)); + SystemId { + entity, + marker: std::marker::PhantomData, + } + } + /// Pushes a generic [`Command`] to the command queue. /// /// `command` can be a built-in command, custom struct that implements [`Command`] or a closure @@ -764,13 +830,13 @@ impl EntityCommands<'_> { /// commands.entity(player.entity) /// // You can try_insert individual components: /// .try_insert(Defense(10)) - /// + /// /// // You can also insert tuples of components: /// .try_insert(CombatBundle { /// health: Health(100), /// strength: Strength(40), /// }); - /// + /// /// // Suppose this occurs in a parallel adjacent system or process /// commands.entity(player.entity) /// .despawn(); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index fb87bb2e018ef..961c30c94d3c4 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -552,7 +552,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// Returns an [`Iterator`] over the read-only query items generated from an [`Entity`] list. /// /// Items are returned in the order of the list of entities, and may not be unique if the input - /// doesnn't guarantee uniqueness. Entities that don't match the query are skipped. + /// doesn't guarantee uniqueness. Entities that don't match the query are skipped. /// /// # Example /// diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index bb4c2fc2d8170..168b285aeb82f 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -38,9 +38,11 @@ impl RemovedSystem { /// /// These are opaque identifiers, keyed to a specific [`World`], /// and are created via [`World::register_system`]. -pub struct SystemId(Entity, std::marker::PhantomData O>); +pub struct SystemId { + pub(crate) entity: Entity, + pub(crate) marker: std::marker::PhantomData O>, +} -// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. impl Eq for SystemId {} // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. @@ -56,22 +58,22 @@ impl Clone for SystemId { // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. impl PartialEq for SystemId { fn eq(&self, other: &Self) -> bool { - self.0 == other.0 && self.1 == other.1 + self.entity == other.entity && self.marker == other.marker } } // A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters. impl std::hash::Hash for SystemId { fn hash(&self, state: &mut H) { - self.0.hash(state); + self.entity.hash(state); } } impl std::fmt::Debug for SystemId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("SystemId") - .field(&self.0) - .field(&self.1) + .field(&self.entity) + .field(&self.entity) .finish() } } @@ -83,7 +85,7 @@ impl From> for Entity { /// is really an entity with associated handler function. /// /// For example, this is useful if you want to assign a name label to a system. - fn from(SystemId(entity, _): SystemId) -> Self { + fn from(SystemId { entity, .. }: SystemId) -> Self { entity } } @@ -113,14 +115,15 @@ impl World { &mut self, system: BoxedSystem, ) -> SystemId { - SystemId( - self.spawn(RegisteredSystem { - initialized: false, - system, - }) - .id(), - std::marker::PhantomData, - ) + SystemId { + entity: self + .spawn(RegisteredSystem { + initialized: false, + system, + }) + .id(), + marker: std::marker::PhantomData, + } } /// Removes a registered system and returns the system, if it exists. @@ -133,7 +136,7 @@ impl World { &mut self, id: SystemId, ) -> Result, RegisteredSystemError> { - match self.get_entity_mut(id.0) { + match self.get_entity_mut(id.entity) { Some(mut entity) => { let registered_system = entity .take::>() @@ -275,7 +278,7 @@ impl World { ) -> Result> { // lookup let mut entity = self - .get_entity_mut(id.0) + .get_entity_mut(id.entity) .ok_or(RegisteredSystemError::SystemIdNotRegistered(id))?; // take ownership of system trait object @@ -294,7 +297,7 @@ impl World { let result = system.run(input, self); // return ownership of system trait object (if entity still exists) - if let Some(mut entity) = self.get_entity_mut(id.0) { + if let Some(mut entity) = self.get_entity_mut(id.entity) { entity.insert::>(RegisteredSystem { initialized, system, @@ -356,6 +359,35 @@ impl Command for RunSystemWithInput { } } +/// The [`Command`] type for registering one shot systems from [Commands](crate::system::Commands). +/// +/// This command needs an already boxed system to register, and an already spawned entity +pub struct RegisterSystem { + system: BoxedSystem, + entity: Entity, +} + +impl RegisterSystem { + /// Creates a new [Command] struct, which can be added to [Commands](crate::system::Commands) + pub fn new + 'static>(system: S, entity: Entity) -> Self { + Self { + system: Box::new(IntoSystem::into_system(system)), + entity, + } + } +} + +impl Command for RegisterSystem { + fn apply(self, world: &mut World) { + let _ = world.get_entity_mut(self.entity).map(|mut entity| { + entity.insert(RegisteredSystem { + initialized: false, + system: self.system, + }); + }); + } +} + /// An operation with stored systems failed. #[derive(Error)] pub enum RegisteredSystemError { diff --git a/crates/bevy_math/src/primitives/dim2.rs b/crates/bevy_math/src/primitives/dim2.rs index 5106e169f240f..f995729ddd075 100644 --- a/crates/bevy_math/src/primitives/dim2.rs +++ b/crates/bevy_math/src/primitives/dim2.rs @@ -376,10 +376,10 @@ impl Triangle2d { } /// Reverse the [`WindingOrder`] of the triangle - /// by swapping the second and third vertices + /// by swapping the first and last vertices #[inline(always)] pub fn reverse(&mut self) { - self.vertices.swap(1, 2); + self.vertices.swap(0, 2); } } @@ -753,9 +753,9 @@ mod tests { assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise); let ccw_triangle = Triangle2d::new( - Vec2::new(0.0, 2.0), Vec2::new(-1.0, -1.0), Vec2::new(-0.5, -1.2), + Vec2::new(0.0, 2.0), ); assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise); diff --git a/crates/bevy_math/src/primitives/dim3.rs b/crates/bevy_math/src/primitives/dim3.rs index 88174c5052d53..a469fa7efb91c 100644 --- a/crates/bevy_math/src/primitives/dim3.rs +++ b/crates/bevy_math/src/primitives/dim3.rs @@ -1,7 +1,10 @@ use std::f32::consts::{FRAC_PI_3, PI}; use super::{Circle, Primitive3d}; -use crate::{Dir3, Vec3}; +use crate::{ + bounding::{Aabb3d, Bounded3d, BoundingSphere}, + Dir3, InvalidDirectionError, Quat, Vec3, +}; /// A sphere primitive #[derive(Clone, Copy, Debug, PartialEq)] @@ -629,6 +632,197 @@ impl Torus { } } +/// A 3D triangle primitive. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub struct Triangle3d { + /// The vertices of the triangle. + pub vertices: [Vec3; 3], +} + +impl Primitive3d for Triangle3d {} + +impl Default for Triangle3d { + /// Returns the default [`Triangle3d`] with the vertices `[0.0, 0.5, 0.0]`, `[-0.5, -0.5, 0.0]`, and `[0.5, -0.5, 0.0]`. + fn default() -> Self { + Self { + vertices: [ + Vec3::new(0.0, 0.5, 0.0), + Vec3::new(-0.5, -0.5, 0.0), + Vec3::new(0.5, -0.5, 0.0), + ], + } + } +} + +impl Triangle3d { + /// Create a new [`Triangle3d`] from points `a`, `b`, and `c`. + #[inline(always)] + pub fn new(a: Vec3, b: Vec3, c: Vec3) -> Self { + Self { + vertices: [a, b, c], + } + } + + /// Get the area of the triangle. + #[inline(always)] + pub fn area(&self) -> f32 { + let [a, b, c] = self.vertices; + let ab = b - a; + let ac = c - a; + ab.cross(ac).length() / 2.0 + } + + /// Get the perimeter of the triangle. + #[inline(always)] + pub fn perimeter(&self) -> f32 { + let [a, b, c] = self.vertices; + a.distance(b) + b.distance(c) + c.distance(a) + } + + /// Get the normal of the triangle in the direction of the right-hand rule, assuming + /// the vertices are ordered in a counter-clockwise direction. + /// + /// The normal is computed as the cross product of the vectors `ab` and `ac`. + /// + /// # Errors + /// + /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if the length + /// of the given vector is zero (or very close to zero), infinite, or `NaN`. + #[inline(always)] + pub fn normal(&self) -> Result { + let [a, b, c] = self.vertices; + let ab = b - a; + let ac = c - a; + Dir3::new(ab.cross(ac)) + } + + /// Checks if the triangle is degenerate, meaning it has zero area. + /// + /// A triangle is degenerate if the cross product of the vectors `ab` and `ac` has a length less than `f32::EPSILON`. + /// This indicates that the three vertices are collinear or nearly collinear. + #[inline(always)] + pub fn is_degenerate(&self) -> bool { + let [a, b, c] = self.vertices; + let ab = b - a; + let ac = c - a; + ab.cross(ac).length() < 10e-7 + } + + /// Reverse the triangle by swapping the first and last vertices. + #[inline(always)] + pub fn reverse(&mut self) { + self.vertices.swap(0, 2); + } + + /// Get the centroid of the triangle. + /// + /// This function finds the geometric center of the triangle by averaging the vertices: + /// `centroid = (a + b + c) / 3`. + #[doc(alias("center", "barycenter", "baricenter"))] + #[inline(always)] + pub fn centroid(&self) -> Vec3 { + (self.vertices[0] + self.vertices[1] + self.vertices[2]) / 3.0 + } + + /// Get the largest side of the triangle. + /// + /// Returns the two points that form the largest side of the triangle. + #[inline(always)] + pub fn largest_side(&self) -> (Vec3, Vec3) { + let [a, b, c] = self.vertices; + let ab = b - a; + let bc = c - b; + let ca = a - c; + + let mut largest_side_points = (a, b); + let mut largest_side_length = ab.length(); + + if bc.length() > largest_side_length { + largest_side_points = (b, c); + largest_side_length = bc.length(); + } + + if ca.length() > largest_side_length { + largest_side_points = (a, c); + } + + largest_side_points + } + + /// Get the circumcenter of the triangle. + #[inline(always)] + pub fn circumcenter(&self) -> Vec3 { + if self.is_degenerate() { + // If the triangle is degenerate, the circumcenter is the midpoint of the largest side. + let (p1, p2) = self.largest_side(); + return (p1 + p2) / 2.0; + } + + let [a, b, c] = self.vertices; + let ab = b - a; + let ac = c - a; + let n = ab.cross(ac); + + // Reference: https://gamedev.stackexchange.com/questions/60630/how-do-i-find-the-circumcenter-of-a-triangle-in-3d + a + ((ac.length_squared() * n.cross(ab) + ab.length_squared() * ac.cross(ab).cross(ac)) + / (2.0 * n.length_squared())) + } +} + +impl Bounded3d for Triangle3d { + /// Get the bounding box of the triangle. + fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d { + let [a, b, c] = self.vertices; + + let a = rotation * a; + let b = rotation * b; + let c = rotation * c; + + let min = a.min(b).min(c); + let max = a.max(b).max(c); + + let bounding_center = (max + min) / 2.0 + translation; + let half_extents = (max - min) / 2.0; + + Aabb3d::new(bounding_center, half_extents) + } + + /// Get the bounding sphere of the triangle. + /// + /// The [`Triangle3d`] implements the minimal bounding sphere calculation. For acute triangles, the circumcenter is used as + /// the center of the sphere. For the others, the bounding sphere is the minimal sphere + /// that contains the largest side of the triangle. + fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere { + if self.is_degenerate() { + let (p1, p2) = self.largest_side(); + let (segment, _) = Segment3d::from_points(p1, p2); + return segment.bounding_sphere(translation, rotation); + } + + let [a, b, c] = self.vertices; + + let side_opposite_to_non_acute = if (b - a).dot(c - a) <= 0.0 { + Some((b, c)) + } else if (c - b).dot(a - b) <= 0.0 { + Some((c, a)) + } else if (a - c).dot(b - c) <= 0.0 { + Some((a, b)) + } else { + None + }; + + if let Some((p1, p2)) = side_opposite_to_non_acute { + let (segment, _) = Segment3d::from_points(p1, p2); + segment.bounding_sphere(translation, rotation) + } else { + let circumcenter = self.circumcenter(); + let radius = circumcenter.distance(a); + BoundingSphere::new(circumcenter + translation, radius) + } + } +} + #[cfg(test)] mod tests { // Reference values were computed by hand and/or with external tools diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index c969db7944c08..429aa85135e01 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -328,7 +328,7 @@ impl Plugin for PbrPlugin { } app.world.resource_mut::>().insert( - Handle::::default(), + &Handle::::default(), StandardMaterial { base_color: Color::srgb(1.0, 0.0, 0.5), unlit: true, diff --git a/crates/bevy_render/src/texture/mod.rs b/crates/bevy_render/src/texture/mod.rs index 986ae7d104ea4..0d186f9aa6139 100644 --- a/crates/bevy_render/src/texture/mod.rs +++ b/crates/bevy_render/src/texture/mod.rs @@ -90,7 +90,7 @@ impl Plugin for ImagePlugin { .register_asset_reflect::(); app.world .resource_mut::>() - .insert(Handle::default(), Image::default()); + .insert(&Handle::default(), Image::default()); #[cfg(feature = "basis-universal")] if let Some(processor) = app .world diff --git a/crates/bevy_scene/src/bundle.rs b/crates/bevy_scene/src/bundle.rs index e47e198728390..a0b27b44d0a0e 100644 --- a/crates/bevy_scene/src/bundle.rs +++ b/crates/bevy_scene/src/bundle.rs @@ -20,7 +20,7 @@ pub struct SceneInstance(InstanceId); /// A component bundle for a [`Scene`] root. /// -/// The scene from `scene` will be spawn as a child of the entity with this component. +/// The scene from `scene` will be spawned as a child of the entity with this component. /// Once it's spawned, the entity will have a [`SceneInstance`] component. #[derive(Default, Bundle)] pub struct SceneBundle { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 86c0c30d9a3d9..08a9695f79a10 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -482,7 +482,7 @@ mod tests { let scene_id = world.resource_mut::>().add(scene); let instance_id = scene_spawner - .spawn_dynamic_sync(&mut world, scene_id) + .spawn_dynamic_sync(&mut world, &scene_id) .unwrap(); // verify we spawned exactly one new entity with our expected component diff --git a/crates/bevy_sprite/src/mesh2d/color_material.rs b/crates/bevy_sprite/src/mesh2d/color_material.rs index 59d489361bbe7..6a5f26463d864 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.rs +++ b/crates/bevy_sprite/src/mesh2d/color_material.rs @@ -25,7 +25,7 @@ impl Plugin for ColorMaterialPlugin { .register_asset_reflect::(); app.world.resource_mut::>().insert( - Handle::::default(), + &Handle::::default(), ColorMaterial { color: Color::srgb(1.0, 0.0, 1.0), ..Default::default() diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index acb0d3b6dce85..01f09c8f40e66 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -274,7 +274,7 @@ impl Plugin for ColoredMesh2dPlugin { // Load our custom shader let mut shaders = app.world.resource_mut::>(); shaders.insert( - COLORED_MESH2D_SHADER_HANDLE, + &COLORED_MESH2D_SHADER_HANDLE, Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()), ); diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs index 8ef6dfb521f06..750478d03893b 100644 --- a/examples/3d/spotlight.rs +++ b/examples/3d/spotlight.rs @@ -7,7 +7,8 @@ use bevy::{ pbr::NotShadowCaster, prelude::*, }; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; const INSTRUCTIONS: &str = "\ Controls @@ -48,7 +49,7 @@ fn setup( )); // cubes - let mut rng = StdRng::seed_from_u64(19878367467713); + let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); let cube_mesh = meshes.add(Cuboid::new(0.5, 0.5, 0.5)); let blue = materials.add(Color::srgb_u8(124, 144, 255)); diff --git a/examples/animation/color_animation.rs b/examples/animation/color_animation.rs index 386f60f7f3ce5..605e8f395e2a3 100644 --- a/examples/animation/color_animation.rs +++ b/examples/animation/color_animation.rs @@ -37,7 +37,7 @@ fn main() { fn setup(mut commands: Commands) { commands.spawn(Camera2dBundle::default()); - // The color spaces `Oklaba`, `Laba`, `LinearRgba` and `Xyza` all are either perceptually or physically linear. + // The color spaces `Oklaba`, `Laba`, `LinearRgba`, `Srgba` and `Xyza` all are either perceptually or physically linear. // This property allows us to define curves, e.g. bezier curves through these spaces. // Define the control points for the curve. diff --git a/examples/animation/custom_skinned_mesh.rs b/examples/animation/custom_skinned_mesh.rs index 9f5f3733bef48..0bc26faabf664 100644 --- a/examples/animation/custom_skinned_mesh.rs +++ b/examples/animation/custom_skinned_mesh.rs @@ -14,7 +14,8 @@ use bevy::{ render_asset::RenderAssetUsages, }, }; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; fn main() { App::new() @@ -122,7 +123,7 @@ fn setup( let mesh = meshes.add(mesh); - let mut rng = StdRng::seed_from_u64(42); + let mut rng = ChaCha8Rng::seed_from_u64(42); for i in -5..5 { // Create joint entities diff --git a/examples/async_tasks/external_source_external_thread.rs b/examples/async_tasks/external_source_external_thread.rs index ee1d6adda9a77..c28747bd0c540 100644 --- a/examples/async_tasks/external_source_external_thread.rs +++ b/examples/async_tasks/external_source_external_thread.rs @@ -3,7 +3,8 @@ use bevy::prelude::*; // Using crossbeam_channel instead of std as std `Receiver` is `!Sync` use crossbeam_channel::{bounded, Receiver}; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::time::{Duration, Instant}; fn main() { @@ -26,7 +27,7 @@ fn setup(mut commands: Commands) { let (tx, rx) = bounded::(10); std::thread::spawn(move || { - let mut rng = StdRng::seed_from_u64(19878367467713); + let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); loop { // Everything here happens in another thread // This is where you could connect to an external data source diff --git a/examples/ecs/iter_combinations.rs b/examples/ecs/iter_combinations.rs index 9c7a8e47206c8..d92737f784963 100644 --- a/examples/ecs/iter_combinations.rs +++ b/examples/ecs/iter_combinations.rs @@ -1,7 +1,8 @@ //! Shows how to iterate over combinations of query results. use bevy::{color::palettes::css::ORANGE_RED, prelude::*}; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; fn main() { App::new() @@ -44,7 +45,7 @@ fn generate_bodies( let color_range = 0.5..1.0; let vel_range = -0.5..0.5; - let mut rng = StdRng::seed_from_u64(19878367467713); + let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); for _ in 0..NUM_BODIES { let radius: f32 = rng.gen_range(0.1..0.7); let mass_value = radius.powi(3) * 10.; diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 341746e414512..d9449c08c159a 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -2,7 +2,8 @@ use bevy::ecs::query::BatchingStrategy; use bevy::prelude::*; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; #[derive(Component, Deref)] struct Velocity(Vec2); @@ -10,7 +11,7 @@ struct Velocity(Vec2); fn spawn_system(mut commands: Commands, asset_server: Res) { commands.spawn(Camera2dBundle::default()); let texture = asset_server.load("branding/icon.png"); - let mut rng = StdRng::seed_from_u64(19878367467713); + let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); for _ in 0..128 { commands.spawn(( SpriteBundle { diff --git a/examples/games/alien_cake_addict.rs b/examples/games/alien_cake_addict.rs index a1571310fb533..02a02d9631261 100644 --- a/examples/games/alien_cake_addict.rs +++ b/examples/games/alien_cake_addict.rs @@ -3,7 +3,8 @@ use std::f32::consts::PI; use bevy::prelude::*; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; #[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)] enum GameState { @@ -82,7 +83,7 @@ struct Game { } #[derive(Resource, Deref, DerefMut)] -struct Random(StdRng); +struct Random(ChaCha8Rng); const BOARD_SIZE_I: usize = 14; const BOARD_SIZE_J: usize = 21; @@ -110,9 +111,9 @@ fn setup_cameras(mut commands: Commands, mut game: ResMut) { fn setup(mut commands: Commands, asset_server: Res, mut game: ResMut) { let mut rng = if std::env::var("GITHUB_ACTIONS") == Ok("true".to_string()) { // Make the game play out the same way every time, this is useful for testing purposes. - StdRng::seed_from_u64(19878367467713) + ChaCha8Rng::seed_from_u64(19878367467713) } else { - StdRng::from_entropy() + ChaCha8Rng::from_entropy() }; // reset the game state diff --git a/examples/gizmos/axes.rs b/examples/gizmos/axes.rs index a7fe43319199e..033b65dbfab66 100644 --- a/examples/gizmos/axes.rs +++ b/examples/gizmos/axes.rs @@ -1,7 +1,8 @@ //! This example demonstrates the implementation and behavior of the axes gizmo. use bevy::prelude::*; use bevy::render::primitives::Aabb; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::f32::consts::PI; const TRANSITION_DURATION: f32 = 2.0; @@ -34,14 +35,14 @@ struct TransformTracking { } #[derive(Resource)] -struct SeededRng(StdRng); +struct SeededRng(ChaCha8Rng); fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { - let mut rng = StdRng::seed_from_u64(19878367467713); + let mut rng = ChaCha8Rng::seed_from_u64(19878367467713); // Lights... commands.spawn(PointLightBundle { diff --git a/examples/shader/array_texture.rs b/examples/shader/array_texture.rs index d09c233b7884d..636287a03aaac 100644 --- a/examples/shader/array_texture.rs +++ b/examples/shader/array_texture.rs @@ -54,7 +54,7 @@ fn create_array_texture( mut materials: ResMut>, ) { if loading_texture.is_loaded - || asset_server.load_state(loading_texture.handle.clone()) != LoadState::Loaded + || asset_server.load_state(loading_texture.handle.id()) != LoadState::Loaded { return; } diff --git a/examples/stress_tests/bevymark.rs b/examples/stress_tests/bevymark.rs index f47065e5bd605..136632c10bd5a 100644 --- a/examples/stress_tests/bevymark.rs +++ b/examples/stress_tests/bevymark.rs @@ -18,7 +18,8 @@ use bevy::{ window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, }; -use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; +use rand::{seq::SliceRandom, Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; const BIRDS_PER_SECOND: u32 = 10000; const GRAVITY: f32 = -9.8 * 100.0; @@ -181,10 +182,10 @@ struct BirdResources { textures: Vec>, materials: Vec>, quad: Mesh2dHandle, - color_rng: StdRng, - material_rng: StdRng, - velocity_rng: StdRng, - transform_rng: StdRng, + color_rng: ChaCha8Rng, + material_rng: ChaCha8Rng, + velocity_rng: ChaCha8Rng, + transform_rng: ChaCha8Rng, } #[derive(Component)] @@ -221,10 +222,10 @@ fn setup( quad: meshes .add(Rectangle::from_size(Vec2::splat(BIRD_TEXTURE_SIZE as f32))) .into(), - color_rng: StdRng::seed_from_u64(42), - material_rng: StdRng::seed_from_u64(42), - velocity_rng: StdRng::seed_from_u64(42), - transform_rng: StdRng::seed_from_u64(42), + color_rng: ChaCha8Rng::seed_from_u64(42), + material_rng: ChaCha8Rng::seed_from_u64(42), + velocity_rng: ChaCha8Rng::seed_from_u64(42), + transform_rng: ChaCha8Rng::seed_from_u64(42), }; let text_section = move |color: Srgba, value: &str| { @@ -300,11 +301,11 @@ fn mouse_handler( windows: Query<&Window>, bird_resources: ResMut, mut counter: ResMut, - mut rng: Local>, + mut rng: Local>, mut wave: Local, ) { if rng.is_none() { - *rng = Some(StdRng::seed_from_u64(42)); + *rng = Some(ChaCha8Rng::seed_from_u64(42)); } let rng = rng.as_mut().unwrap(); let window = windows.single(); @@ -332,7 +333,7 @@ fn mouse_handler( fn bird_velocity_transform( half_extents: Vec2, mut translation: Vec3, - velocity_rng: &mut StdRng, + velocity_rng: &mut ChaCha8Rng, waves: Option, dt: f32, ) -> (Transform, Vec3) { @@ -537,7 +538,7 @@ fn counter_system( } fn init_textures(textures: &mut Vec>, args: &Args, images: &mut Assets) { - let mut color_rng = StdRng::seed_from_u64(42); + let mut color_rng = ChaCha8Rng::seed_from_u64(42); while textures.len() < args.material_texture_count { let pixel = [color_rng.gen(), color_rng.gen(), color_rng.gen(), 255]; textures.push(images.add(Image::new_fill( @@ -572,8 +573,8 @@ fn init_materials( texture: textures.first().cloned(), })); - let mut color_rng = StdRng::seed_from_u64(42); - let mut texture_rng = StdRng::seed_from_u64(42); + let mut color_rng = ChaCha8Rng::seed_from_u64(42); + let mut texture_rng = ChaCha8Rng::seed_from_u64(42); materials.extend( std::iter::repeat_with(|| { assets.add(ColorMaterial { diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index 88755973c6863..b1f9c0a79eaa1 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -23,7 +23,8 @@ use bevy::{ window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, }; -use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; +use rand::{seq::SliceRandom, Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; #[derive(FromArgs, Resource)] /// `many_cubes` stress test @@ -123,7 +124,7 @@ fn setup( let material_textures = init_textures(args, images); let materials = init_materials(args, &material_textures, material_assets); - let mut material_rng = StdRng::seed_from_u64(42); + let mut material_rng = ChaCha8Rng::seed_from_u64(42); match args.layout { Layout::Sphere => { // NOTE: This pattern is good for testing performance of culling as it provides roughly @@ -202,7 +203,7 @@ fn setup( } fn init_textures(args: &Args, images: &mut Assets) -> Vec> { - let mut color_rng = StdRng::seed_from_u64(42); + let mut color_rng = ChaCha8Rng::seed_from_u64(42); let color_bytes: Vec = (0..(args.material_texture_count * 4)) .map(|i| if (i % 4) == 3 { 255 } else { color_rng.gen() }) .collect(); @@ -246,8 +247,8 @@ fn init_materials( ..default() })); - let mut color_rng = StdRng::seed_from_u64(42); - let mut texture_rng = StdRng::seed_from_u64(42); + let mut color_rng = ChaCha8Rng::seed_from_u64(42); + let mut texture_rng = ChaCha8Rng::seed_from_u64(42); materials.extend( std::iter::repeat_with(|| { assets.add(StandardMaterial { diff --git a/examples/transforms/align.rs b/examples/transforms/align.rs index c950ee50a2ff8..242f437d98428 100644 --- a/examples/transforms/align.rs +++ b/examples/transforms/align.rs @@ -6,7 +6,8 @@ use bevy::color::{ }; use bevy::input::mouse::{MouseButton, MouseButtonInput, MouseMotion}; use bevy::prelude::*; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; use std::f32::consts::PI; fn main() { @@ -44,7 +45,7 @@ struct Instructions; struct MousePressed(bool); #[derive(Resource)] -struct SeededRng(StdRng); +struct SeededRng(ChaCha8Rng); // Setup @@ -53,7 +54,7 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - let mut seeded_rng = StdRng::seed_from_u64(19878367467712); + let mut seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712); // A camera looking at the origin commands.spawn(Camera3dBundle { diff --git a/examples/ui/font_atlas_debug.rs b/examples/ui/font_atlas_debug.rs index 1cb3dd6c33b23..2179e3cf0cdd7 100644 --- a/examples/ui/font_atlas_debug.rs +++ b/examples/ui/font_atlas_debug.rs @@ -2,7 +2,8 @@ //! Bevy uses `FontAtlas`'s under the hood to optimize text rendering. use bevy::{color::palettes::basic::YELLOW, prelude::*, text::FontAtlasSets}; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha8Rng; fn main() { App::new() @@ -32,7 +33,7 @@ impl Default for State { } #[derive(Resource, Deref, DerefMut)] -struct SeededRng(StdRng); +struct SeededRng(ChaCha8Rng); fn atlas_render_system( mut commands: Commands, @@ -104,5 +105,5 @@ fn setup(mut commands: Commands, asset_server: Res, mut state: ResM }, )); }); - commands.insert_resource(SeededRng(StdRng::seed_from_u64(19878367467713))); + commands.insert_resource(SeededRng(ChaCha8Rng::seed_from_u64(19878367467713))); }