From 21dacbf137745f4d138dcdaacd78f9e504a8b2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Le=C3=A9h?= Date: Tue, 16 Aug 2022 20:28:31 +0000 Subject: [PATCH 01/15] fix typos in examples (#5711) ## Objective Fixed some typos I came across while reading examples. --- examples/2d/mesh2d_manual.rs | 2 +- examples/3d/split_screen.rs | 2 +- examples/android/android.rs | 2 +- examples/shader/post_processing.rs | 2 +- examples/transforms/scale.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 139b88ddea46e..13fd3c24d6632 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -59,7 +59,7 @@ fn star( // 6 // 7 5 // - // These vertices are specificed in 3D space. + // These vertices are specified in 3D space. let mut v_pos = vec![[0.0, 0.0, 0.0]]; for i in 0..10 { // Angle of each vertex is 1/10 of TAU, plus PI/2 for positioning vertex 0 diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 70cc464ea2593..6dffd9d60ba60 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -67,7 +67,7 @@ fn setup( ..default() }, camera_3d: Camera3d { - // dont clear on the second camera because the first camera already cleared the window + // don't clear on the second camera because the first camera already cleared the window clear_color: ClearColorConfig::None, ..default() }, diff --git a/examples/android/android.rs b/examples/android/android.rs index 3fcae59b1662e..e497230266e59 100644 --- a/examples/android/android.rs +++ b/examples/android/android.rs @@ -8,7 +8,7 @@ use bevy::{ fn main() { App::new() // This configures the app to use the most compatible rendering settings. - // They help with compatibilty with as many devices as possible. + // They help with compatibility with as many devices as possible. .insert_resource(WgpuSettings { priority: WgpuSettingsPriority::Compatibility, ..default() diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index cbbb20456f5aa..efe6d6fd7d814 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -1,5 +1,5 @@ //! A custom post processing effect, using two cameras, with one reusing the render texture of the first one. -//! Here a chromatic aberration is applied to a 3d scene containting a rotating cube. +//! Here a chromatic aberration is applied to a 3d scene containing a rotating cube. //! This example is useful to implement your own post-processing effect such as //! edge detection, blur, pixelization, vignette... and countless others. diff --git a/examples/transforms/scale.rs b/examples/transforms/scale.rs index c7d8251045f75..51ebd9e1afceb 100644 --- a/examples/transforms/scale.rs +++ b/examples/transforms/scale.rs @@ -13,7 +13,7 @@ struct Scaling { min_element_size: f32, } -// Implement a simple initialisation. +// Implement a simple initialization. impl Scaling { fn new() -> Self { Scaling { From bd317ea364f5a74e265223cd16a3723a9f28c02e Mon Sep 17 00:00:00 2001 From: Marc-Stefan Cassola Date: Tue, 16 Aug 2022 20:46:44 +0000 Subject: [PATCH 02/15] register `Cow<'static, str>` for reflection (#5664) # Objective Fixes #5597 ## Solution Registered type at suggested place. --- crates/bevy_core/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 41b8e02d4935e..527332ab97def 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -17,6 +17,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::entity::Entity; use bevy_utils::HashSet; +use std::borrow::Cow; use std::ops::Range; /// Adds core functionality to Apps. @@ -43,7 +44,8 @@ fn register_rust_types(app: &mut App) { app.register_type::>() .register_type::() .register_type::>() - .register_type::>(); + .register_type::>() + .register_type::>(); } fn register_math_types(app: &mut App) { From 110831150e8228ab96dce8a73123d9f1a1a56a90 Mon Sep 17 00:00:00 2001 From: Jonas Wagner Date: Tue, 16 Aug 2022 20:46:45 +0000 Subject: [PATCH 03/15] Make vertex colors work without textures in bevy_sprite (#5685) # Objective This PR changes it possible to use vertex colors without a texture using the bevy_sprite ColorMaterial. Fixes #5679 ## Solution - Made multiplication of the output color independent of the COLOR_MATERIAL_FLAGS_TEXTURE_BIT bit - Extended mesh2d_vertex_color_texture example to show off both vertex colors and tinting Not sure if extending the existing example was the right call but it seems to be reasonable to me. I couldn't find any tests for the shaders and I think adding shader testing would be beyond the scope of this PR. So no tests in this PR. :grimacing: Co-authored-by: Jonas Wagner --- .../src/mesh2d/color_material.wgsl | 7 +++-- examples/2d/mesh2d_vertex_color_texture.rs | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/crates/bevy_sprite/src/mesh2d/color_material.wgsl b/crates/bevy_sprite/src/mesh2d/color_material.wgsl index 3b6a2b5217176..3b5893d4ce285 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.wgsl +++ b/crates/bevy_sprite/src/mesh2d/color_material.wgsl @@ -26,12 +26,11 @@ struct FragmentInput { @fragment fn fragment(in: FragmentInput) -> @location(0) vec4 { var output_color: vec4 = material.color; - if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) { #ifdef VERTEX_COLORS - output_color = output_color * textureSample(texture, texture_sampler, in.uv) * in.color; -#else - output_color = output_color * textureSample(texture, texture_sampler, in.uv); + output_color = output_color * in.color; #endif + if ((material.flags & COLOR_MATERIAL_FLAGS_TEXTURE_BIT) != 0u) { + output_color = output_color * textureSample(texture, texture_sampler, in.uv); } return output_color; } diff --git a/examples/2d/mesh2d_vertex_color_texture.rs b/examples/2d/mesh2d_vertex_color_texture.rs index de3ec5c4491a1..1633c2f736c3f 100644 --- a/examples/2d/mesh2d_vertex_color_texture.rs +++ b/examples/2d/mesh2d_vertex_color_texture.rs @@ -1,7 +1,10 @@ //! Shows how to render a polygonal [`Mesh`], generated from a [`Quad`] primitive, in a 2D scene. //! Adds a texture and colored vertices, giving per-vertex tinting. -use bevy::{prelude::*, sprite::MaterialMesh2dBundle}; +use bevy::{ + prelude::*, + sprite::{MaterialMesh2dBundle, Mesh2dHandle}, +}; fn main() { App::new() @@ -29,11 +32,26 @@ fn setup( ]; // Insert the vertex colors as an attribute mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_colors); - // Spawn + + let mesh_handle: Mesh2dHandle = meshes.add(mesh).into(); + + // Spawn camera commands.spawn_bundle(Camera2dBundle::default()); + + // Spawn the quad with vertex colors + commands.spawn_bundle(MaterialMesh2dBundle { + mesh: mesh_handle.clone(), + transform: Transform::from_translation(Vec3::new(-96., 0., 0.)) + .with_scale(Vec3::splat(128.)), + material: materials.add(ColorMaterial::default()), + ..default() + }); + + // Spawning the quad with vertex colors and a texture results in tinting commands.spawn_bundle(MaterialMesh2dBundle { - mesh: meshes.add(mesh).into(), - transform: Transform::default().with_scale(Vec3::splat(128.)), + mesh: mesh_handle, + transform: Transform::from_translation(Vec3::new(96., 0., 0.)) + .with_scale(Vec3::splat(128.)), material: materials.add(ColorMaterial::from(texture_handle)), ..default() }); From f20c9ee0f5223189c8f3ca49e9bde2d7a17e7fa6 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 16 Aug 2022 20:46:46 +0000 Subject: [PATCH 04/15] fix: grammar and typo fixes in rendergraph docs (#5710) # Objective - fix a typo on RendGraph Docs ## Solution - fixed typo --- Co-authored-by: Carter Anderson --- crates/bevy_render/src/render_graph/graph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_graph/graph.rs b/crates/bevy_render/src/render_graph/graph.rs index 0633cb342a353..39d76120a4aa9 100644 --- a/crates/bevy_render/src/render_graph/graph.rs +++ b/crates/bevy_render/src/render_graph/graph.rs @@ -12,7 +12,7 @@ use std::{borrow::Cow, fmt::Debug}; use super::EdgeExistence; /// The render graph configures the modular, parallel and re-usable render logic. -/// It is a retained and stateless (nodes themselves may have internal state) structure, +/// It is a retained and stateless (nodes themselves may have their own internal state) structure, /// which can not be modified while it is executed by the graph runner. /// /// The `RenderGraphRunner` is responsible for executing the entire graph each frame. From f9104b73a2980717a0745c108d47c1ea573ddf2d Mon Sep 17 00:00:00 2001 From: Tomasz Galkowski Date: Tue, 16 Aug 2022 23:18:54 +0000 Subject: [PATCH 05/15] Use circle for breakout example (#5657) # Objective - Replace the square with a circle in the breakout example. - Fixes #4324, adopted from #4682 by @shaderduck. ## Solution - Uses the Mesh2D APIs to draw a circle. The collision still uses the AABB algorithm, but it seems to be working fine, and I haven't seen any odd looking cases. --- examples/games/breakout.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index 5677a4bf90337..c2d592b4c7e3c 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -3,6 +3,7 @@ use bevy::{ prelude::*, sprite::collide_aabb::{collide, Collision}, + sprite::MaterialMesh2dBundle, time::FixedTimestep, }; @@ -171,7 +172,12 @@ struct Scoreboard { } // Add the game's entities to our world -fn setup(mut commands: Commands, asset_server: Res) { +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + asset_server: Res, +) { // Camera commands.spawn_bundle(Camera2dBundle::default()); @@ -203,16 +209,10 @@ fn setup(mut commands: Commands, asset_server: Res) { commands .spawn() .insert(Ball) - .insert_bundle(SpriteBundle { - transform: Transform { - scale: BALL_SIZE, - translation: BALL_STARTING_POSITION, - ..default() - }, - sprite: Sprite { - color: BALL_COLOR, - ..default() - }, + .insert_bundle(MaterialMesh2dBundle { + mesh: meshes.add(shape::Circle::default().into()).into(), + material: materials.add(ColorMaterial::from(BALL_COLOR)), + transform: Transform::from_translation(BALL_STARTING_POSITION).with_scale(BALL_SIZE), ..default() }) .insert(Velocity(INITIAL_BALL_DIRECTION.normalize() * BALL_SPEED)); From 3221e569e059eaf133fb3d46c0b45725eca96d77 Mon Sep 17 00:00:00 2001 From: JoJoJet Date: Tue, 16 Aug 2022 23:40:24 +0000 Subject: [PATCH 06/15] Remove an outdated workaround for `impl Trait` (#5659) # Objective Rust 1.63 resolved [an issue](https://github.com/rust-lang/rust/issues/83701) that prevents you from combining explicit generic arguments with `impl Trait` arguments. Now, we no longer need to use dynamic dispatch to work around this. ## Migration Guide The methods `Schedule::get_stage` and `get_stage_mut` now accept `impl StageLabel` instead of `&dyn StageLabel`. ### Before ```rust let stage = schedule.get_stage_mut::(&MyLabel)?; ``` ### After ```rust let stage = schedule.get_stage_mut::(MyLabel)?; ``` --- crates/bevy_ecs/src/schedule/mod.rs | 29 +++++++++++++++-------------- crates/bevy_render/src/lib.rs | 12 ++++++------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 2e1bd167caedf..0ac1e8702d685 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -211,9 +211,10 @@ impl Schedule { ) } + let label = stage_label.as_label(); let stage = self - .get_stage_mut::(&stage_label) - .unwrap_or_else(move || stage_not_found(&stage_label.as_label())); + .get_stage_mut::(label) + .unwrap_or_else(move || stage_not_found(&label)); stage.add_system(system); self } @@ -278,14 +279,12 @@ impl Schedule { /// Panics if `label` refers to a non-existing stage, or if it's not of type `T`. pub fn stage &mut T>( &mut self, - label: impl StageLabel, + stage_label: impl StageLabel, func: F, ) -> &mut Self { - let stage = self.get_stage_mut::(&label).unwrap_or_else(move || { - panic!( - "stage '{:?}' does not exist or is the wrong type", - label.as_label() - ) + let label = stage_label.as_label(); + let stage = self.get_stage_mut::(label).unwrap_or_else(move || { + panic!("stage '{label:?}' does not exist or is the wrong type",) }); func(stage); self @@ -304,11 +303,12 @@ impl Schedule { /// # let mut schedule = Schedule::default(); /// # schedule.add_stage("my_stage", SystemStage::parallel()); /// # - /// let stage = schedule.get_stage::(&"my_stage").unwrap(); + /// let stage = schedule.get_stage::("my_stage").unwrap(); /// ``` - pub fn get_stage(&self, label: &dyn StageLabel) -> Option<&T> { + pub fn get_stage(&self, stage_label: impl StageLabel) -> Option<&T> { + let label = stage_label.as_label(); self.stages - .get(&label.as_label()) + .get(&label) .and_then(|stage| stage.downcast_ref::()) } @@ -325,11 +325,12 @@ impl Schedule { /// # let mut schedule = Schedule::default(); /// # schedule.add_stage("my_stage", SystemStage::parallel()); /// # - /// let stage = schedule.get_stage_mut::(&"my_stage").unwrap(); + /// let stage = schedule.get_stage_mut::("my_stage").unwrap(); /// ``` - pub fn get_stage_mut(&mut self, label: &dyn StageLabel) -> Option<&mut T> { + pub fn get_stage_mut(&mut self, stage_label: impl StageLabel) -> Option<&mut T> { + let label = stage_label.as_label(); self.stages - .get_mut(&label.as_label()) + .get_mut(&label) .and_then(|stage| stage.downcast_mut::()) } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 5efbb5ba9c03a..fbe5f14923970 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -249,7 +249,7 @@ impl Plugin for RenderPlugin { // prepare let prepare = render_app .schedule - .get_stage_mut::(&RenderStage::Prepare) + .get_stage_mut::(RenderStage::Prepare) .unwrap(); prepare.run(&mut render_app.world); } @@ -262,7 +262,7 @@ impl Plugin for RenderPlugin { // queue let queue = render_app .schedule - .get_stage_mut::(&RenderStage::Queue) + .get_stage_mut::(RenderStage::Queue) .unwrap(); queue.run(&mut render_app.world); } @@ -275,7 +275,7 @@ impl Plugin for RenderPlugin { // phase sort let phase_sort = render_app .schedule - .get_stage_mut::(&RenderStage::PhaseSort) + .get_stage_mut::(RenderStage::PhaseSort) .unwrap(); phase_sort.run(&mut render_app.world); } @@ -288,7 +288,7 @@ impl Plugin for RenderPlugin { // render let render = render_app .schedule - .get_stage_mut::(&RenderStage::Render) + .get_stage_mut::(RenderStage::Render) .unwrap(); render.run(&mut render_app.world); } @@ -301,7 +301,7 @@ impl Plugin for RenderPlugin { // cleanup let cleanup = render_app .schedule - .get_stage_mut::(&RenderStage::Cleanup) + .get_stage_mut::(RenderStage::Cleanup) .unwrap(); cleanup.run(&mut render_app.world); } @@ -335,7 +335,7 @@ struct ScratchMainWorld(World); fn extract(app_world: &mut World, render_app: &mut App) { let extract = render_app .schedule - .get_stage_mut::(&RenderStage::Extract) + .get_stage_mut::(RenderStage::Extract) .unwrap(); // temporarily add the app world to the render world as a resource From aed3232e38e2d700cbb06b782ed7242d0684c244 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Wed, 17 Aug 2022 00:21:15 +0000 Subject: [PATCH 07/15] bevy_reflect: Relax bounds on `Option` (#5658) # Objective The reflection impls on `Option` have the bound `T: Reflect + Clone`. This means that using `FromReflect` requires `Clone` even though we can normally get away with just `FromReflect`. ## Solution Update the bounds on `Option` to match that of `Vec`, where `T: FromReflect`. This helps remove a `Clone` implementation that may be undesired but added for the sole purpose of getting the code to compile. --- ## Changelog * Reflection on `Option` now has `T` bound by `FromReflect` rather than `Reflect + Clone` * Added a `FromReflect` impl for `Instant` ## Migration Guide If using `Option` with Bevy's reflection API, `T` now needs to implement `FromReflect` rather than just `Clone`. This can be achieved easily by simply deriving `FromReflect`: ```rust // OLD #[derive(Reflect, Clone)] struct Foo; let reflected: Box = Box::new(Some(Foo)); // NEW #[derive(Reflect, FromReflect)] struct Foo; let reflected: Box = Box::new(Some(Foo)); ``` > Note: You can still derive `Clone`, but it's not required in order to compile. --- crates/bevy_reflect/src/impls/std.rs | 39 +++++++++++++++++++------ crates/bevy_render/src/camera/camera.rs | 5 ++-- crates/bevy_time/src/time.rs | 4 +-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 7b66aba360249..90fa58dc372ec 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -76,6 +76,7 @@ impl_from_reflect_value!(String); impl_from_reflect_value!(HashSet); impl_from_reflect_value!(Range); impl_from_reflect_value!(Duration); +impl_from_reflect_value!(Instant); impl_from_reflect_value!(NonZeroI128); impl_from_reflect_value!(NonZeroU128); impl_from_reflect_value!(NonZeroIsize); @@ -585,13 +586,13 @@ impl Reflect for Cow<'static, str> { } } -impl GetTypeRegistration for Option { +impl GetTypeRegistration for Option { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::>() } } -impl Enum for Option { +impl Enum for Option { fn field(&self, _name: &str) -> Option<&dyn Reflect> { None } @@ -655,7 +656,7 @@ impl Enum for Option { } } -impl Reflect for Option { +impl Reflect for Option { #[inline] fn type_name(&self) -> &str { std::any::type_name::() @@ -741,7 +742,7 @@ impl Reflect for Option { #[inline] fn clone_value(&self) -> Box { - Box::new(self.clone()) + Box::new(Enum::clone_dynamic(self)) } fn reflect_hash(&self) -> Option { @@ -753,7 +754,7 @@ impl Reflect for Option { } } -impl FromReflect for Option { +impl FromReflect for Option { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { match dyn_enum.variant_name() { @@ -762,7 +763,7 @@ impl FromReflect for Option { .field_at(0) .expect("Field at index 0 should exist") .clone_value(); - let field = field.take::().unwrap_or_else(|_| { + let field = T::from_reflect(field.as_ref()).unwrap_or_else(|| { panic!( "Field at index 0 should be of type {}", std::any::type_name::() @@ -783,7 +784,7 @@ impl FromReflect for Option { } } -impl Typed for Option { +impl Typed for Option { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { @@ -827,10 +828,12 @@ impl FromReflect for Cow<'static, str> { #[cfg(test)] mod tests { + use crate as bevy_reflect; use crate::{ - Enum, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, + Enum, FromReflect, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, + VariantType, }; - use bevy_utils::HashMap; + use bevy_utils::{HashMap, Instant}; use std::f32::consts::{PI, TAU}; #[test] @@ -939,6 +942,17 @@ mod tests { assert_eq!(Some(321), value); } + #[test] + fn option_should_from_reflect() { + #[derive(Reflect, FromReflect, PartialEq, Debug)] + struct Foo(usize); + + let expected = Some(Foo(123)); + let output = as FromReflect>::from_reflect(&expected).unwrap(); + + assert_eq!(expected, output); + } + #[test] fn option_should_impl_typed() { type MyOption = Option; @@ -979,4 +993,11 @@ mod tests { let forty_two: std::num::NonZeroUsize = crate::FromReflect::from_reflect(a).unwrap(); assert_eq!(forty_two, std::num::NonZeroUsize::new(42).unwrap()); } + + #[test] + fn instant_should_from_reflect() { + let expected = Instant::now(); + let output = ::from_reflect(&expected).unwrap(); + assert_eq!(expected, output); + } } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index f81c5b6ad1aed..0bfd3c5a7de03 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -19,6 +19,7 @@ use bevy_ecs::{ }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; +use bevy_reflect::FromReflect; use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; @@ -32,7 +33,7 @@ use wgpu::Extent3d; /// You can overlay multiple cameras in a single window using viewports to create effects like /// split screen, minimaps, and character viewers. // TODO: remove reflect_value when possible -#[derive(Reflect, Debug, Clone, Serialize, Deserialize)] +#[derive(Reflect, FromReflect, Debug, Clone, Serialize, Deserialize)] #[reflect_value(Default, Serialize, Deserialize)] pub struct Viewport { /// The physical position to render this viewport to within the [`RenderTarget`] of this [`Camera`]. @@ -71,7 +72,7 @@ pub struct ComputedCameraValues { target_info: Option, } -#[derive(Component, Debug, Reflect, Clone)] +#[derive(Component, Debug, Reflect, FromReflect, Clone)] #[reflect(Component)] pub struct Camera { /// If set, this camera will render to the given [`Viewport`] rectangle within the configured [`RenderTarget`]. diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 8cb864882f25e..1835f841c93a7 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -1,9 +1,9 @@ use bevy_ecs::{reflect::ReflectResource, system::Resource}; -use bevy_reflect::Reflect; +use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{Duration, Instant}; /// Tracks elapsed time since the last update and since the App has started -#[derive(Resource, Reflect, Debug, Clone)] +#[derive(Resource, Reflect, FromReflect, Debug, Clone)] #[reflect(Resource)] pub struct Time { delta: Duration, From 56fc1dfe775e7e3dbb7230422f143f14aa89db75 Mon Sep 17 00:00:00 2001 From: Verte Date: Wed, 17 Aug 2022 14:00:10 +0000 Subject: [PATCH 08/15] Correctly use as_hsla_f32 in Add and AddAssign, fixes #5543 (#5546) Probably a copy-paste error, but `Add` and `AddAssign` should use `rhs.as_hlsa_f32()` instead of `rhs.as_linear_rgba_f32()` when the LHS is a `Color::Hsla`. Fixes #5543. Co-authored-by: Verte <105466627+vertesians@users.noreply.github.com> --- crates/bevy_render/src/color/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/color/mod.rs b/crates/bevy_render/src/color/mod.rs index 51c3a21a7cdc9..0362d8386a9e4 100644 --- a/crates/bevy_render/src/color/mod.rs +++ b/crates/bevy_render/src/color/mod.rs @@ -745,7 +745,7 @@ impl AddAssign for Color { lightness, alpha, } => { - let rhs = rhs.as_linear_rgba_f32(); + let rhs = rhs.as_hsla_f32(); *hue += rhs[0]; *saturation += rhs[1]; *lightness += rhs[2]; @@ -794,7 +794,7 @@ impl Add for Color { lightness, alpha, } => { - let rhs = rhs.as_linear_rgba_f32(); + let rhs = rhs.as_hsla_f32(); Color::Hsla { hue: hue + rhs[0], saturation: saturation + rhs[1], From f0c512731b46723d1ab8ec9203d19891596f7f86 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Thu, 18 Aug 2022 18:31:12 +0000 Subject: [PATCH 09/15] SystemParam for the name of the system you are currently in (#5731) # Objective - Similar to `SystemChangeTick`, probably somewhat useful for debugging messages. --- ## Changelog - Added `SystemName` which copies the `SystemMeta::name` field so it can be accessed within a system. --- crates/bevy_ecs/src/system/mod.rs | 1 + crates/bevy_ecs/src/system/system_param.rs | 86 ++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index ec3fd6ea78553..aef76c277a5ec 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -59,6 +59,7 @@ //! - [`NonSendMut`] and `Option` //! - [`&World`](crate::world::World) //! - [`RemovedComponents`] +//! - [`SystemName`] //! - [`SystemChangeTick`] //! - [`Archetypes`](crate::archetype::Archetypes) (Provides Archetype metadata) //! - [`Bundles`](crate::bundle::Bundles) (Provides Bundles metadata) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 2356d732a8dda..8762bd64c4255 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -16,6 +16,7 @@ pub use bevy_ecs_macros::SystemParam; use bevy_ecs_macros::{all_tuples, impl_param_set}; use bevy_ptr::UnsafeCellDeref; use std::{ + borrow::Cow, fmt::Debug, marker::PhantomData, ops::{Deref, DerefMut}, @@ -1304,6 +1305,91 @@ impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState { } } +/// Name of the system that corresponds to this [`crate::system::SystemState`]. +/// +/// This is not a reliable identifier, it is more so useful for debugging +/// purposes of finding where a system parameter is being used incorrectly. +pub struct SystemName<'s> { + name: &'s str, +} + +impl<'s> SystemName<'s> { + pub fn name(&self) -> &str { + self.name + } +} + +impl<'s> Deref for SystemName<'s> { + type Target = str; + fn deref(&self) -> &Self::Target { + self.name() + } +} + +impl<'s> AsRef for SystemName<'s> { + fn as_ref(&self) -> &str { + self.name() + } +} + +impl<'s> From> for &'s str { + fn from(name: SystemName<'s>) -> &'s str { + name.name + } +} + +impl<'s> std::fmt::Debug for SystemName<'s> { + #[inline(always)] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_tuple("SystemName").field(&self.name()).finish() + } +} + +impl<'s> std::fmt::Display for SystemName<'s> { + #[inline(always)] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.name(), f) + } +} + +impl<'s> SystemParam for SystemName<'s> { + type Fetch = SystemNameState; +} + +// SAFETY: Only reads internal system state +unsafe impl ReadOnlySystemParamFetch for SystemNameState {} + +/// The [`SystemParamState`] of [`SystemName`]. +#[doc(hidden)] +pub struct SystemNameState { + name: Cow<'static, str>, +} + +// SAFETY: no component value access +unsafe impl SystemParamState for SystemNameState { + fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self { + Self { + name: system_meta.name.clone(), + } + } +} + +impl<'w, 's> SystemParamFetch<'w, 's> for SystemNameState { + type Item = SystemName<'s>; + + #[inline] + unsafe fn get_param( + state: &'s mut Self, + _system_meta: &SystemMeta, + _world: &'w World, + _change_tick: u32, + ) -> Self::Item { + SystemName { + name: state.name.as_ref(), + } + } +} + macro_rules! impl_system_param_tuple { ($($param: ident),*) => { impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { From 00508d110adfd3964a5432a8944f0653a5ac1901 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 18 Aug 2022 18:53:07 +0000 Subject: [PATCH 10/15] bevy_reflect: Add `FromReflect` to the prelude (#5720) # Objective `FromReflect` is a commonly used component to the Reflect API. It's required as a bound for reflecting things like `Vec` and `HashMap` and is generally useful (if not necessary) to derive on most structs or enums. Currently, however, it is not exported in `bevy_reflect`'s prelude. This means a module that uses `bevy_reflect` might have the following two lines: ```rust use bevy_reflect::prelude::*; use bevy_reflect::FromReflect; ``` Additionally, users of the full engine might need to put: ```rust use bevy::prelude::*; use bevy::reflect::FromReflect; ``` ## Solution Add `FromReflect` to the prelude of `bevy_reflect`. --- ## Changelog - Added `FromReflect` to the prelude of `bevy_reflect` --- crates/bevy_reflect/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index f6dbc37ce67f3..ef9e05ccd3cff 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -35,7 +35,7 @@ pub mod prelude { pub use crate::std_traits::*; #[doc(hidden)] pub use crate::{ - reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize, + reflect_trait, FromReflect, GetField, GetTupleStructField, Reflect, ReflectDeserialize, ReflectSerialize, Struct, TupleStruct, }; } From 00323b3048443b821df759d468d9d4c3e0515534 Mon Sep 17 00:00:00 2001 From: Nathan Ward Date: Thu, 18 Aug 2022 18:53:08 +0000 Subject: [PATCH 11/15] Better error message for `World::resource_scope` (#5727) # Objective - Fixes #5365 - The `assert!()` when the resource from `World::resource_scope` is inserted into the world is not descriptive. ## Solution - Add more context to the assert inside of `World::resource_scope` when the `FnOnce` param inserts the resource. --- crates/bevy_ecs/src/world/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 314bea0d4d82b..661ef2ddd5813 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1111,7 +1111,10 @@ impl World { }, }; let result = f(self, value_mut); - assert!(!self.contains_resource::()); + assert!(!self.contains_resource::(), + "Resource `{}` was inserted during a call to World::resource_scope.\n\ + This is not allowed as the original resource is reinserted to the world after the FnOnce param is invoked.", + std::any::type_name::()); let resource_archetype = self.archetypes.resource_mut(); let unique_components = resource_archetype.unique_components_mut(); From 1c6be94f4fceb36ed0b52a5807bf431b1890b390 Mon Sep 17 00:00:00 2001 From: pwygab <88221256+merelymyself@users.noreply.github.com> Date: Thu, 18 Aug 2022 18:53:09 +0000 Subject: [PATCH 12/15] Correctly parse labels with '#' (#5729) # Objective - Fixes #5707 ## Solution - Used `splitn` instead of `split` to collect the rest of the string into the label after the first '#'. --- --- crates/bevy_asset/src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/path.rs b/crates/bevy_asset/src/path.rs index d47bca18e7617..1cfbc87dcc33d 100644 --- a/crates/bevy_asset/src/path.rs +++ b/crates/bevy_asset/src/path.rs @@ -154,7 +154,7 @@ impl<'a, 'b> From<&'a AssetPath<'b>> for AssetPathId { impl<'a> From<&'a str> for AssetPath<'a> { fn from(asset_path: &'a str) -> Self { - let mut parts = asset_path.split('#'); + let mut parts = asset_path.splitn(2, '#'); let path = Path::new(parts.next().expect("Path must be set.")); let label = parts.next(); AssetPath { From 681c9c6dc859be52565609e56ce9dc7f6f1c1f12 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Thu, 18 Aug 2022 21:54:40 +0000 Subject: [PATCH 13/15] bevy_pbr: Fix tangent and normal normalization (#5666) # Objective - Morten Mikkelsen clarified that the world normal and tangent must be normalized in the vertex stage and the interpolated values must not be normalized in the fragment stage. This is in order to match the mikktspace approach exactly. - Fixes #5514 by ensuring the tangent basis matrix (TBN) is orthonormal ## Solution - Normalize the world normal in the vertex stage and not the fragment stage - Normalize the world tangent xyz in the vertex stage - Take into account the sign of the determinant of the local to world matrix when calculating the bitangent --- ## Changelog - Fixed - scaling a model that uses normal mapping now has correct lighting again --- crates/bevy_pbr/src/render/mesh.rs | 16 ++++-- .../bevy_pbr/src/render/mesh_functions.wgsl | 49 ++++++++++++++----- crates/bevy_pbr/src/render/mesh_types.wgsl | 2 + crates/bevy_pbr/src/render/pbr_functions.wgsl | 10 +++- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 082e20b733585..3d70cfd110eb5 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -9,7 +9,7 @@ use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem, SystemState}, }; -use bevy_math::{Mat4, Vec2}; +use bevy_math::{Mat3A, Mat4, Vec2}; use bevy_reflect::TypeUuid; use bevy_render::{ extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, @@ -117,6 +117,9 @@ bitflags::bitflags! { #[repr(transparent)] struct MeshFlags: u32 { const SHADOW_RECEIVER = (1 << 0); + // Indicates the sign of the determinant of the 3x3 model matrix. If the sign is positive, + // then the flag should be set, else it should not be set. + const SIGN_DETERMINANT_MODEL_3X3 = (1 << 31); const NONE = 0; const UNINITIALIZED = 0xFFFF; } @@ -143,13 +146,16 @@ pub fn extract_meshes( for (entity, _, transform, handle, not_receiver, not_caster) in visible_meshes { let transform = transform.compute_matrix(); - let shadow_receiver_flags = if not_receiver.is_some() { - MeshFlags::empty().bits + let mut flags = if not_receiver.is_some() { + MeshFlags::empty() } else { - MeshFlags::SHADOW_RECEIVER.bits + MeshFlags::SHADOW_RECEIVER }; + if Mat3A::from_mat4(transform).determinant().is_sign_positive() { + flags |= MeshFlags::SIGN_DETERMINANT_MODEL_3X3; + } let uniform = MeshUniform { - flags: shadow_receiver_flags, + flags: flags.bits, transform, inverse_transpose_model: transform.inverse().transpose(), }; diff --git a/crates/bevy_pbr/src/render/mesh_functions.wgsl b/crates/bevy_pbr/src/render/mesh_functions.wgsl index 20c763bd22d79..40779b28d5c6e 100644 --- a/crates/bevy_pbr/src/render/mesh_functions.wgsl +++ b/crates/bevy_pbr/src/render/mesh_functions.wgsl @@ -17,20 +17,47 @@ fn mesh_position_local_to_clip(model: mat4x4, vertex_position: vec4) - } fn mesh_normal_local_to_world(vertex_normal: vec3) -> vec3 { - return mat3x3( - mesh.inverse_transpose_model[0].xyz, - mesh.inverse_transpose_model[1].xyz, - mesh.inverse_transpose_model[2].xyz - ) * vertex_normal; + // NOTE: The mikktspace method of normal mapping requires that the world normal is + // re-normalized in the vertex shader to match the way mikktspace bakes vertex tangents + // and normal maps so that the exact inverse process is applied when shading. Blender, Unity, + // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code + // unless you really know what you are doing. + // http://www.mikktspace.com/ + return normalize( + mat3x3( + mesh.inverse_transpose_model[0].xyz, + mesh.inverse_transpose_model[1].xyz, + mesh.inverse_transpose_model[2].xyz + ) * vertex_normal + ); +} + +// Calculates the sign of the determinant of the 3x3 model matrix based on a +// mesh flag +fn sign_determinant_model_3x3() -> f32 { + // bool(u32) is false if 0u else true + // f32(bool) is 1.0 if true else 0.0 + // * 2.0 - 1.0 remaps 0.0 or 1.0 to -1.0 or 1.0 respectively + return f32(bool(mesh.flags & MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT)) * 2.0 - 1.0; } fn mesh_tangent_local_to_world(model: mat4x4, vertex_tangent: vec4) -> vec4 { + // NOTE: The mikktspace method of normal mapping requires that the world tangent is + // re-normalized in the vertex shader to match the way mikktspace bakes vertex tangents + // and normal maps so that the exact inverse process is applied when shading. Blender, Unity, + // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code + // unless you really know what you are doing. + // http://www.mikktspace.com/ return vec4( - mat3x3( - model[0].xyz, - model[1].xyz, - model[2].xyz - ) * vertex_tangent.xyz, - vertex_tangent.w + normalize( + mat3x3( + model[0].xyz, + model[1].xyz, + model[2].xyz + ) * vertex_tangent.xyz + ), + // NOTE: Multiplying by the sign of the determinant of the 3x3 model matrix accounts for + // situations such as negative scaling. + vertex_tangent.w * sign_determinant_model_3x3() ); } diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index e0bfd2c2899cd..d44adbc2bb13f 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -14,3 +14,5 @@ struct SkinnedMesh { #endif let MESH_FLAGS_SHADOW_RECEIVER_BIT: u32 = 1u; +// 2^31 - if the flag is set, the sign is positive, else it is negative +let MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT: u32 = 2147483648u; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index cd922bf74412c..a77c065fca221 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -15,7 +15,13 @@ fn prepare_normal( #endif is_front: bool, ) -> vec3 { - var N: vec3 = normalize(world_normal); + // NOTE: The mikktspace method of normal mapping explicitly requires that the world normal NOT + // be re-normalized in the fragment shader. This is primarily to match the way mikktspace + // bakes vertex tangents and normal maps so that this is the exact inverse. Blender, Unity, + // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code + // unless you really know what you are doing. + // http://www.mikktspace.com/ + var N: vec3 = world_normal; #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP @@ -236,7 +242,7 @@ fn pbr( fn tone_mapping(in: vec4) -> vec4 { // tone_mapping return vec4(reinhard_luminance(in.rgb), in.a); - + // Gamma correction. // Not needed with sRGB buffer // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2)); From 04538fd802e99080210a587e9a4805d098eb2d75 Mon Sep 17 00:00:00 2001 From: Tomasz Galkowski Date: Sat, 20 Aug 2022 19:55:53 +0000 Subject: [PATCH 14/15] fixes the types for Vec3 and Quat in scene example to remove WARN from the logs (#5751) # Objective - Fixes #5745. ## Solution - Changes the Vec3 and Quat types. --- assets/scenes/load_scene_example.scn.ron | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index 1f9de9dae7da8..981c85d724d5e 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -6,15 +6,15 @@ "type": "bevy_transform::components::transform::Transform", "struct": { "translation": { - "type": "glam::vec3::Vec3", + "type": "glam::f32::vec3::Vec3", "value": (0.0, 0.0, 0.0), }, "rotation": { - "type": "glam::quat::Quat", + "type": "glam::f32::scalar::quat::Quat", "value": (0.0, 0.0, 0.0, 1.0), }, "scale": { - "type": "glam::vec3::Vec3", + "type": "glam::f32::vec3::Vec3", "value": (1.0, 1.0, 1.0), }, }, From cde5ae8104a0473ffe82c0ba2380bdd96a0b17ab Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sun, 21 Aug 2022 00:45:49 +0000 Subject: [PATCH 15/15] bevy_ecs: Use 32-bit entity ID cursor on platforms without AtomicI64 (#4452) # Objective - Fixes #4451 ## Solution - Conditionally compile entity ID cursor as `AtomicI32` when compiling on a platform that does not support 64-bit atomics. - This effectively raises the MSRV to 1.60 as it uses a `#[cfg]` that was only just stabilized there. (should this be noted in changelog?) --- ## Changelog - Added `bevy_ecs` support for platforms without 64-bit atomic ints ## Migration Guide N/A --- crates/bevy_ecs/src/entity/mod.rs | 51 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index e022483fa836c..2da5d5e0c556f 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -40,11 +40,20 @@ pub use self::serde::*; pub use map_entities::*; use crate::{archetype::ArchetypeId, storage::SparseSetIndex}; -use std::{ - convert::TryFrom, - fmt, mem, - sync::atomic::{AtomicI64, Ordering}, -}; +use std::{convert::TryFrom, fmt, mem, sync::atomic::Ordering}; + +#[cfg(target_has_atomic = "64")] +use std::sync::atomic::AtomicI64 as AtomicIdCursor; +#[cfg(target_has_atomic = "64")] +type IdCursor = i64; + +/// Most modern platforms support 64-bit atomics, but some less-common platforms +/// do not. This fallback allows compilation using a 32-bit cursor instead, with +/// the caveat that some conversions may fail (and panic) at runtime. +#[cfg(not(target_has_atomic = "64"))] +use std::sync::atomic::AtomicIsize as AtomicIdCursor; +#[cfg(not(target_has_atomic = "64"))] +type IdCursor = isize; /// Lightweight identifier of an [entity](crate::entity). /// @@ -291,7 +300,7 @@ pub struct Entities { /// /// Once `flush()` is done, `free_cursor` will equal `pending.len()`. pending: Vec, - free_cursor: AtomicI64, + free_cursor: AtomicIdCursor, /// Stores the number of free entities for [`len`](Entities::len) len: u32, } @@ -304,8 +313,12 @@ impl Entities { // Use one atomic subtract to grab a range of new IDs. The range might be // entirely nonnegative, meaning all IDs come from the freelist, or entirely // negative, meaning they are all new IDs to allocate, or a mix of both. - let range_end = self.free_cursor.fetch_sub(count as i64, Ordering::Relaxed); - let range_start = range_end - count as i64; + let range_end = self + .free_cursor + // Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics + // and use AtomicIsize instead (see note on `IdCursor`). + .fetch_sub(IdCursor::try_from(count).unwrap(), Ordering::Relaxed); + let range_start = range_end - IdCursor::try_from(count).unwrap(); let freelist_range = range_start.max(0) as usize..range_end.max(0) as usize; @@ -322,7 +335,7 @@ impl Entities { // In this example, we truncate the end to 0, leaving us with `-3..0`. // Then we negate these values to indicate how far beyond the end of `meta.end()` // to go, yielding `meta.len()+0 .. meta.len()+3`. - let base = self.meta.len() as i64; + let base = self.meta.len() as IdCursor; let new_id_end = u32::try_from(base - range_start).expect("too many entities"); @@ -359,7 +372,7 @@ impl Entities { // and farther beyond `meta.len()`. Entity { generation: 0, - id: u32::try_from(self.meta.len() as i64 - n).expect("too many entities"), + id: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"), } } } @@ -377,7 +390,7 @@ impl Entities { self.verify_flushed(); self.len += 1; if let Some(id) = self.pending.pop() { - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; Entity { generation: self.meta[id as usize].generation, @@ -399,14 +412,14 @@ impl Entities { let loc = if entity.id as usize >= self.meta.len() { self.pending.extend((self.meta.len() as u32)..entity.id); - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY); self.len += 1; None } else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) { self.pending.swap_remove(index); - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; self.len += 1; None @@ -430,14 +443,14 @@ impl Entities { let result = if entity.id as usize >= self.meta.len() { self.pending.extend((self.meta.len() as u32)..entity.id); - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY); self.len += 1; AllocAtWithoutReplacement::DidNotExist } else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) { self.pending.swap_remove(index); - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; self.len += 1; AllocAtWithoutReplacement::DidNotExist @@ -472,7 +485,7 @@ impl Entities { self.pending.push(entity.id); - let new_free_cursor = self.pending.len() as i64; + let new_free_cursor = self.pending.len() as IdCursor; *self.free_cursor.get_mut() = new_free_cursor; self.len -= 1; Some(loc) @@ -483,7 +496,9 @@ impl Entities { self.verify_flushed(); let freelist_size = *self.free_cursor.get_mut(); - let shortfall = additional as i64 - freelist_size; + // Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics + // and use AtomicIsize instead (see note on `IdCursor`). + let shortfall = IdCursor::try_from(additional).unwrap() - freelist_size; if shortfall > 0 { self.meta.reserve(shortfall as usize); } @@ -540,7 +555,7 @@ impl Entities { } fn needs_flush(&mut self) -> bool { - *self.free_cursor.get_mut() != self.pending.len() as i64 + *self.free_cursor.get_mut() != self.pending.len() as IdCursor } /// Allocates space for entities previously reserved with `reserve_entity` or