Commit Graph

7160 Commits

Author SHA1 Message Date
robtfm
c617fc49ae
fix distinct directional lights per view (#19147)
# Objective

after #15156 it seems like using distinct directional lights on
different views is broken (and will probably break spotlights too). fix
them

## Solution

the reason is a bit hairy so with an example:

- camera 0 on layer 0
- camera 1 on layer 1
- dir light 0 on layer 0 (2 cascades)
- dir light 1 on layer 1 (2 cascades)

in render/lights.rs:
- outside of any view loop, 
- we count the total number of shadow casting directional light cascades
(4) and assign an incrementing `depth_texture_base_index` for each (0-1
for one light, 2-3 for the other, depending on iteration order) (line
1034)
- allocate a texture array for the total number of cascades plus
spotlight maps (4) (line 1106)

- in the view loop, for directional lights we 
  - skip lights that don't intersect on renderlayers (line 1440)
- assign an incrementing texture layer to each light/cascade starting
from 0 (resets to 0 per view) (assigning 0 and 1 each time for the 2
cascades of the intersecting light) (line 1509, init at 1421)

then in the rendergraph:
- camera 0 renders the shadow map for light 0 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

- camera 1 renders the shadow map for light 1 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

issues:
- one of the views uses empty shadow maps (bug)
- we allocated a texture layer per cascade per light, even though not
all lights are used on all views (just inefficient)
- I think we're allocating texture layers even for lights with
`shadows_enabled: false` (just inefficient)

solution:
- calculate upfront the view with the largest number of directional
cascades
- allocate this many layers (plus layers for spotlights) in the texture
array
- keep using texture layers 0..n in the per-view loop, but build
GpuLights.gpu_directional_lights within the loop too so it refers to the
same layers we render to

nice side effects: 
- we can now use `max_texture_array_layers / MAX_CASCADES_PER_LIGHT`
shadow-casting directional lights per view, rather than overall.
- we can remove the `GpuDirectionalLight::skip` field, since the gpu
lights struct is constructed per view

a simpler approach would be to keep everything the same, and just
increment the texture layer index in the view loop even for
non-intersecting lights. this pr reduces the total shadowmap vram used
as well and isn't *much* extra complexity. but if we want something less
risky/intrusive for 16.1 that would be the way.

## Testing

i edited the split screen example to put separate lights on layer 1 and
layer 2, and put the plane and fox on both layers (using lots of
unrelated code for render layer propagation from #17575).
without the fix the directional shadows will only render on one of the
top 2 views even though there are directional lights on both layers.

```rs
//! Renders two cameras to the same window to accomplish "split screen".

use std::f32::consts::PI;

use bevy::{
    pbr::CascadeShadowConfigBuilder, prelude::*, render:📷:Viewport, window::WindowResized,
};
use bevy_render::view::RenderLayers;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(HierarchyPropagatePlugin::<RenderLayers>::default())
        .add_systems(Startup, setup)
        .add_systems(Update, (set_camera_viewports, button_system))
        .run();
}

/// set up a simple 3D scene
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let all_layers = RenderLayers::layer(1).with(2).with(3).with(4);

    // plane
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))),
        MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
        all_layers.clone()
    ));

    commands.spawn((
        SceneRoot(
            asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
        ),
        Propagate(all_layers.clone()),
    ));

    // Light
    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(1),
    ));

    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(2),
    ));

    // Cameras and their dedicated UI
    for (index, (camera_name, camera_pos)) in [
        ("Player 1", Vec3::new(0.0, 200.0, -150.0)),
        ("Player 2", Vec3::new(150.0, 150., 50.0)),
        ("Player 3", Vec3::new(100.0, 150., -150.0)),
        ("Player 4", Vec3::new(-100.0, 80., 150.0)),
    ]
    .iter()
    .enumerate()
    {
        let camera = commands
            .spawn((
                Camera3d::default(),
                Transform::from_translation(*camera_pos).looking_at(Vec3::ZERO, Vec3::Y),
                Camera {
                    // Renders cameras with different priorities to prevent ambiguities
                    order: index as isize,
                    ..default()
                },
                CameraPosition {
                    pos: UVec2::new((index % 2) as u32, (index / 2) as u32),
                },
                RenderLayers::layer(index+1)
            ))
            .id();

        // Set up UI
        commands
            .spawn((
                UiTargetCamera(camera),
                Node {
                    width: Val::Percent(100.),
                    height: Val::Percent(100.),
                    ..default()
                },
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new(*camera_name),
                    Node {
                        position_type: PositionType::Absolute,
                        top: Val::Px(12.),
                        left: Val::Px(12.),
                        ..default()
                    },
                ));
                buttons_panel(parent);
            });
    }

    fn buttons_panel(parent: &mut ChildSpawnerCommands) {
        parent
            .spawn(Node {
                position_type: PositionType::Absolute,
                width: Val::Percent(100.),
                height: Val::Percent(100.),
                display: Display::Flex,
                flex_direction: FlexDirection::Row,
                justify_content: JustifyContent::SpaceBetween,
                align_items: AlignItems::Center,
                padding: UiRect::all(Val::Px(20.)),
                ..default()
            })
            .with_children(|parent| {
                rotate_button(parent, "<", Direction::Left);
                rotate_button(parent, ">", Direction::Right);
            });
    }

    fn rotate_button(parent: &mut ChildSpawnerCommands, caption: &str, direction: Direction) {
        parent
            .spawn((
                RotateCamera(direction),
                Button,
                Node {
                    width: Val::Px(40.),
                    height: Val::Px(40.),
                    border: UiRect::all(Val::Px(2.)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BorderColor(Color::WHITE),
                BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
            ))
            .with_children(|parent| {
                parent.spawn(Text::new(caption));
            });
    }
}

#[derive(Component)]
struct CameraPosition {
    pos: UVec2,
}

#[derive(Component)]
struct RotateCamera(Direction);

enum Direction {
    Left,
    Right,
}

fn set_camera_viewports(
    windows: Query<&Window>,
    mut resize_events: EventReader<WindowResized>,
    mut query: Query<(&CameraPosition, &mut Camera)>,
) {
    // We need to dynamically resize the camera's viewports whenever the window size changes
    // so then each camera always takes up half the screen.
    // A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
    for resize_event in resize_events.read() {
        let window = windows.get(resize_event.window).unwrap();
        let size = window.physical_size() / 2;

        for (camera_position, mut camera) in &mut query {
            camera.viewport = Some(Viewport {
                physical_position: camera_position.pos * size,
                physical_size: size,
                ..default()
            });
        }
    }
}

fn button_system(
    interaction_query: Query<
        (&Interaction, &ComputedNodeTarget, &RotateCamera),
        (Changed<Interaction>, With<Button>),
    >,
    mut camera_query: Query<&mut Transform, With<Camera>>,
) {
    for (interaction, computed_target, RotateCamera(direction)) in &interaction_query {
        if let Interaction::Pressed = *interaction {
            // Since TargetCamera propagates to the children, we can use it to find
            // which side of the screen the button is on.
            if let Some(mut camera_transform) = computed_target
                .camera()
                .and_then(|camera| camera_query.get_mut(camera).ok())
            {
                let angle = match direction {
                    Direction::Left => -0.1,
                    Direction::Right => 0.1,
                };
                camera_transform.rotate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, angle));
            }
        }
    }
}








use std::marker::PhantomData;

use bevy::{
    app::{App, Plugin, Update},
    ecs::query::QueryFilter,
    prelude::{
        Changed, Children, Commands, Component, Entity, Local, Query,
        RemovedComponents, SystemSet, With, Without,
    },
};

/// Causes the inner component to be added to this entity and all children.
/// A child with a Propagate<C> component of it's own will override propagation from
/// that point in the tree
#[derive(Component, Clone, PartialEq)]
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);

/// Internal struct for managing propagation
#[derive(Component, Clone, PartialEq)]
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);

/// Stops the output component being added to this entity.
/// Children will still inherit the component from this entity or its parents
#[derive(Component, Default)]
pub struct PropagateOver<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

/// Stops the propagation at this entity. Children will not inherit the component.
#[derive(Component, Default)]
pub struct PropagateStop<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

pub struct HierarchyPropagatePlugin<C: Component + Clone + PartialEq, F: QueryFilter = ()> {
    _p: PhantomData<fn() -> (C, F)>,
}

impl<C: Component + Clone + PartialEq, F: QueryFilter> Default for HierarchyPropagatePlugin<C, F> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
pub struct PropagateSet<C: Component + Clone + PartialEq> {
    _p: PhantomData<fn() -> C>,
}

impl<C: Component + Clone + PartialEq> std::fmt::Debug for PropagateSet<C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PropagateSet")
            .field("_p", &self._p)
            .finish()
    }
}

impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
impl<C: Component + Clone + PartialEq> std:#️⃣:Hash for PropagateSet<C> {
    fn hash<H: std:#️⃣:Hasher>(&self, state: &mut H) {
        self._p.hash(state);
    }
}

impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static> Plugin
    for HierarchyPropagatePlugin<C, F>
{
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            (
                update_source::<C, F>,
                update_stopped::<C, F>,
                update_reparented::<C, F>,
                propagate_inherited::<C, F>,
                propagate_output::<C, F>,
            )
                .chain()
                .in_set(PropagateSet::<C>::default()),
        );
    }
}

pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<(Entity, &Propagate<C>), (Changed<Propagate<C>>, Without<PropagateStop<C>>)>,
    mut removed: RemovedComponents<Propagate<C>>,
) {
    for (entity, source) in &changed {
        commands
            .entity(entity)
            .try_insert(Inherited(source.0.clone()));
    }

    for removed in removed.read() {
        if let Ok(mut commands) = commands.get_entity(removed) {
            commands.remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn update_stopped<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    q: Query<Entity, (With<Inherited<C>>, F, With<PropagateStop<C>>)>,
) {
    for entity in q.iter() {
        let mut cmds = commands.entity(entity);
        cmds.remove::<Inherited<C>>();
    }
}

pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    moved: Query<
        (Entity, &ChildOf, Option<&Inherited<C>>),
        (
            Changed<ChildOf>,
            Without<Propagate<C>>,
            Without<PropagateStop<C>>,
            F,
        ),
    >,
    parents: Query<&Inherited<C>>,
) {
    for (entity, parent, maybe_inherited) in &moved {
        if let Ok(inherited) = parents.get(parent.parent()) {
            commands.entity(entity).try_insert(inherited.clone());
        } else if maybe_inherited.is_some() {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (&Inherited<C>, &Children),
        (Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
    >,
    recurse: Query<
        (Option<&Children>, Option<&Inherited<C>>),
        (Without<Propagate<C>>, Without<PropagateStop<C>>, F),
    >,
    mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
    mut removed: RemovedComponents<Inherited<C>>,
) {
    // gather changed
    for (inherited, children) in &changed {
        to_process.extend(
            children
                .iter()
                .map(|child| (child, Some(inherited.clone()))),
        );
    }

    // and removed
    for entity in removed.read() {
        if let Ok((Some(children), _)) = recurse.get(entity) {
            to_process.extend(children.iter().map(|child| (child, None)))
        }
    }

    // propagate
    while let Some((entity, maybe_inherited)) = (*to_process).pop() {
        let Ok((maybe_children, maybe_current)) = recurse.get(entity) else {
            continue;
        };

        if maybe_current == maybe_inherited.as_ref() {
            continue;
        }

        if let Some(children) = maybe_children {
            to_process.extend(
                children
                    .iter()
                    .map(|child| (child, maybe_inherited.clone())),
            );
        }

        if let Some(inherited) = maybe_inherited {
            commands.entity(entity).try_insert(inherited.clone());
        } else {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (Entity, &Inherited<C>, Option<&C>),
        (Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
    >,
) {
    for (entity, inherited, maybe_current) in &changed {
        if maybe_current.is_some_and(|c| &inherited.0 == c) {
            continue;
        }

        commands.entity(entity).try_insert(inherited.0.clone());
    }
}
```
2025-05-30 19:35:55 +00:00
Manuel Brea Carreras
3902804114
Fix #19219 by moving observer triggers out of resource_scope (#19221)
# Objective

Fixes #19219 

## Solution

Instead of calling `world.commands().trigger` and
`world.commands().trigger_targets` whenever each scene is spawned, save
the `instance_id` and optional parent entity to perform all such calls
at the end. This prevents the potential flush of the world command queue
that can happen if `add_child` is called from causing the crash.

## Testing

- Did you test these changes? If so, how?
- Verified that I can no longer reproduce the bug with the instructions
at #19219.
  - Ran `bevy_scene` tests
- Visually verified that the following examples still run as expected
`many_foxes`, `scene` . (should I test any more?)
- Are there any parts that need more testing?
- Pending to run `cargo test` at the root to test that all examples
still build; I will update the PR when that's done
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run bevy as usual
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - N/a (tested on Linux/wayland but it shouldn't be relevant)

---
2025-05-30 19:33:47 +00:00
eugineerd
0694743d8d
Fix EntityCloner replacing required components. (#19326)
# Objective
Fix #19324

## Solution
`EntityCloner` replaces required components when filtering. This is
unexpected when comparing with the way the rest of bevy handles required
components. This PR separates required components from explicit
components when filtering in `EntityClonerBuilder`.

## Testing
Added a regression test for this case.
2025-05-30 19:28:53 +00:00
theotherphil
0ee8bf978c
Clarify Resource change detection behaviour in condition docs (#19252)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/17933

## Solution

Correct "value has changed'" in docs to "value has been added or mutably
dereferenced", with a note for emphasis copied from the docs for
Changed.

## Testing

-
2025-05-30 00:47:25 +00:00
AlephCubed
6c003ef794
Loosen add_state_scoped_event trait bound. (#19401)
Fixes #18623, allowing `add_state_scoped_event` to work with computed
states.

As a side note, should state scoped events be updated to match the
recently changed [state scoped
entities](https://github.com/bevyengine/bevy/blob/main/release-content/migration-guides/rename_StateScoped.md)?
2025-05-29 21:37:53 +00:00
Coty
439c0fb973
fix reference in example usage comments (#19434)
# Objective

- Fix a reference in the example usage comments of bevy_utils
Default::default

## Solution

- Just a word change in the comments
 
## Testing

- No actual code changes to test
2025-05-29 19:12:55 +00:00
L. Borella (Villi)
59ef10562a
Allow restricting audio playback to a custom region (#19400)
# Objective

Adds the ability to restrict playback of an audio source to a certain
region in time. In other words, you can set a custom start position and
duration for the audio clip. These options are set via the
`PlaybackSettings` component, and it works on all kinds of audio
sources.

## Solution

- Added public `start_position` and `duration` fields to
`PlaybackSettings`, both of type `Option<std::time::Duration>`.
- Used rodio's `Source::skip_duration` and `Source::take_duration`
functions to implement start position and duration, respectively.
- If the audio is looping, it interacts as you might expect - the loop
will start at the start position and end after the duration.
- If the start position is None (the default value), the audio will
start from the beginning, like normal. Similarly, if the duration is
None (default), the audio source will play for as long as possible.

## Testing

I tried adding a custom start position to all the existing audio
examples to test a bunch of different audio sources and settings, and
they all worked fine. I verified that it skips the right amount of time,
and that it skips the entire audio clip if the start position is longer
than the length of the clip. All my testing was done on Fedora Linux.

Update: I did similar testing for duration, and ensured that the two
options worked together in combination and interacted well with looping
audio.

---

## Showcase

```rust
// Play a 10 second segment of a song, starting at 0:30.5
commands.spawn((
    AudioPlayer::new(song_handle),
    PlaybackSettings::LOOP
        .with_start_position(Duration::from_secs_f32(30.5))
        .with_duration(Duration::from_secs(10))
));
```
2025-05-29 18:45:37 +00:00
andristarr
fe678e1eeb
Updating mesh_picking doc to include RenderAssetUsages (#19413)
# Objective
Fixes #19102 

Updating mesh_picking doc stating that MAIN_WORLD RenderAssetUsages
needs to be on the mesh to be picked.
2025-05-29 18:44:50 +00:00
Daniel Skates
922ee480d2
Rename Position to UiPosition in bevy_ui (#19422)
# Objective

- Fixes #19418

## Solution

- Rename Position to UiPosition in bevy_ui

## Testing

- `cargo build`
- `cargo run --example gradients`
- `cargo run --example stacked_gradients`
2025-05-29 14:52:44 +00:00
Eagster
1966e44400
EntityGeneration ordering (#19421)
# Objective

Recently the `u32` `Entity::generation` was replaced with the new
`EntityGeneration` in #19121.
This made meanings a lot more clear, and prevented accidental misuse.

One common misuse was assuming that `u32`s that were greater than others
came after those others.
Wrapping makes this assumption false.
When `EntityGeneration` was created, it retained the `u32` ordering,
which was useless at best and wrong at worst.
This pr fixes the ordering implementation, so new generations are
greater than older generations.

Some users were already accounting for this ordering issue (which was
still present in 0.16 and before) by manually accessing the `u32`
representation. This made migrating difficult for avian physics; see
[here](https://discord.com/channels/691052431525675048/749335865876021248/1377431569228103780).

I am generally of the opinion that this type should be kept opaque to
prevent accidental misuse.
As we find issues like this, the functionality should be added to
`EntityGeneration` directly.

## Solution

Fix the ordering implementation through `Ord`.

Alternatively, we could keep `Ord` the same and make a `cmp_age` method,
but I think this is better, even though sorting entity ids may be
*marginally* slower now (but more correct). This is a tradeoff.

## Testing

I improved documentation for aliasing and ordering, adding some doc
tests.
2025-05-29 05:48:32 +00:00
SpecificProtagonist
a266e7e642
More uninlined_format_args fixes (#19396)
# Objective

There are several uninlined format args (seems to be in more formatting
macros and in more crates) that are not detected on stable, but are on
nightly.

## Solution

Fix them.
2025-05-28 02:35:18 +00:00
SpecificProtagonist
13e89a1678
Fix EntityMeta.spawned_or_despawned unsoundness (#19350)
# Objective

#19047 added an `MaybeUninit` field to `EntityMeta`, but did not
guarantee that it will be initialized before access:

```rust
let mut world = World::new();
let id = world.entities().reserve_entity();
world.flush();
world.entity(id);
```

<details>
<summary>Miri Error</summary>

```
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
    --> /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26
     |
1121 |                 unsafe { meta.spawned_or_despawned.assume_init() }
     |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
     = note: BACKTRACE:
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26: 1121:65
     = note: inside `std::option::Option::<&bevy_ecs::entity::EntityMeta>::map::<bevy_ecs::entity::SpawnedOrDespawned, {closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned::{closure#1}}>` at /home/vj/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1144:29: 1144:33
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1112:9: 1122:15
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1094:13: 1094:57
     = note: inside `bevy_ecs::change_detection::MaybeLocation::<std::option::Option<&std::panic::Location<'_>>>::new_with_flattened::<{closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by::{closure#0}}>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/change_detection.rs:1371:20: 1371:24
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1093:9: 1096:11
     = note: inside `bevy_ecs::entity::Entities::entity_does_not_exist_error_details` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1163:23: 1163:70
     = note: inside `bevy_ecs::entity::EntityDoesNotExistError::new` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1182:22: 1182:74
     = note: inside `bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell::<'_>::get_entity` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/unsafe_world_cell.rs:368:20: 368:73
     = note: inside `<bevy_ecs::entity::Entity as bevy_ecs::world::WorldEntityFetch>::fetch_ref` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/entity_fetch.rs:207:21: 207:42
     = note: inside `bevy_ecs::world::World::get_entity::<bevy_ecs::entity::Entity>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/mod.rs:911:18: 911:42
note: inside `main`
    --> src/main.rs:12:15
     |
12   |     world.entity(id);
     |
```

</details>

## Solution

- remove the existing `MaybeUninit` in `EntityMeta.spawned_or_despawned`
- initialize during flush. This is not needed for soundness, but not
doing this means we can't return a sensible location/tick for flushed
entities.

## Testing

Test via the snippet above (also added equivalent test).

---------

Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
2025-05-27 22:45:07 +00:00
andriyDev
b866bb4254
Remove Shader weak_handles from bevy_pbr (excluding meshlets). (#19365)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_pbr` (excluding meshlets).

## Testing

- `atmosphere` example still works
- `fog` example still works
- `decal` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:47 +00:00
andriyDev
e8c2a5af66
Remove Shader weak_handles from bevy_ui. (#19393)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_ui`.

## Testing

- `box_shadow` example still works.
- `gradient` example is broken at head (see #19384) - but otherwise
gives the same result in the console.
- `ui_materials` example still works.
- `ui_texture_slice` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:40 +00:00
andriyDev
846cec9ed2
Remove Shader weak_handles from bevy_gizmos. (#19394)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_gizmos`.

## Testing

- `2d_gizmos` example still works.
- `3d_gizmos` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:32 +00:00
andriyDev
57588eb7eb
Remove Shader weak_handles from bevy_core_pipeline (except two). (#19395)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_core_pipeline`.

## Testing

- `bloom_3d` example still works.
- `motion_blur` example still works.
- `meshlet` example still works (it uses a shader from core).

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:27 +00:00
Chris Berger
a8376e982e
Rename Timer::finished and Timer::paused to is_finished and is_paused (#19386)
# Objective
Renames `Timer::finished` and `Timer::paused` to `Timer::is_finished`
and `Timer::is_paused` to align the public APIs for `Time`, `Timer`, and
`Stopwatch`.

Fixes #19110
2025-05-27 22:24:18 +00:00
Rob Parrett
e0ed28022d
Fix error in gradient shader (#19384)
# Objective

Fixes #19383

## Solution

Add missing param and flags from `ui.wgsl` to `gradients.wgsl`

## Testing

`cargo run --example gradients`
`cargo run --example stacked_gradients`
`cargo run --example radial_gradients`

## Notes

`radial_gradients` looks broken, but this appears to be a separate
issue. Its appearance now is the same as in the [first
screenshot](https://pixel-eagle.com/project/b25a040a-a980-4602-b90c-d480ab84076d/run/10348/compare/10342?screenshot=UI%20(User%20Interface)/radial_gradients.png)
recorded in the example runner.

I will document this in a separate issue.
2025-05-27 21:29:10 +00:00
HeartofPhos
131f99de23
Fix custom relations panics with parent/child relations (#19341)
# Objective

Fixes #18905

## Solution

`world.commands().entity(target_entity).queue(command)` calls
`commands.with_entity` without an error handler, instead queue on
`Commands` with an error handler

## Testing

Added unit test

Co-authored-by: Heart <>
2025-05-27 21:05:31 +00:00
Griffin
e981bb8902
Make light cascades and tonemapping luts pub (#19189)
Make directional light cascades and tonemapping luts pub so that custom
render passes / backends can use them.
2025-05-27 19:46:04 +00:00
Chris Russell
571b3ba475
Remove ArchetypeComponentId and archetype_component_access (#19143)
# Objective

Remove `ArchetypeComponentId` and `archetype_component_access`.
Following #16885, they are no longer used by the engine, so we can stop
spending time calculating them or space storing them.

## Solution

Remove `ArchetypeComponentId` and everything that touches it.  

The `System::update_archetype_component_access` method no longer needs
to update `archetype_component_access`. We do still need to update query
caches, but we no longer need to do so *before* running the system. We'd
have to touch every caller anyway if we gave the method a better name,
so just remove `System::update_archetype_component_access` and
`SystemParam::new_archetype` entirely, and update the query cache in
`Query::get_param`.

The `Single` and `Populated` params also need their query caches updated
in `SystemParam::validate_param`, so change `validate_param` to take
`&mut Self::State` instead of `&Self::State`.
2025-05-27 19:04:32 +00:00
atlv
2946de4573
doc(render): fix incorrectly transposed view matrix docs (#19317)
# Objective

- Mend incorrect docs

## Solution

- Mend them
- add example use
- clarify column major

## Testing

- No code changes
2025-05-27 04:58:58 +00:00
atlv
d4985af7cb
refactor(utils): move SyncCell and SyncUnsafeCell to bevy_platform (#19305)
# Objective

- move SyncCell and SyncUnsafeCell to bevy_platform

## Solution

- move SyncCell and SyncUnsafeCell to bevy_platform

## Testing

- cargo clippy works
2025-05-27 04:57:26 +00:00
andriyDev
dc6563477d
Remove Shader weak_handles from bevy_sprite. (#19392)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_sprite`.

## Testing

- `sprite` example still works.
- `mesh2d` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 04:01:34 +00:00
atlv
1732c2253b
refactor(render): move WgpuWrapper into bevy_utils (#19303)
# Objective

- A step towards splitting out bevy_camera from bevy_render

## Solution

- Move a shim type into bevy_utils to avoid a dependency cycle
- Manually expand Deref/DerefMut to avoid having a bevy_derive
dependency so early in the dep tree

## Testing

- It compiles
2025-05-27 03:43:49 +00:00
AlephCubed
7d32dfec18
Add insert_if_new test for sparse set. (#19387)
Fixes #19081.
Simply created a duplicate of the existing `insert_if_new` test, but
using sparse sets.

## Testing:
The test passes on main, but fails if #19059 is reverted.
2025-05-27 03:15:30 +00:00
andriyDev
33b3c08515
Remove Shader weak_handles from bevy_anti_aliasing. (#19391)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_anti_aliasing`.

## Testing

- `anti_aliasing` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 03:14:55 +00:00
Rob Parrett
0ff44c9493
Small fixes for gradient docs (#19388)
# Objective

Found a typo while looking at gradients in another issue and gave the
docs a skim for more.

## Solution

A couple typo fixes and some tiny improvements
2025-05-26 23:20:24 +00:00
theotherphil
1e1853b876
Add missing doc comments for log_diagnostics_plugin (#19261)
# Objective

Fill some of the missing docs from bevy_diagnostics.
2025-05-26 22:29:59 +00:00
ickshonpe
f415e2e96d
Constify Val::resolve and BorderRadius::resolve (#18595)
# Objective

Constify `Val::resolve` and  `BorderRadius::resolve`

# Solution

* Replace uses of `Vec2::min_element` and `Vec2::max_element` with `min`
and `max` called on the components.
* Make `BorderRadius::resolve` and `BorderRadius::resolve_single_corner`
`const`.
* Swap the order of the `bottom_left` and `bottom_right` fields of
`BorderRadius` and `ResolvedBorderRadius` so they match the ccw order
used in the shader and in css.
2025-05-26 22:17:52 +00:00
theotherphil
523600133d
Add missing docs for diagnostics.rs (#19264)
# Objective

Fill in some more missing doc comments.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-05-26 20:27:46 +00:00
theotherphil
16a286dac3
Update .entry() docs to show both insert-then-modify and modify-or-insert examples (#19327)
# Objective

Fix https://github.com/bevyengine/bevy/issues/16379
2025-05-26 20:27:20 +00:00
François Mockers
8a223be651
Enable state scoped entities by default (#19354)
# Objective

- Enable state scoped entities by default
- Provide a way to disable it when needed

---------

Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-26 20:26:41 +00:00
andriyDev
8db7b6e122
Remove Shader weak_handles from bevy_render. (#19362)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
bevy_render.

## Testing

- `animate_shader` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-26 20:20:25 +00:00
SpecificProtagonist
158d9aff0e
Fix spawn tracking for spawn commands (#19351)
# Objective

See also
https://discord.com/channels/691052431525675048/1374187654425481266/1375553989185372292.

## Solution

Set spawn info in `Commands::spawn_empty`.
Also added a benchmark for `Commands::spawn`.

## Testing

See added test.
2025-05-26 20:15:21 +00:00
theotherphil
54c9f03021
Mention in .add_observer() docs that first parameter must be a Trigger (#19315)
# Objective

Fix https://github.com/bevyengine/bevy/issues/13860

## Solution

Add note in docs that Trigger must be the first parameter of observer
systems
2025-05-26 20:06:08 +00:00
Benjamin Brienen
818a8fe154
Allow unfocused window creation (#19237)
# Objective

Allow creating a new window without it being focused, when `Window`'s
`focused` is `false.
## Solution

Use `winit`'s `WindowBuilder`'s `with_active` method

## Notes

- `winit`'s doc lists [redox's
`Orbital`](https://gitlab.redox-os.org/redox-os/orbital) as an
unsupported platform, but since Bevy doesn't officially support this
platform, I didn't put it in the documentation.

- I only tested on Linux, which is an unsupported platform. I can give
you a test code if you want to test on another platform.

- I initially put a line
[here](https://github.com/bevyengine/bevy/blob/v0.11.0/crates/bevy_winit/src/system.rs#L72)
to set the Bevy `Window`'s `focused` to `winit_window.has_focus()` after
window creation to avoid the case where `with_active` is not supported,
the window is spawned focused, no `WindowFocused` event is triggered,
and Bevy `Window` would be desynced from winit's window. But after
testing on Linux (which doesn't support `with_active`) it seems like at
that point `has_focus` returns `false` and the event is triggered, so I
removed it. Do you think I should add it back to be safe?

## Changelog

- A new unfocused `Window` can be created by setting `focused` to
`false`.

## Migration Guide

- If a `Window` is spawned with `focused` set to `false`, it will now
start not focused on supported platforms.

Adopted from #9208

---------

Co-authored-by: Sélène Amanita <selene.amanita@net-c.com>
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 20:05:33 +00:00
theotherphil
f8cb8f237d
Fix a few typos in bevy_ecs docs (#19280)
# Objective

Fix a few minor typos that I noticed when reading the docs.
2025-05-26 20:02:13 +00:00
theotherphil
348779850b
Add missing doc comments to system_information_diagnostics_plugin (#19267)
# Objective

More trivial doc comments towards being able to deny missing docs across
the board.
2025-05-26 19:59:49 +00:00
theotherphil
3690ad5b0b
Remove apostrophes in possessive its (#19244)
# Objective

Fix some grammatical errors: it's -> its

Not the most useful commit in the world, but I saw a couple of these and
decided to fix the lot.

## Solution
-

## Testing
-
2025-05-26 19:53:14 +00:00
theotherphil
07c9d1acce
Clarify RenderLayers docs (#19241)
# Objective

Clarify `RenderLayers` docs, to fix
https://github.com/bevyengine/bevy/issues/18874

## Solution

-

## Testing

-
2025-05-26 19:52:22 +00:00
theotherphil
a521998eea
Make remove_reflect parameter names consistent between ReflectCommandExt and impl for EntityCommands (#19243)
# Objective

Fix https://github.com/bevyengine/bevy/issues/13390

## Solution

The second parameter of the remove_reflect function is called
component_type_name in ReflectCommandExt but component_type_path in the
implementation for EntityCommands. Use component_type_path in both
places.

## Testing

None
2025-05-26 19:49:56 +00:00
Benjamin Brienen
20057e5ed7
Make sure that serde_json::Map::into_values exists (#19229)
# Objective

cargo update was required to build because into_values was added in a
patch version

## Solution

Depend on the new patch

## Testing

Builds locally now
2025-05-26 19:38:28 +00:00
stevehello166
66877f63be
Add a Dir4 to resolve #17983 (#19223)
# Objective
Fixes #17983 

## Solution

Implemented a basic `Dir4` struct with methods similar to `Dir3` and
`Dir2`.

## Testing

- Did you test these changes? If so, how?
Added unit tests that follow the same pattern of the other Dir structs.
- Are there any parts that need more testing?
Since the other Dir structs use rotations to test renormalization and I
have been following them as a pattern for the Dir4 struct I haven't
implemented a test to cover renormalization of Dir4's.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Use Dir4 in the wild.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
N/A (Tested on Linux/X11 but it shouldn't be a problem)
2025-05-26 19:35:07 +00:00
Chris Berger
0777cc26aa
Navigate through TabGroups in order. (#19218)
# Objective
Fixes #19156

## Solution
There was a call to sort by `TabIndex` after iterating through all the
`TabGroup`s. Simply removing that line of code made the new test case
pass (navigating through `TabIndex` within different `TabGroup`s and
kept everything else working as expected as far as I can tell.

I went ahead and broke the `focusable` vec down into per-group vecs that
get sorted by `TabIndex` and appended to the main vec in group sorted
order to maintain the sorting that was being done before, but respecting
the `TabGroup` sorting that was missing in the issue that this PR
addresses.

## Testing
I added a new unit test that reproduced the issue outlined above that
will run in CI. This test was failing before deleting the sort, and now
both unit tests are passing.
2025-05-26 19:32:10 +00:00
urben1680
b92c0ebd3d
No schedule build pass overwrite if build settings do not change auto_insert_apply_deferred from true (#19217)
# Objective

Fixes #18790.
Simpler alternative to #19195.

## Solution

As suggested by @PixelDust22, simply avoid overwriting the pass if the
schedule already has auto sync points enabled.
Leave pass logic untouched.

It still is probably a bad idea to add systems/set configs before
changing the build settings, but that is not important as long there are
no more complex build passes.

## Testing

Added a test.

---------

Co-authored-by: Thierry Berger <contact@thierryberger.com>
2025-05-26 19:28:56 +00:00
IceSentry
2dc6337875
Add WakeUp event to App (#19212)
# Objective

- The WakeUp event is never added to the app. If you need to use that
event you currently need to add it yourself.

## Solution

- Add the WakeUp event to the App in the WinitPlugin

## Testing

- I tested the window_setting example and it compiled and worked
2025-05-26 19:26:26 +00:00
Emerson Coskey
7ab00ca185
Split Camera.hdr out into a new component (#18873)
# Objective

- Simplify `Camera` initialization
- allow effects to require HDR

## Solution

- Split out `Camera.hdr` into a marker `Hdr` component

## Testing

- ran `bloom_3d` example

---

## Showcase

```rs
// before
commands.spawn((
  Camera3d
  Camera {
    hdr: true
    ..Default::default()
  }
))

// after
commands.spawn((Camera3d, Hdr));

// other rendering components can require that the camera enables hdr!
// currently implemented for Bloom, AutoExposure, and Atmosphere.
#[require(Hdr)]
pub struct Bloom;
```
2025-05-26 19:24:45 +00:00
Gino Valente
1674938c49
bevy_reflect: Split up the std module (#18939)
> [!important]
> To **maintainers**: we should wait to merge this one in until after
#18944 lands so that cherry-picking the latter for 0.16.1 is simpler.

# Objective

The `std` module is where we implement the reflection traits for types
that are exported from `std` (including `core` and `alloc`). Over time,
this file has grown increasingly large, making it difficult to navigate
and a pain point for merge conflicts.

The goal of this PR is to break up the module into smaller chunks.

## Solution

The `std` module has been split into many submodules:
- `alloc`
- `bevy_platform`
- `core`
- `std`

Each of these new modules is comprised of submodules that closely
resemble the actual module. For example, the impls for
`::alloc::vec::Vec` have been moved to
`bevy_reflect::impls::alloc::vec::Vec`.

Some liberties were taken. For example, `Cow<'static, Path>` was kept in
`bevy_reflect::impls::std::path` rather than
`bevy_reflect::impls::alloc::borrow`.

You may ask: _Isn't this a little overkill? Why does the one-line impl
for `TypeId` need its own file?_

And yes, it is partly overkill. But the benefit with this approach is
that where an `std`-related type should live is mostly unambiguous. If
we wanted to reflect `::core::net::Ipv4Addr`, it's very clear that it
should be done in `bevy_reflect::impls::core::net`.

We can discuss better ways of breaking this up if people have other
ideas or opinions, but I think this is a pretty straightforward way of
doing it.

### Note to Reviewers

The code is pretty much copy-paste from the mega module to the new
submodules. It's probably best to focus efforts on reviewing the general
module structure, as well as maybe which impls are included where.

You _can_ review the code contained within each impl, but I promise you
the only thing I touched were the paths in the macros so they could be
more hygienic :)

## Testing

You can just check that everything compiles still:

```
cargo check -p bevy_reflect --tests
```
2025-05-26 19:10:47 +00:00
IceSentry
85284cbb90
Move trigger_screenshots to finish() (#19204)
# Objective

- The tigger_screenshots system gets added in `.build()` but relies on a
resource that is only inserted in `.finish()`
- This isn't a bug for most users, but when doing headless mode testing
it can technically work without ever calling `.finish()` and did work
before bevy 0.15 but while migrating my work codebase I had an issue of
test failing because of this

## Solution

- Move the trigger_screenshots system to `.finish()`

## Testing

- I ran the screenshot example and it worked as expected
2025-05-26 18:07:17 +00:00
Griffin
d79efada3b
Optional explicit compressed image format support (#19190)
# Objective

- Allow compressed image formats to be used with `ImagePlugin` and
`GltfPlugin` in cases where there is no `RenderDevice` resource. (For
example, when using a custom render backend)

## Solution

- Define a `CompressedImageFormatSupport` component that allows the user
to explicitly determine which formats are supported.

~~Not sure if this is the best solution. Alternatively, I considered
initializing CompressedImageFormatSupport from render device features
separately, it would need to run after the render device is initialized
but before `ImagePlugin` and `GltfPlugin` finish. Not sure where the
best place for that to happen would be.~~

Update: decided on going with @greeble-dev solution: defining the
`CompressedImageFormatSupport` resource in `bevy_image`, but letting
`bevy_render` register the resource value.
2025-05-26 18:00:33 +00:00
universe
5117e6fd49
don't filter dragged entity out of DragEnter events (#19179)
## produce a DragEnter event when reentering the dragged entity

when making a piano, i want dragging across the keys to trigger the
notes of each key, but currently if i drag out of a key, then back to
it, this will not work since the dragged entity gets filtered out

## Solution

- make DragEnter event work whenever there's an entry. if the user wants
to ignore the dragged entity they can compare `target` and `dragged`

## Testing

- tested this with a modified version of the 2d_shapes example. i added
an observer to the entities: (and added mesh picking plugin)
```rust
.observe(|t: Trigger<Pointer<DragEnter>>| {
    info!("entered {}, started from {}", t.target(), t.dragged);
}
```
- i'm not sure if other things need more testing, or if this is wrong
completely and breaks other things i don't know of!

---

## Showcase

before:


https://github.com/user-attachments/assets/48de606a-e44d-4ca1-ae16-d8dcef640d6e

after:


https://github.com/user-attachments/assets/b1be231f-c826-47bc-be43-c637f22e7846
2025-05-26 17:56:54 +00:00
Weihnachtsbaum
841105a993
Add AudioSinkPlayback::position (#19173)
# Objective

- Allow users to get the playback position of playing audio.

## Solution

- Add a `position` method to `AudioSinkPlayback`
- Implement it for `AudioSink` and `SpatialAudioSink`

## Testing

- Updated `audio_control` example to show playback position
2025-05-26 17:50:40 +00:00
Arnold Loubriat
645871e74e
Bump accesskit to 0.19 and accesskit_winit to 0.27 (#19160)
# Objective

- Update AccessKit crates to their latest versions.
- Fixes #19040 

## Solution

- Only modifying Cargo.toml files is needed, few changes under the hood
but nothing impacting Bevy.

## Testing

- I ran the tab_navigation example on Windows 11.
2025-05-26 17:48:36 +00:00
Eagster
b6b54912fa
Nonmax all rows (#19132)
# Objective

Since #18704 is done, we can track the length of unique entity row
collections with only a `u32` and identify an index within that
collection with only a `NonMaxU32`. This leaves an opportunity for
performance improvements.

## Solution

- Use `EntityRow` in sparse sets.
- Change table, entity, and query lengths to be `u32` instead of
`usize`.
- Keep `batching` module `usize` based since that is reused for events,
which may exceed `u32::MAX`.
- Change according `Range<usize>` to `Range<u32>`. This is more
efficient and helps justify safety.
- Change `ArchetypeRow` and `TableRow` to wrap `NonMaxU32` instead of
`u32`.

Justifying `NonMaxU32::new_unchecked` everywhere is predicated on this
safety comment in `Entities::set`: "`location` must be valid for the
entity at `index` or immediately made valid afterwards before handing
control to unknown code." This ensures no entity is in two table rows
for example. That fact is used to argue uniqueness of the entity rows in
each table, archetype, sparse set, query, etc. So if there's no
duplicates, and a maximum total entities of `u32::MAX` none of the
corresponding row ids / indexes can exceed `NonMaxU32`.

## Testing

CI

---------

Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
2025-05-26 17:39:55 +00:00
atlv
7bb97a7eec
Provide physical dimensions to wgpu Device::create_texture_with_data (#19129)
# Objective

- Make errors in #19124 #13289 clearer, and opt for option 1. of
https://github.com/gfx-rs/wgpu/issues/7677

## Solution

Remove the round to block size `.physical_size(texture_format);`

Error message now becomes much clearer:
```
thread 'Compute Task Pool (5)' panicked at E:\r\wgpu\wgpu\src\backend\wgpu_core.rs:1423:26:
wgpu error: Validation Error

Caused by:
  In Device::create_texture
    Width 2050 is not a multiple of Bc7RgbaUnormSrgb's block width (4)
```

## Testing

- Tested using the repro in #19124
2025-05-26 17:36:47 +00:00
Gilles Henaux
6341ae4158
Fix AssetChanged code documentation to mention the PostUpdate schedule (#19093)
# Objective

- Fix `AssetChanged` code documentation to mention the `PostUpdate`
schedule instead of the `Last` schedule

## Testing

- Trivial (code doc). Check `bevy_asset/src/lib.rs` in function
`init_asset` to see where this is scheduled:
```rust
           .add_systems(
                PostUpdate,
                Assets::<A>::asset_events
                    .run_if(Assets::<A>::asset_events_condition)
                    .in_set(AssetEvents),
            )
```

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 17:34:46 +00:00
SpecificProtagonist
57eda2efa1
Remove warning from Image::resize (#19116)
# Objective

`Image::resize` currently prints a warning when resizing an
uninitialized `Image`, even though the resizing works correctly. For
[code](c92f14c9e7/crates/bevy_ui/src/widget/viewport.rs (L175-L179))
that doesn't care about whether the image is initialized CP-side, this
is inconvenient and unnecessary.
2025-05-26 17:34:07 +00:00
noxmore
c84a808f80
Derive PartialEq for ImageSampler (#19105)
# Objective

Ran into a situation where I need to compare two image samplers. My
current workaround is to compare the `Debug` outputs

## Solution

Derive `PartialEq` on `ImageSampler` and structs in its fields.

## Testing

Full CI passed.
2025-05-26 17:31:21 +00:00
Kumikaya
ee3d9b2a88
Reduce operations in RayCast2d::circle_intersection_at using cross product (#19103)
# Objective

- Using the cross product to compute the perpendicular distance reduces
one multiplication and two additions.
2025-05-26 17:22:14 +00:00
SpecificProtagonist
1c8d2ee3e1
viewport_node example: Remove main world image initialization (#19098)
# Objective

The new viewport example allocates a texture in main memory, even though
it's only needed on the GPU. Also fix an unnecessary warning when a
viewport's texture doesn't exist CPU-side.

## Testing

Run the `viewport_node` example.
2025-05-26 17:20:29 +00:00
robtfm
b641aa0ecf
separate border colors (#18682)
# Objective

allow specifying the left/top/right/bottom border colors separately for
ui elements

fixes #14773

## Solution

- change `BorderColor` to 
```rs
pub struct BorderColor {
    pub left: Color,
    pub top: Color,
    pub right: Color,
    pub bottom: Color,
}
```
- generate one ui node per distinct border color, set flags for the
active borders
- render only the active borders

i chose to do this rather than adding multiple colors to the
ExtractedUiNode in order to minimize the impact for the common case
where all border colors are the same.

## Testing

modified the `borders` example to use separate colors:


![image](https://github.com/user-attachments/assets/5d9a4492-429a-4ee1-9656-215511886164)

the behaviour is a bit weird but it mirrors html/css border behaviour.

---

## Migration:

To keep the existing behaviour, just change `BorderColor(color)` into
`BorderColor::all(color)`.

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-05-26 16:57:13 +00:00
Emerson Coskey
fb2d79ad60
Better macro errors for get_struct_fields (#17639)
# Objective

- Currently, the error span for `get_struct_field` when encountering an
enum or union points to the macro invocation, rather than the `enum` or
`union` token. It also doesn't mention which macro reported the error.

## Solution

- Report the correct error span
- Add parameter for passing in the name of the macro invocation

## Testing

Bevy compiles fine with this change

## Migration Guide

```rs
// before
let fields = get_struct_fields(&ast.data);

// after
let fields = get_struct_fields(&ast.data, "derive(Bundle)");
```

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-05-26 16:57:03 +00:00
Periwink
f33074116b
Make SystemChangeTick Clone (#18991)
# Objective

It can be useful to store the `SystemChangeTick` for a given System. I
wanted to copy them but I noticed that it doesn't implement Clone.
2025-05-26 15:43:06 +00:00
Ben Frankel
475b5a8557
Improve visibility of debug picking node (#18990)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/18989.

## Solution

Add `GlobalZIndex(i32::MAX)`,
`BackgroundColor(Color::BLACK.with_alpha(0.75))`, and some padding.

## Testing

Ran `cargo run --example debug_picking`:


![2025-04-30_1745997924_1280x720](https://github.com/user-attachments/assets/4bd39897-f4ab-4d4d-850b-1b885284b072)

Before this PR:


![2025-04-29_1745972540_1280x720](https://github.com/user-attachments/assets/9c8649f4-0f06-4d4d-8fed-ac20e0d5366e)
2025-05-26 15:39:49 +00:00
Tim
4924cf5828
Remove upcasting methods + Cleanup interned label code (#18984)
Hiya!

# Objective

- Remove upcasting methods that are no longer necessary since Rust 1.86.
- Cleanup the interned label code.
 
## Notes
- I didn't try to remove the upcasting methods from `bevy_reflect`, as
there appears to be some complexity related to remote type reflection.
- There are likely some other upcasting methods floating around.

## Testing
I ran the `breakout` example to check that the hashing/eq
implementations of the labels are still correct.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 15:38:12 +00:00
Jakob Hellermann
93b8f9a303
bevy_ecs: forward type_id in InfallibleSystemWrapper (#18931)
similar to https://github.com/bevyengine/bevy/pull/12030

# Objective

`bevy_mod_debugdump` uses the `SystemTypeSet::system_type` to look up
constrains like `(system_1, system_2.after(system_1))`. For that it
needs to find the type id in `schedule.graph().systems()`

Now with systems being wrapped in an `InfallibleSystemWrapper` this
association was no longer possible.

## Solution

By forwarding the type id in `InfallibleSystemWrapper`,
`bevy_mod_debugdump` can resolve the dependencies as before, and the
wrapper is an unnoticable implementation detail.

## Testing

- `cargo test -p bevy_ecs`
I'm not sure what exactly could break otherwise.
2025-05-26 15:33:05 +00:00
Sébastien Job
2b8bf45f0d
Fix BRP query failing when specifying missing/invalid components (#18871)
# Objective

- Fixes #18869.

## Solution

The issue was the `?` after a `Result` raising the error, instead of
treating it.
Instead it is handled with `ok`, `and_then`, `map` ...

_Edit: I added the following logic._
On `bevy/query` remote requests, when `strict` is false:
- Unregistered components in `option` and `without` are ignored.
- Unregistered components in `has` are considered absent from the
entity.
- Unregistered components in `components` and `with` result in an empty
response since they specify hard requirements.

I made the `get_component_ids` function return a
`AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)>` instead of the
previous `AnyhowResult<Vec<(TypeId, ComponentId)>>`; that is I added the
list of unregistered components.

## Testing

I tested changes using the same procedure as in the linked issue:
```sh
cargo run --example server --features="bevy_remote"
```
In another terminal:
```sh
# Not strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] } } }'
{"jsonrpc":"2.0","id":0,"result":[]}

# Strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] }, "strict": true } }'
{"jsonrpc":"2.0","id":0,"error":{"code":-23402,"message":"Component `foo::bar::MyComponent` isn't registered or used in the world"}}
```
2025-05-26 15:26:46 +00:00
ickshonpe
c8872c7665
Allow access to font atlases (#18851)
# Objective
It's not possible atm for third-party crates to create their own text
systems that use the existing cosmic-text `FontSystem` and font atlases.

## Solution
Make `FontFaceInfo`, `TextPipeline::map_handle_to_font_id`, and
`load_font_fontdb` public so third-party crates can access and update
the existing font atlases
2025-05-26 15:22:56 +00:00
Greeble
ed0266b5a6
Fix glTF importer wrongly ignoring sampler filters (#19118)
## Objective

Fix #19114.

## Solution

#17875 changed the glTF importer to make sure that sampler filters are
linear when anisotropic filtering is enabled - this is required by
`wgpu`. But the condition was mistakenly inverted, so it forces the
filtering to linear when anisotropic filtering is _not_ enabled.

## Testing

```
cargo run --example color_grading
cargo run --example testbed_3d
```
2025-05-26 13:20:03 +00:00
Ben Frankel
3d9fc5ca10
Register some types (#19361)
# Objective

Fill in some `Reflect` and `app.register_type` gaps.

I only really wanted `GlobalZIndex` but figured I'd fill in a few others
as well.
2025-05-26 02:30:07 +00:00
Zachary Harrold
3d3746e5d0
Simplify bevy_utils Features (#19090)
# Objective

Now that `bevy_platform::cfg` is merged, we can start tidying up
features. This PR starts with `bevy_utils`.

## Solution

- Removed `serde` and `critical-section` features (they were just
re-exports of `bevy_platform` anyway)
- Removed `std`, `alloc` features, relying on `bevy_platform::cfg` to
check for availability.
- Added `parallel` feature to provide access to the `Parallel` type.
- Moved the `HashMap` type aliases into `map.rs` for better
organisation.

## Testing

- CI
2025-05-24 01:46:11 +00:00
DaoLendaye
cf3f26f10b
Add GltfMeshName component and Deref implementations (#19331)
Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.

# Objective

Fixed the side effects of #19287
Fixes Examples that modify gltf materials are broken #19322

## Solution

Add GltfMeshName component and Deref implementations

Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.


## Testing

cargo run --example depth_of_field
cargo run --example lightmaps
cargo run --example mixed_lighting
They are consistent with the situation before the error occurred.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-05-23 20:56:48 +00:00
Lucas Franca
d3012df755
Expose LogDiagnosticsState (#19323)
# Objective

Closes #19175 
Make `LogDiagnosticsState` public to be able to edit its filters

## Solution

Make `LogDiagnosticsState` public and add methods to allow editing the
duration and filter

## Testing

`cargo run -p ci`

## Showcase

Updated `log_diagnostics` example

![image](https://github.com/user-attachments/assets/25bc00f3-40e2-4b4a-b90b-137cc1f307a5)
2025-05-23 20:56:36 +00:00
NonbinaryCoder
f95287ca9b
Diagnostic reset sum ema (#19337)
# Objective

Fix incorrect average returned by `Diagnostic` after `clear_history` is
called.

## Solution

Reset sum and ema values in `Diagnostic::clear_history`.

## Testing

I have added a cargo test for `Diagnostic::clear_history` that checks
average and smoothed average. This test passes, and should not be
platform dependent.
2025-05-22 19:04:24 +00:00
theotherphil
88e73f8eda
Fix one-character typo in SystemParam docs (#19338)
# Objective

Remove errant "a" from docs.

(I'm assuming that this sort of trivial fix is easy enough to merge that
it's worth doing, but let me know if you'd prefer me to not bother.)
2025-05-22 18:54:02 +00:00
Lucas
e2d087551e
feat: derive Serialize on Childof (#19336)
# Objective

allow serialization / deserialization on the `ChildOf` entity, for
example in network usage.
my usage was for the bevy_replicon crate, to replicate `ChildOf`.

## Solution

same implementation of serde as other types in the bevy repo

---------

Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
2025-05-22 18:30:14 +00:00
Kevin Reid
870ad21238
Add documentation and examples of [ScheduleLabel] usage. (#19329)
## Objective

Add documentation useful to users of `bevy_ecs` not also using `App`.

Fixes #19270.

## Solution

* Add explanation of labels to `Schedule` documentation.
* Add example of `derive(ScheduleLabel)` to `trait ScheduleLabel`.
* Add a third example to `Schedule` which demonstrates using a schedule
via label instead of owning it directly.
* Add further explanation and links to `World::add_schedule()`, and
`World::run_schedule()`.

## Testing

Reviewed generated documentation.

Please review this documentation carefully for correctness, as I have
little experience with `bevy_ecs` and I am adding this information
because it would have helped my own past confusion, but I may still be
wrong about how things should be done.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-05-22 15:55:35 +00:00
stevehello166
c9e69ac65e
Rename Condition to SystemCondition` (#19328)
# Objective
Fixes #19120 

## Solution
Use the find and replace token feature in VSCode to replace all the
`Condition`s with `SystemCondition`s. Then look through all the
documentation with find and replace to replace all the `Condition`s
there.

## Testing

- Did you test these changes? If so, how?
Yes, used cargo clippy, cargo build and cargo test.
- Are there any parts that need more testing?
Nope
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
By compiling and running bevy
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Shouldn't be, but Fedora Linux with KDE Wayland
2025-05-22 15:50:19 +00:00
atlv
031fa25ff6
fix(picking): Location is not a Component anymore. (#19306)
# Objective

- Fixes #17390

## Solution

- Remove Component derive

## Testing

- cargo clippy
2025-05-22 01:33:01 +00:00
ickshonpe
2b59ab8e2d
Fix Anchor component inconsistancies (#18393)
## Objective

Fix the misleading 2d anchor API where `Anchor` is a component and
required by `Text2d` but is stored on a field for sprites.

Fixes #18367

## Solution

Remove the `anchor` field from `Sprite` and require `Anchor` instead.

## Migration Guide

The `anchor` field has been removed from `Sprite`. Instead the `Anchor`
component is now a required component on `Sprite`.
2025-05-21 15:32:04 +00:00
ickshonpe
bf20c630a8
UI Node Gradients (#18139)
# Objective

Allowing drawing of UI nodes with a gradient instead of a flat color.

## Solution

The are three gradient structs corresponding to the three types of
gradients supported: `LinearGradient`, `ConicGradient` and
`RadialGradient`. These are then wrapped in a `Gradient` enum
discriminator which has `Linear`, `Conic` and `Radial` variants.

Each gradient type consists of the geometric properties for that
gradient and a list of color stops.
Color stops consist of a color, a position or angle and an optional
hint. If no position is specified for a stop, it's evenly spaced between
the previous and following stops. Color stop positions are absolute, if
you specify a list of stops:
```vec![vec![ColorStop::new(RED, Val::Percent(90.), ColorStop::new(Color::GREEN, Val::Percent(10.))```
the colors will be reordered and the gradient will transition from green at 10% to red at 90%. 

Colors are interpolated between the stops in SRGB space. The hint is a normalized value that can be used to shift the mid-point where the colors are mixed 50-50.  between the stop with the hint and the following stop.

For sharp stops with no interpolated transition, place two stops at the same position.

`ConicGradient`s and RadialGradient`s have a center which is set using the new `Position` type. `Position` consists of a normalized (relative to the UI node) `Vec2` anchor point and a responsive x, y offset.

To draw a UI node with a gradient you insert the components `BackgroundGradient` and `BorderGradient`, which both newtype a vector of `Gradient`s. If you set a background color, the background color is drawn first and the gradient(s) are drawn on top.

The implementation is deliberately simple and self contained. The shader draws the gradient in multiple passes which is quite inefficient for gradients with a very large number of color stops. It's simple though and there won't be any compatibility issues. We could make gradients a specialization for `UiPipeline` but I used a separate pipeline plugin for now to ensure that these changes don't break anything. 

#### Not supported in this PR
* Interpolation in other color spaces besides SRGB. 
* Images and text: This would need some breaking changes like a `UiColor` enum type with `Color` and `Gradient` variants, to enable `BorderColor`, `TextColor`, `BackgroundColor` and `ImageNode::color` to take either a `Color` or a gradient.
* Repeating gradients

## Testing

Includes three examples that can be used for testing:
```
cargo run --example linear_gradients
cargo run --example stacked_gradients
cargo run --example radial_gradients
```

Most of the code except the components API is contained within the `bevy_ui/src/render/linear_gradients` module.
There are no changes to any existing systems or plugins except for the addition of the gradients rendering systems to the render world schedule and the `Val` changes from #18164 . 

## Showcase

![gradients](https://github.com/user-attachments/assets/a09c5bb2-f9dc-4bc5-9d17-21a6338519d3)
![stacked](https://github.com/user-attachments/assets/7a1ad28e-8ae0-41d5-85b2-aa62647aef03)
![rad](https://github.com/user-attachments/assets/48609cf1-52aa-453c-afba-3b4845f3ddec)

Conic gradients can be used to draw simple pie charts like in CSS:
![PIE](https://github.com/user-attachments/assets/4594b96f-52ab-4974-911a-16d065d213bc)
2025-05-20 14:45:22 +00:00
DaoLendaye
c62ca1a0d3
Use material name for mesh entity's Name when available (#19287)
# Objective

Fixes #19286

## Solution

Use material name for mesh entity's Name when available

## Testing

Test code, modified from examples/load_gltf.rs
```rust
//! Loads and renders a glTF file as a scene.

use bevy::{gltf::GltfMaterialName, prelude::*, scene::SceneInstanceReady};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_observer(on_scene_load)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
    ));

    commands.spawn((DirectionalLight {
        shadows_enabled: true,
        ..default()
    },));

    commands.spawn(SceneRoot(asset_server.load(
        GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
    )));
}

fn on_scene_load(
    trigger: Trigger<SceneInstanceReady>,
    children: Query<&Children>,
    names: Query<&Name>,
    material_names: Query<&GltfMaterialName>,
) {
    let target = trigger.target();

    for child in children.iter_descendants(target) {
        let name = if let Ok(name) = names.get(child) {
            Some(name.to_string())
        } else {
            None
        };
        let material_name = if let Ok(name) = material_names.get(child) {
            Some(name.0.clone())
        } else {
            None
        };

        info!("Entity name:{:?} | material name:{:?}", name, material_name);
    }
}
```

---

## Showcase

Run log:
<img width="859" alt="Image"
src="https://github.com/user-attachments/assets/87daddf3-31e6-41f8-9be2-4b292da9b75a"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
2025-05-20 14:45:04 +00:00
Lucas Franca
55edb0b476
Fix warnings and errors reported on Rust beta (#19294)
# Objective

Fixes errors and warnings on this week's Rust beta pipeline 
*
https://github.com/bevyengine/bevy/issues/18748#issuecomment-2890820218
2025-05-19 23:56:48 +00:00
Eero Lehtinen
17914943a3
Fix spot light shadow glitches (#19273)
# Objective

Spot light shadows are still broken after fixing point lights in #19265

## Solution

Fix spot lights in the same way, just using the spot light specific
visible entities component. I also changed the query to be directly in
the render world instead of being extracted to be more accurate.

## Testing

Tested with the same code but changing `PointLight` to `SpotLight`.
2025-05-19 19:42:09 +00:00
theotherphil
70e6a9010d
Add missing words in Traversal doc comment (#19298)
# Objective

Minor docs fix - add missing "is responsible".
2025-05-19 19:34:59 +00:00
theotherphil
fe16624d3c
Add boilerplate docs for PointerHits::new and HitData::new (#19259)
# Objective

Add documentation for the last two functions in bevy_picking that are
missing them.

## Solution

Add boilerplate "Constructs an X" to `PointerHits::new()` and
`HitData::new()`.

This form of no-information documentation of `new()` functions is used
in several places in the repo, and @alice-i-cecile agreed that this is a
reasonable approach - the params are already documented on the fields
within the struct definition.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-05-19 19:22:07 +00:00
ickshonpe
37b16d869d
Remove YAxisOrientation from bevy_text (#19077)
# Objective

Been looking for simplifications in the text systems as part of the text
input changes.

This enum isn't very helpful I think. We can remove it and the
associated parameters and instead just negate the glyph's y-offsets in
`extract_text2d_sprite`.

## Solution

Remove the `YAxisOrientation` enum and parameters. 
Queue text sprites relative to the top-left in `extract_text2d_sprite`
and negate the glyph's y-offset.

## Testing

The `text2d` example can be used for testing:
```
cargo run --example text2d
```
2025-05-19 19:17:20 +00:00
SpecificProtagonist
e7e9973c80
Per world error handler (#18810)
# Objective

[see original
comment](https://github.com/bevyengine/bevy/pull/18801#issuecomment-2796981745)
> Alternately, could we store it on the World instead of a global? I
think we have a World nearby whenever we call default_error_handler().
That would avoid the need for atomics or locks, since we could do
ordinary reads and writes to the World.

Global error handlers don't actually need to be global – per world is
enough. This allows using different handlers for different worlds and
also removes the restrictions on changing the handler only once.

## Solution

Each `World` can now store its own error handler in a resource.

For convenience, you can also set the default error handler for an
`App`, which applies it to the worlds of all `SubApp`s. The old behavior
of only being able to set the error handler once is kept for apps.

We also don't need the `configurable_error_handler` feature anymore now.

## Testing

New/adjusted tests for failing schedule systems & observers.

---

## Showcase

```rust
App::new()
    .set_error_handler(info)
    …
```
2025-05-19 01:35:07 +00:00
atlv
45ba5b9f03
refactor(render): cleanup add_import_to_composer (#19269)
# Objective

- Reduce nesting

## Solution

- Refactor

## Testing

- `bevy run --example=3d_scene web --open`
2025-05-18 06:30:38 +00:00
Eero Lehtinen
2db103708a
Make sure prepass notices changes in alpha mode (#19170)
# Objective

Fixes #19150

## Solution

Normally the `validate_cached_entity` in 

86cc02dca2/crates/bevy_pbr/src/prepass/mod.rs (L1109-L1126)
marks unchanged entites as clean, which makes them remain in the phase.

If a material is changed to an `alpha_mode` that isn't supposed to be
added to the prepass pipeline, the specialization system just
`continue`s and doesn't indicate to the cache that the entity is not
clean anymore.

I made these invalid entities get removed from the pipeline cache so
that they are correctly not marked clean and then removed from the
phase.

## Testing

Tested with the example code from the issue.
2025-05-18 06:28:30 +00:00
Eero Lehtinen
4d1b045855
Fix point light shadow glitches (#19265)
# Objective

Fixes #18945

## Solution

Entities that are not visible in any view (camera or light), get their
render meshes removed. When they become visible somewhere again, the
meshes get recreated and assigned possibly different ids.

Point/spot light visible entities weren't cleared when the lights
themseves went out of view, which caused them to try to queue these fake
visible entities for rendering every frame. The shadow phase cache
usually flushes non visible entites, but because of this bug it never
flushed them and continued to queue meshes with outdated ids.

The simple solution is to every frame clear all visible entities for all
point/spot lights that may or may not be visible. The visible entities
get repopulated directly afterwards. I also renamed the
`global_point_lights` to `global_visible_clusterable` to make it clear
that it includes only visible things.

## Testing

- Tested with the code from the issue.
2025-05-18 06:24:37 +00:00
atlv
eed1dc428b
fix(render): transitive shader imports now work consistently on web (#19266)
# Objective

- transitive shader imports sometimes fail to load silently and return
Ok
- Fixes #19226

## Solution

- Don't return Ok, return the appropriate error code which will retry
the load later when the dependencies load

## Testing

- `bevy run --example=3d_scene web --open`


Note: this is was theoretically a problem before the hot reloading PR,
but probably extremely unlikely to occur.
2025-05-17 19:03:47 +00:00
atlv
139515278c
Use embedded_asset to load PBR shaders (#19137)
# Objective

- Get in-engine shader hot reloading working

## Solution

- Adopt #12009
- Cut back on everything possible to land an MVP: we only hot-reload PBR
in deferred shading mode. This is to minimize the diff and avoid merge
hell. The rest shall come in followups.

## Testing

- `cargo run --example pbr --features="embedded_watcher"` and edit some
pbr shader code
2025-05-16 05:47:34 +00:00
atlv
415ffa5028
clippy: expect large variants and other small fixes (#19222)
# Objective

- Fix CI

## Solution

- Expect new lint
- See #19220

## Testing

- cargo clippy
2025-05-15 22:29:59 +00:00
mgi388
01d2b8571c
Remove dead states code from bevy_ecs (#19210)
`bevy_ecs` was meant to have the `States` and `SubStates`
`proc_macro_derive`s removed when the separate `bevy_state` [was
created](https://github.com/bevyengine/bevy/issues/13216) but they were
missed.
2025-05-14 13:19:20 +00:00
atlv
673e70c72e
Fix specular cutoff on lights with radius overlapping with mesh (#19157)
# Objective

- Fixes #13318

## Solution

- Clamp a dot product to be positive to avoid choosing a `centerToRay`
which is not on the ray but behind it.

## Testing

- Repro in #13318

Main:
<img width="963" alt="{DA2A2B99-27C7-4A76-83B6-CCB70FB57CAD}"
src="https://github.com/user-attachments/assets/afae8001-48ee-4762-9522-e247bbe3577a"
/>

This PR:
<img width="963" alt="{2C4BC3E7-C6A6-4736-A916-0366FBB618DA}"
src="https://github.com/user-attachments/assets/5bea4162-0b58-4df0-bf22-09fcb27dc167"
/>

Eevee reference:

![329697008-ff28a5f3-27f3-4e98-9cee-d836a6c76aee](https://github.com/user-attachments/assets/a1b566ab-16ee-40d3-a0b6-ad179ca0fe3a)
2025-05-12 19:14:13 +00:00
Daniel Gallups
eb0f4f76ba
Fix: Provide CPU mesh processing with MaterialBindingId (#19083)
# Objective
Fixes #19027

## Solution
Query for the material binding id if using fallback CPU processing

## Testing
I've honestly no clue how to test for this, and I imagine that this
isn't entirely failsafe :( but would highly appreciate a suggestion!

To verify this works, please run the the texture.rs example using WebGL
2.

Additionally, I'm extremely naive about the nuances of pbr. This PR is
essentially to kinda *get the ball rolling* of sorts. Thanks :)

---------

Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
Co-authored-by: charlotte <charlotte.c.mcelwain@gmail.com>
2025-05-12 18:11:14 +00:00
atlv
ddc9f41519
fix windows wasm embedded assets (#19139)
# Objective

- Fix #14246

## Solution

- If building for wasm windows, add a bit of code that replaces `\\`
with `/` in the `file!()` arg

## Testing

- Used MRE https://github.com/janhohenheim/asset-crash

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-12 10:10:32 +00:00
Jordan Dominion
86cc02dca2
Fix macro pollution in SystemParam derive (#19155)
# Objective

Fixes #19130 

## Solution

Fully quality `Result::Ok` so as to not accidentally invoke the anyhow
function of the same name

## Testing

Tested on this minimal repro with and without change.

main.rs
```rs
use anyhow::Ok;
use bevy::ecs::system::SystemParam;

#[derive(SystemParam)]
pub struct SomeParams;

fn main() {
}
```
Cargo.toml
```toml
[package]
name = "bevy-playground"
version = "0.1.0"
edition = "2024"

[dependencies]
anyhow = "1.0.98"
bevy = { path = "../bevy" }
```
2025-05-10 02:45:25 +00:00
databasedav
95470df3c8
fix .insert_related index bound (#19134)
# Objective

resolves #19092

## Solution

- remove the `.saturating_sub` from the index transformation
- add `.saturating_add` to the internal offset calculation

## Testing

- added regression test, confirming 0 index order + testing max bound
2025-05-09 17:10:54 +00:00
urben1680
732b2e0c79
Track spawn Tick of entities, offer methods, query data SpawnDetails and query filter Spawned (#19047)
# Objective

In my own project I was encountering the issue to find out which
entities were spawned after applying commands. I began maintaining a
vector of all entities with generational information before and after
applying the command and diffing it. This was awfully complicated though
and has no constant complexity but grows with the number of entities.

## Solution

Looking at `EntyMeta` it seemed obvious to me that struct can track the
tick just as it does with `MaybeLocation`, updated from the same call.
After that it became almost a given to also introduce query data
`SpawnDetails` which offers methods to get the spawn tick and location,
and query filter `Spawned` that filters entities out that were not
spawned since the last run.

## Testing

I expanded a few tests and added new ones, though maybe I forgot a group
of tests that should be extended too. I basically searched `bevy_ecs`
for mentions of `Changed` and `Added` to see where the tests and docs
are.

Benchmarks of spawn/despawn can be found
[here](https://github.com/bevyengine/bevy/pull/19047#issuecomment-2852181374).

---

## Showcase

From the added docs, systems with equal complexity since the filter is
not archetypal:
```rs
fn system1(q: Query<Entity, Spawned>) {
    for entity in &q { /* entity spawned */ }
}

fn system2(query: Query<(Entity, SpawnDetails)>) {
    for (entity, spawned) in &query {
        if spawned.is_spawned() { /* entity spawned */ }
    }
}
```

`SpawnedDetails` has a few more methods:

```rs
fn print_spawn_details(query: Query<(Entity, SpawnDetails)>) {
    for (entity, spawn_details) in &query {
        if spawn_details.is_spawned() {
            print!("new ");
        }
        println!(
            "entity {:?} spawned at {:?} by {:?}",
            entity,
            spawn_details.spawned_at(),
            spawn_details.spawned_by()
        );        
    }
}
```

## Changes

No public api was changed, I only added to it. That is why I added no
migration guide.

- query data `SpawnDetails`
- query filter `Spawned`
- method `Entities::entity_get_spawned_or_despawned_at`
- method `EntityRef::spawned_at`
- method `EntityMut::spawned_at`
- method `EntityWorldMut::spawned_at`
- method `UnsafeEntityCell::spawned_at`
- method `FilteredEntityRef::spawned_at`
- method `FilteredEntityMut::spawned_at`
- method `EntityRefExcept::spawned_at`
- method `EntityMutExcept::spawned_at`

---------

Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-08 14:57:33 +00:00
Eagster
12aba64900
Make entity generation a new type and remove identifier (#19121)
# Objective

This is a followup to #18704 . There's lots more followup work, but this
is the minimum to unblock #18670, etc.

This direction has been given the green light by Alice
[here](https://github.com/bevyengine/bevy/pull/18704#issuecomment-2853368129).

## Solution

I could have split this over multiple PRs, but I figured skipping
straight here would be easiest for everyone and would unblock things the
quickest.

This removes the now no longer needed `identifier` module and makes
`Entity::generation` go from `NonZeroU32` to `struct
EntityGeneration(u32)`.

## Testing

CI

---------

Co-authored-by: Mark Nokalt <marknokalt@live.com>
2025-05-08 04:03:05 +00:00
Eagster
0b4858726c
Make entity::index non max (#18704)
# Objective

There are two problems this aims to solve. 

First, `Entity::index` is currently a `u32`. That means there are
`u32::MAX + 1` possible entities. Not only is that awkward, but it also
make `Entity` allocation more difficult. I discovered this while working
on remote entity reservation, but even on main, `Entities` doesn't
handle the `u32::MAX + 1` entity very well. It can not be batch reserved
because that iterator uses exclusive ranges, which has a maximum upper
bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid
index can be thought of as a bug right now. We either need to make that
invalid (this PR), which makes Entity allocation cleaner and makes
remote reservation easier (because the length only needs to be u32
instead of u64, which, in atomics is a big deal), or we need to take
another pass at `Entities` to make it handle the `u32::MAX` index
properly.

Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for
u32) all have `u32` as the underlying type. That means using these as
the index type in a `SparseSet` uses 64 bits for the sparse list because
it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the
memory of that list in half. To my knowledge, `EntityIndex` is the only
thing that would really benefit from this niche. `TableRow` and
`ArchetypeRow` I think are not stored in an `Option` in bulk. But if
they ever are, this would help. Additionally this ensures
`TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an
actual row, which in a nice bonus.

As a related note, if we do components as entities where `ComponentId`
becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar
memory improvement too.

## Solution

Create a new type `EntityRow` that wraps `NonMaxU32`, similar to
`TableRow` and `ArchetypeRow`.
Change `Entity::index` to this type.

## Downsides

`NonMax` is implemented as a `NonZero` with a binary inversion. That
means accessing and storing the value takes one more instruction. I
don't think that's a big deal, but it's worth mentioning.

As a consequence, `to_bits` uses `transmute` to skip the inversion which
keeps it a nop. But that also means that ordering has now flipped. In
other words, higher indices are considered less than lower indices. I
don't think that's a problem, but it's also worth mentioning.

## Alternatives

We could keep the index as a u32 type and just document that `u32::MAX`
is invalid, modifying `Entities` to ensure it never gets handed out.
(But that's not enforced by the type system.) We could still take
advantage of the niche here in `ComponentSparseSet`. We'd just need some
unsafe manual conversions, which is probably fine, but opens up the
possibility for correctness problems later.

We could change `Entities` to fully support the `u32::MAX` index. (But
that makes `Entities` more complex and potentially slightly slower.)

## Testing

- CI
- A few tests were changed because they depend on different ordering and
`to_bits` values.

## Future Work

- It might be worth removing the niche on `Entity::generation` since
there is now a different niche.
- We could move `Entity::generation` into it's own type too for clarity.
- We should change `ComponentSparseSet` to take advantage of the new
niche. (This PR doesn't change that yet.)
- Consider removing or updating `Identifier`. This is only used for
`Entity`, so it might be worth combining since `Entity` is now more
unique.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-05-07 18:20:30 +00:00
Chris Russell
9e2bd8ac18
Generic SystemParam impls for Option and Result (#18766)
# Objective

Provide a generic `impl SystemParam for Option<P>` that uses system
parameter validation. This immediately gives useful impls for params
like `EventReader` and `GizmosState` that are defined in terms of `Res`.
It also allows third-party system parameters to be usable with `Option`,
which was previously impossible due to orphan rules.

Note that this is a behavior change for `Option<Single>`. It currently
fails validation if there are multiple matching entities, but with this
change it will pass validation and produce `None`.

Also provide an impl for `Result<P, SystemParamValidationError>`. This
allows systems to inspect the error if necessary, either for bubbling it
up or for checking the `skipped` flag.

Fixes #12634
Fixes #14949
Related to #18516

## Solution

Add generic `SystemParam` impls for `Option` and `Result`, and remove
the impls for specific types.

Update documentation and `fallible_params` example with the new
semantics for `Option<Single>`.
2025-05-07 18:20:08 +00:00
Periwink
60ea43d01d
Add system ticks to EntityRef/Mut WorldQuery (#19115)
# Objective

- Fixes a subset of https://github.com/bevyengine/bevy/issues/13735 by
making `EntityRef`, `EntityMut` + similar WorldQueries use the system's
change ticks when being created from within a system.
In particular, this means that `entity_ref.get_ref::<T>()` will use the
correct change ticks (the ones from the system), which matches the
behaviour of querying for `Ref<T>` directly in the system parameters.

## Solution

- Implements the solution described by
https://github.com/bevyengine/bevy/issues/13735#issuecomment-2652482918
which is to add change ticks to the `UnsafeEntityCell`

## Testing

- Added a unit test that is close to what users would encounter: before
this PR the `Added`/`Changed` filters on `Ref`s created from `EntityRef`
are incorrect.
2025-05-07 18:19:35 +00:00
Zachary Harrold
63e78fe489
Deprecated Begone! 0.16 Cleanup (#19108)
# Objective

A fair few items were deprecated in 0.16. Let's delete them now that
we're in the 0.17 development cycle!

## Solution

- Deleted items marked deprecated in 0.16.

## Testing

- CI

---

## Notes

I'm making the assumption that _everything_ deprecated in 0.16 should be
removed in 0.17. That may be a false assumption in certain cases. Please
check the items to be removed to see if there are any exceptions we
should keep around for another cycle!
2025-05-07 18:17:41 +00:00
Zachary Harrold
4051465b06
Make NonSendMarker !Send (#19107)
# Objective

In #18301, `NonSendMarker` was defined in such a way that it actually
implements `Send`. This isn't strictly a soundness issue, as its goal is
to be used as a `SystemParam`, and it _does_ appropriately mark system
access as `!Send`. It just seems odd that `NonSendMarker: Send`.

## Solution

- Made `NonSendMarker` wrap `PhantomData<*mut ()>`, which forces it to
be `!Send`.

## Testing

- CI

---

## Notes

This does mean constructing a `NonSendMarker` _value_ will require using
the `SystemParam` trait, but I think that's acceptable as the marker as
a value should be rarely required if at all.
2025-05-07 00:40:35 +00:00
SpecificProtagonist
73cde28cf8
Missing punctuation (#19097) 2025-05-06 23:01:59 +00:00
Joshua Holmes
770f10bc19
Remove remaining internal use of !Send resources (#18386)
# Objective

Remaining work for and closes #17682. First half of work for that issue
was completed in [PR
17730](https://github.com/bevyengine/bevy/pull/17730). However, the rest
of the work ended up getting blocked because we needed a way of forcing
systems to run on the main thread without the use of `!Send` resources.
That was unblocked by [PR
18301](https://github.com/bevyengine/bevy/pull/18301).

This work should finish unblocking the resources-as-components effort.

# Testing

Ran several examples using my Linux machine, just to make sure things
are working as expected and no surprises pop up.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-06 22:23:59 +00:00
Joona Aalto
7b1c9f192e
Adopt consistent FooSystems naming convention for system sets (#18900)
# Objective

Fixes a part of #14274.

Bevy has an incredibly inconsistent naming convention for its system
sets, both internally and across the ecosystem.

<img alt="System sets in Bevy"
src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b"
width="450" />

*Names of public system set types in Bevy*

Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are
also a few `FooSystems` and `FooSet` types. In ecosystem crates on the
other hand, `FooSet` is perhaps the most commonly used name in general.
Conventions being so wildly inconsistent can make it harder for users to
pick names for their own types, to search for system sets on docs.rs, or
to even discern which types *are* system sets.

To reign in the inconsistency a bit and help unify the ecosystem, it
would be good to establish a common recommended naming convention for
system sets in Bevy itself, similar to how plugins are commonly suffixed
with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming
convention in first-party Bevy, we can softly nudge ecosystem crates to
follow suit (for types where it makes sense to do so).

Choosing a naming convention is also relevant now, as the [`bevy_cli`
recently adopted
lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce
naming for plugins and system sets, and the recommended naming used for
system sets is still a bit open.

## Which Name To Use?

Now the contentious part: what naming convention should we actually
adopt?

This was discussed on the Bevy Discord at the end of last year, starting
[here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>).
`FooSet` and `FooSystems` were the clear favorites, with `FooSet` very
narrowly winning an unofficial poll. However, it seems to me like the
consensus was broadly moving towards `FooSystems` at the end and after
the poll, with Cart
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708))
and later Alice
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533))
and also me being in favor of it.

Let's do a quick pros and cons list! Of course these are just what I
thought of, so take it with a grain of salt.

`FooSet`:

- Pro: Nice and short!
- Pro: Used by many ecosystem crates.
- Pro: The `Set` suffix comes directly from the trait name `SystemSet`.
- Pro: Pairs nicely with existing APIs like `in_set` and
`configure_sets`.
- Con: `Set` by itself doesn't actually indicate that it's related to
systems *at all*, apart from the implemented trait. A set of what?
- Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex:
`ContactSet`, `MeshSet`, `EnemySet`...

`FooSystems`:

- Pro: Very clearly indicates that the type represents a collection of
systems. The actual core concept, system(s), is in the name.
- Pro: Parallels nicely with `FooPlugins` for plugin groups.
- Pro: Low risk of conflicts with other names or misunderstandings about
what the type is.
- Pro: In most cases, reads *very* nicely and clearly. Ex:
`PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and
`AnimationSet`.
- Pro: Easy to search for on docs.rs.
- Con: Usually results in longer names.
- Con: Not yet as widely used.

Really the big problem with `FooSet` is that it doesn't actually
describe what it is. It describes what *kind of thing* it is (a set of
something), but not *what it is a set of*, unless you know the type or
check its docs or implemented traits. `FooSystems` on the other hand is
much more self-descriptive in this regard, at the cost of being a bit
longer to type.

Ultimately, in some ways it comes down to preference and how you think
of system sets. Personally, I was originally in favor of `FooSet`, but
have been increasingly on the side of `FooSystems`, especially after
seeing what the new names would actually look like in Avian and now
Bevy. I prefer it because it usually reads better, is much more clearly
related to groups of systems than `FooSet`, and overall *feels* more
correct and natural to me in the long term.

For these reasons, and because Alice and Cart also seemed to share a
preference for it when it was previously being discussed, I propose that
we adopt a `FooSystems` naming convention where applicable.

## Solution

Rename Bevy's system set types to use a consistent `FooSet` naming where
applicable.

- `AccessibilitySystem` → `AccessibilitySystems`
- `GizmoRenderSystem` → `GizmoRenderSystems`
- `PickSet` → `PickingSystems`
- `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems`
- `TransformSystem` → `TransformSystems`
- `RemoteSet` → `RemoteSystems`
- `RenderSet` → `RenderSystems`
- `SpriteSystem` → `SpriteSystems`
- `StateTransitionSteps` → `StateTransitionSystems`
- `RenderUiSystem` → `RenderUiSystems`
- `UiSystem` → `UiSystems`
- `Animation` → `AnimationSystems`
- `AssetEvents` → `AssetEventSystems`
- `TrackAssets` → `AssetTrackingSystems`
- `UpdateGizmoMeshes` → `GizmoMeshSystems`
- `InputSystem` → `InputSystems`
- `InputFocusSet` → `InputFocusSystems`
- `ExtractMaterialsSet` → `MaterialExtractionSystems`
- `ExtractMeshesSet` → `MeshExtractionSystems`
- `RumbleSystem` → `RumbleSystems`
- `CameraUpdateSystem` → `CameraUpdateSystems`
- `ExtractAssetsSet` → `AssetExtractionSystems`
- `Update2dText` → `Text2dUpdateSystems`
- `TimeSystem` → `TimeSystems`
- `AudioPlaySet` → `AudioPlaybackSystems`
- `SendEvents` → `EventSenderSystems`
- `EventUpdates` → `EventUpdateSystems`

A lot of the names got slightly longer, but they are also a lot more
consistent, and in my opinion the majority of them read much better. For
a few of the names I took the liberty of rewording things a bit;
definitely open to any further naming improvements.

There are still also cases where the `FooSystems` naming doesn't really
make sense, and those I left alone. This primarily includes system sets
like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`,
or `TransitionSchedules<S>`, where the type has some special purpose and
semantics.

## Todo

- [x] Should I keep all the old names as deprecated type aliases? I can
do this, but to avoid wasting work I'd prefer to first reach consensus
on whether these renames are even desired.
- [x] Migration guide
- [x] Release notes
2025-05-06 15:18:03 +00:00
axlitEels
775fae5b62
Add image sampler configuration in GLTF loader (#17875)
I can't underrate anisotropic filtering.

# Objective

- Allow easily enabling anisotropic filtering on glTF assets.
- Allow selecting `ImageFilterMode` for glTF assets at runtime.

## Solution

- Added a Resource `DefaultGltfImageSampler`: it stores
`Arc<Mutex<ImageSamplerDescriptor>>` and the same `Arc` is stored in
`GltfLoader`. The default is independent from provided to `ImagePlugin`
and is set in the same way but with `GltfPlugin`. It can then be
modified at runtime with `DefaultGltfImageSampler::set`.
- Added two fields to `GltfLoaderSettings`: `default_sampler:
Option<ImageSamplerDescriptor>` to override aforementioned global
default descriptor and `override_sampler: bool` to ignore glTF sampler
data.

## Showcase

Enabling anisotropic filtering as easy as:
```rust
app.add_plugins(DefaultPlugins.set(GltfPlugin{
    default_sampler: ImageSamplerDescriptor {
        min_filter: ImageFilterMode::Linear,
        mag_filter: ImageFilterMode::Linear,
        mipmap_filter: ImageFilterMode::Linear,
        anisotropy_clamp: 16,
        ..default()
    },
    ..default()
}))
```

Use code below to ignore both the global default sampler and glTF data,
having `your_shiny_sampler` used directly for all textures instead:
```rust
commands.spawn(SceneRoot(asset_server.load_with_settings(
    GltfAssetLabel::Scene(0).from_asset("models/test-scene.gltf"),
    |settings: &mut GltfLoaderSettings| {
        settings.default_sampler = Some(your_shiny_sampler);
        settings.override_sampler = true;
    }
)));
```
Remove either setting to get different result! They don't come in pair!

Scene rendered with trillinear texture filtering:

![Trillinear](https://github.com/user-attachments/assets/be4c417f-910c-4806-9e64-fd2c21b9fd8d)
Scene rendered with 16x anisotropic texture filtering:
![Anisotropic Filtering
x16](https://github.com/user-attachments/assets/68190be8-aabd-4bef-8e97-d1b5124cce60)

## Migration Guide

- The new fields in `GltfLoaderSettings` have their default values
replicate previous behavior.

---------

Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
2025-05-06 05:48:13 +00:00
Christian Hughes
7e51f60de1
Add IntoSystem::with_input and ::with_input_from system wrappers (#18067)
# Objective

Originally [provided as a solution to a user's problem in
Discord](https://discord.com/channels/691052431525675048/1247654592838111302/1344431131277394042),
library authors might find the need to present user-registered systems
with system-specific data. Typically `Local<T>` is used for this type of
thing, but its not generally feasible or possible to configure/set the
underlying `T` data for locals. Alternatively, we can use `SystemInput`
to pass the data.

## Solution

- Added `IntoSystem::with_input`: Allows system-specific data to be
passed in explicitly.
- Added `IntoSystem::with_input_from`: Allows system-specific data to be
created at initialization time via `FromWorld`.

## Testing

Added two new tests, testing each of `with_input` and `with_input_from`.
2025-05-06 05:46:30 +00:00
Han Kruiger
b8724c21ce
implement MapEntities for higher-order types (#19071)
# Objective

With the current `MapEntities` `impl`s, it is not possible to derive
things like this:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```

This is because `MapEntities` is only implemented for `Vec<Entity>` &
`Option<Entity>`, and not arbitrary combinations of those.

It would be nice to also support those types.

## Solution

I replaced the `impl`s of the following types

- `Option<Entity>`: replaced with `Option<T>` 
- `Vec<Entity>`: replaced with `Vec<T>`
- `HashSet<Entity, S>`: replaced with `HashSet<T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `IndexSet<Entity, S>`: replaced with `IndexSet <T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `BTreeSet<Entity>`: replaced with `BTreeSet<T>`
- `VecDeque<Entity>`: replaced with `VecDeque<T>`
- `SmallVec<A: smallvec::Array<Item = Entity>>`: replaced with
`SmallVec<A: smallvec::Array<Item = T>>`

(in all of the above, `T` is a generic type that implements
`MapEntities` (`Entity` being one of them).)

## Testing

I did not test any of this, but extended the `Component::map_entities`
doctest with an example usage of the newly supported types.

---

## Showcase

With these changes, this is now possible:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```
2025-05-06 05:24:37 +00:00
UkoeHB
8f3e45b45f
Expose CustomCursorUrl (#19006)
# Objective

`CustomCursorUrl` is inaccessible.

## Solution

Expose `CustomCursorUrl`.
2025-05-06 05:23:48 +00:00
urben1680
2ae1510f89
Add world and world_mut methods to RelatedSpawner (#18880)
# Objective

`RelatedSpawnerCommands` offers methods to get the underlying
`Commands`.
`RelatedSpawner` does not expose the inner `World` reference so far.

I currently want to write extension traits for both of them but I need
to duplicate the whole API for the latter because I cannot get it's
`&mut World`.

## Solution

Add methods for immutable and mutable `World` access
2025-05-06 05:18:56 +00:00
Corvus
a312170749
Increase upper limit of children! (#18865)
# Objective

Currently, `bevy_ecs`'s `children!` macro only supports spawning up to
twelve children at once. Ideally there would be no limit.

## Solution

`children!` is limited because `SpawnableList`, [the primary trait bound
here](https://docs.rs/bevy/0.16.0-rc.5/bevy/ecs/hierarchy/struct.Children.html#method.spawn),
uses the fake variadics pattern on tuples of up to twelve elements.
However, since a tuple itself implements `SpawnableList`, we can simply
nest tuples of entities when we run out of room.

This PR achieves this using `macro_rules` macros with a bit of brute
force, following [some discussion on
Discord](https://discord.com/channels/691052431525675048/692572690833473578/1362174415458013314).
If we create patterns for lists of up to eleven bundles, then use a
repetition pattern to handle the rest, we can "special-case" the
recursion into a nested tuple.

In principle, this would permit an arbitrary number of children, but
Rust's recursion limits will cut things short at around 1400 elements by
default. Of course, it's generally not a good idea to stick that many
bundles in a single invocation, but it might be worth mentioning in the
docs.

## Implementation notes

### Why are cases 0-11 expanded by hand?

We could make use of a tertiary macro:

```rs
macro_rules! recursive_spawn {
    // so that this...
    ($a:expr, $b:expr) => {
        (
            $crate::spawn::Spawn($a),
            $crate::spawn::Spawn($b),
        )
    };
    
    // becomes this...
    ($a:expr, $b:expr) => {
        $crate::spawn_tuple!($a, $b)
    };
}
```

But I already feel a little bad exporting `recursive_spawn`. I'd really
like to avoid exposing more internals, even if they are annotated with
`#[doc(hidden)]`. If I had to guess, I'd say it'll also make the
expansion a tiny bit slower.

### Do we really need to handle up to twelve elements in the macro?

The macro is a little long, but doing it this way maximizes the
"flatness" of the types to be spawned. This should improve the codegen a
bit and makes the macro output a little bit easier to look at.

## Future work

The `related!` macro is essentially the same as `children!`, so if this
direction is accepted, `related!` should receive the same treatment. I
imagine we'd want to extract out the `recursive_spawn` macro into its
own file since it can be used for both. If this should be tackled in
this PR, let me know!

## Testing

This change is fairly trivial, but I added a single test to verify that
it compiles and nothing goes wrong once recursion starts happening. It's
pretty easy to verify that the change works in practice -- just spawn
over twelve entities as children at once!
2025-05-06 00:58:30 +00:00
Zachary Harrold
aadd3a3ec2
Create bevy_platform::cfg for viral feature management (#18822)
# Objective

- Acts on certain elements of #18799
- Closes #1615
- New baseline for #18170

## Solution

- Created a new `cfg` module in `bevy_platform` which contains two
macros to aid in working with features like `web`, `std`, and `alloc`.
- `switch` is a stable implementation of
[`cfg_match`](https://doc.rust-lang.org/std/macro.cfg_match.html), which
itself is a `core` alternative to [`cfg_if`](https://docs.rs/cfg-if).
- `define_alias` is a `build.rs`-free alternative to
[`cfg_aliases`](https://docs.rs/cfg_aliases) with the ability to share
feature information between crates.
- Switched to these macros within `bevy_platform` to demonstrate usage. 

## Testing

- CI

---

## Showcase

Consider the typical `std` feature as an example of a "virality". With
just `bevy_platform`, `bevy_utils`, and `bevy_ecs`, we have 3 crates in
a chain where activating `std` in any of them should really activate it
everywhere. The status-quo for this is for each crate to define its own
`std` feature, and ensure it includes the `std` feature of every
dependency in that feature. For crates which don't even interact with
`std` directly, this can be quite cumbersome. Especially considering
that Bevy has a fundamental crate, `bevy_platform`, which is a
dependency for effectively every crate.

Instead, we can use `define_alias` to create a macro which will
conditionally compile code if and only if the specified configuration
condition is met _in the defining crate_.

```rust
// In `bevy_platform`

define_alias! {
    #[cfg(feature = "std")] => {
        /// Indicates the `std` crate is available and can be used.
        std
    }
    #[cfg(all(target_arch = "wasm32", feature = "web"))] => {
        /// Indicates that this target has access to browser APIs.
        web
    }
}
```

The above `web` and `std` macros will either no-op the provided code if
the conditions are not met, or pass it unmodified if it is met. Since it
is evaluated in the context of the defining crate, `bevy_platform/std`
can be used to conditionally compile code in `bevy_utils` and `bevy_ecs`
_without_ those crates including their own `std` features.

```rust
// In `bevy_utils`
use bevy_platform::cfg;

// If `bevy_platform` has `std`, then we can too!
cfg::std! {
    extern crate std;
}
```

To aid in more complex configurations, `switch` is provided to provide a
`cfg_if` alternative that is compatible with `define_alias`:

```rust
use bevy_platform::cfg;

cfg::switch! {
    #[cfg(feature = "foo")] => { /* use the foo API */ }
    cfg::web => { /* use browser API */ }
    cfg::std => { /* use std */ }
    _ => { /* use a fallback implementation */ }
}
```

This paradigm would allow Bevy's sub-crates to avoid re-exporting viral
features, and also enable functionality in response to availability in
their dependencies, rather than from explicit features (bottom-up
instead of top-down). I imagine that a "full rollout" of this paradigm
would remove most viral features from Bevy's crates, leaving only
`bevy_platform`, `bevy_internal`, and `bevy` (since `bevy`/`_internal`
are explicitly re-exports of all of Bevy's crates).

This bottom-up approach may be useful in other areas of Bevy's features
too. For example, `bevy_core_pipeline/tonemapping_luts` requires:
- bevy_render/ktx2
- bevy_image/ktx2
- bevy_image/zstd

If `define_alias` was used in `bevy_image`, `bevy_render` would not need
to re-export the `ktx2` feature, and `bevy_core_pipeline` could directly
probe `bevy_image` for the status of `ktx2` and `zstd` features to
determine if it should compile the `tonemapping_luts` functionality,
rather than having an explicitly feature. Of course, an explicit feature
is still important for _features_, so this may not be the best example,
but it highlights that with this paradigm crates can reactively provide
functionality, rather than needing to proactively declare feature
combinations up-front and hope the user enables them.

---------

Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
2025-05-06 00:52:15 +00:00
mgi388
7a1fcb7fe7
Rename StateScoped to DespawnOnExitState and add DespawnOnEnterState (#18818)
# Objective

- Alternative to and builds on top of #16284.
- Fixes #15849.

## Solution

- Rename component `StateScoped` to `DespawnOnExitState`.
- Rename system `clear_state_scoped_entities` to
`despawn_entities_on_exit_state`.
- Add `DespawnOnEnterState` and `despawn_entities_on_enter_state` which
is the `OnEnter` equivalent.

> [!NOTE]
> Compared to #16284, the main change is that I did the rename in such a
way as to keep the terms `OnExit` and `OnEnter` together. In my own
game, I was adding `VisibleOnEnterState` and `HiddenOnExitState` and
when naming those, I kept the `OnExit` and `OnEnter` together. When I
checked #16284 it stood out to me that the naming was a bit awkward.
Putting the `State` in the middle and breaking up `OnEnter` and `OnExit`
also breaks searching for those terms.

## Open questions

1. Should we split `enable_state_scoped_entities` into two functions,
one for the `OnEnter` and one for the `OnExit`? I personally have zero
need thus far for the `OnEnter` version, so I'd be interested in not
having this enabled unless I ask for it.
2. If yes to 1., should we follow my lead in my `Visibility` state
components (see below) and name these
`app.enable_despawn_entities_on_enter_state()` and
`app.enable_despawn_entities_on_exit_state()`, which IMO says what it
does on the tin?

## Testing

Ran all changed examples.

## Side note: `VisibleOnEnterState` and `HiddenOnExitState`

For reference to anyone else and to help with the open questions, I'm
including the code I wrote for controlling entity visibility when a
state is entered/exited.

<details>
<summary>visibility.rs</summary>

```rust
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::prelude::*;
use bevy_state::{prelude::*, state::StateTransitionSteps};
use tracing::*;

pub trait AppExtStates {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self;

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self;
}

impl AppExtStates for App {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        self.main_mut()
            .enable_visible_entities_on_enter_state::<S>();
        self
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        self.main_mut().enable_hidden_entities_on_exit_state::<S>();
        self
    }
}

impl AppExtStates for SubApp {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Visible entities on enter state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_visible_on_enter_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Hidden entities on exit state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_hidden_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }
}

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct VisibleOnEnterState<S: States>(pub S);

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct HiddenOnExitState<S: States>(pub S);

/// Makes entities marked with [`VisibleOnEnterState<S>`] visible when the state
/// `S` is entered.
pub fn update_to_visible_on_enter_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&VisibleOnEnterState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(entered) = &transition.entered else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *entered {
            visibility.set_if_neq(Visibility::Visible);
        }
    }
}

/// Makes entities marked with [`HiddenOnExitState<S>`] invisible when the state
/// `S` is exited.
pub fn update_to_hidden_on_exit_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&HiddenOnExitState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(exited) = &transition.exited else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *exited {
            visibility.set_if_neq(Visibility::Hidden);
        }
    }
}
```

</details>

---------

Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-06 00:37:04 +00:00
Tim Overbeek
60cdefd128
Derive clone_behavior for Components (#18811)
Allow Derive(Component) to specify a clone_behavior

```rust
#[derive(Component)]
#[component(clone_behavior = Ignore)]
MyComponent;
```
2025-05-06 00:32:59 +00:00
Eagster
af8d12c3e1
deprecate SimpleExecutor (#18753)
# Objective

Contributes to #18741 and #18453.

## Solution

Deprecate `SimpleExecutor`. If users run into migration issues, we can
backtrack. Otherwise, we follow this up with #18741

We can't easily deprecate the module too because of
[this](https://github.com/rust-lang/rust/issues/47238).

## Testing

CI

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Cyrill Schenkel <cyrill.schenkel@gmail.com>
2025-05-06 00:21:57 +00:00
Jonathan Chan Kwan Yin
cdcb773e9b
Add EntityWorldMut::reborrow_scope() (#18730)
# Objective

Allow `EntityCommand` implementors to delegate to other entity commands
easily:

```rs
impl EntityCommand for Foo {
    fn apply(self, mut entity: EntityWorldMut) {
        entity.reborrow_scope(|e| StepOne.apply(e));
        entity.reborrow_scope(|e| StepTwo.apply(e));
    }
}
```
2025-05-06 00:19:56 +00:00
re0312
5ed8e0639a
Merge ObserverState and Observer into single component (#18728)
# Objective

- bevy removed `Observe` type parameters in #15151 ,it enables merging
`Observer` and `ObserverState ` into a single component. with this
consolidation ,we can improve efficiency while reducing boilerplate.

## Solution

- remove `ObserverState `and merge it  into `Observer`

## Testing

40%~60% performance win due to removal of redundant look up.

![image](https://github.com/user-attachments/assets/eb1d46cb-cca3-4c2b-948c-bf4ecb617de9)

This also improves ergonomics when using dynamic observer
```rust
// previously 
world.spawn(ObserverState {
            // SAFETY: we registered `event_a` above and it matches the type of EventA
            descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) },
            runner: |mut world, _trigger, _ptr, _propagate| {
                world.resource_mut::<Order>().observed("event_a");
            },
            ..Default::default()
        });

// now
let observe = unsafe {
    Observer::with_dynamic_runner(|mut world, _trigger, _ptr, _propagate| {
        world.resource_mut::<Order>().observed("event_a");
    })
    .with_event(event_a)
};
world.spawn(observe);
```
2025-05-06 00:12:27 +00:00
Chris Russell
3442e2556d
Use new run_without_applying_deferred method in SingleThreadedExecutor (#18684)
# Objective

Simplify code in the `SingleThreadedExecutor` by removing a special case
for exclusive systems.

The `SingleThreadedExecutor` runs systems without immediately applying
deferred buffers. That required calling `run_unsafe()` instead of
`run()`, but that would `panic` for exclusive systems, so the code also
needed a special case for those. Following #18076 and #18406, we have a
`run_without_applying_deferred` method that has the exact behavior we
want and works on exclusive systems.

## Solution

Replace the code in `SingleThreadedExecutor` that runs systems with a
single call to `run_without_applying_deferred()`. Also add this as a
wrapper in the `__rust_begin_short_backtrace` module to preserve the
special behavior for backtraces.
2025-05-06 00:09:02 +00:00
Mincong Lu
023b502153
Implemented Alpha for f32. (#18653)
# Objective

`f32` can be used to represent alpha, this streamlines generic code
related to colors.

## Solution

- Implemented `Alpha` for `f32`.
2025-05-06 00:00:17 +00:00
Mincong Lu
818459113e
Added StableInterpolate implementations for linear colors. (#18601)
# Objective

Colors currently do not implement `StableInterpolate`, which makes them
ineligible for functions like `smooth_nudge` and make some generic APIs
awkward.

## Solution

Implemented `StableInterpolate` for linear color types that should be
uncontroversial. Non-linear types like `Hsl` are not implemented in this
PR.

## Testing

Added a test that checks implementations are correct.
2025-05-05 23:58:56 +00:00
andriyDev
798e1c5498
Move initializing the ScreenshotToScreenPipeline to the ScreenshotPlugin. (#18524)
# Objective

- Minor cleanup.
- This seems to have been introduced in #8336. There is no discussion
about it I can see, there's no comment explaining why this is here and
not in `ScreenshotPlugin`. This seems to have just been misplaced.

## Solution

- Move this to the ScreenshotPlugin!

## Testing

- The screenshot example still works at least on desktop.
2025-05-05 23:56:22 +00:00
Eagster
f6543502b4
Add BundleRemover (#18521)
# Objective

It has long been a todo item in the ecs to create a `BundleRemover`
alongside the inserter, spawner, etc.

This is an uncontroversial first step of #18514.

## Solution

Move existing code from complex helper functions to one generalized
`BundleRemover`.

## Testing

Existing tests.
2025-05-05 23:55:04 +00:00
Chris Russell
bea0a0a9bc
Let FilteredEntity(Ref|Mut) receive access when nested. (#18236)
# Objective

Let `FilteredEntityRef` and `FilteredEntityMut` receive access when
nested inside tuples or `#[derive(QueryData)]` types. Make sure to
exclude any access that would conflict with other subqueries!

Fixes #14349

## Solution

Replace `WorldQuery::set_access(state, access)` with a new method,
`QueryData::provide_extra_access(state, access, available_access)`, that
passes both the total available access and the currently used access.
This is called after `WorldQuery::update_component_access()`, so any
access used by ordinary subqueries will be known. `FilteredEntityRef`
and `FilteredEntityMut` can use the combination to determine how much
access they can safely take, while tuples can safely pass those
parameters directly to their subqueries.

This requires a new `Access::remove_conflicting_access()` method that
can be used to remove any access that would conflict with existing
access. Implementing this method was easier by first factoring some
common set manipulation code out of `Access::extend`. I can extract that
refactoring to a separate PR if desired.

Have `FilteredEntity(Ref|Mut)` store `Access` instead of
`FilteredAccess` because they do not need to keep track of the filter.
This was necessary in an early draft but no longer is. I left it in
because it's small and I'm touching that code anyway, but I can extract
it to a separate PR if desired.
2025-05-05 23:23:46 +00:00
NiseVoid
02d569d0e4
Add Allows filter to bypass DefaultQueryFilters (#18192)
# Objective

Fixes #17803 

## Solution

- Add an `Allows<T>` `QueryFilter` that adds archetypal access for `T`
- Fix access merging to include archetypal from both sides

## Testing

- Added a case to the unit test for the application of
`DefaultQueryFilters`
2025-05-05 23:21:26 +00:00
Eagster
bfc76c589e
Remove insert_or_spawn function family (#18148)
# Objective

Based on and closes #18054, this PR builds on #18035 and #18147 to
remove:

- `Commands::insert_or_spawn_batch`
- `Entities::alloc_at_without_replacement`
- `Entities::alloc_at`
- `entity::AllocAtWithoutReplacement`
- `World::insert_or_spawn_batch`
- `World::insert_or_spawn_batch_with_caller`

## Testing

Just removing unused, deprecated code, so no new tests. Note that as of
writing, #18035 is still under testing and review.

## Future Work

Per
[this](https://github.com/bevyengine/bevy/issues/18054#issuecomment-2689088899)
comment on #18054, there may be additional performance improvements
possible to the entity allocator now that `alloc_at` no longer is
supported. At a glance, I don't see anything obvious to improve, but it
may be worth further investigation in the future.

---------

Co-authored-by: JaySpruce <jsprucebruce@gmail.com>
2025-05-05 23:14:32 +00:00
Jean Mertz
3b24f520b9
feat(log): support customizing default log formatting (#17722)
The LogPlugin now allows overriding the default
`tracing_subscriber::fmt::Layer` through a new `fmt_layer` option. This
enables customization of the default log output format without having to
replace the entire logging system.

For example, to disable timestamps in the log output:

```rust
fn fmt_layer(_app: &mut App) -> Option<bevy::log::BoxedFmtLayer> {
    Some(Box::new(
        bevy::log::tracing_subscriber::fmt::Layer::default()
            .without_time()
            .with_writer(std::io::stderr),
    ))
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
            fmt_layer,
            ..default()
        }))
        .run();
}
```

This is different from the existing `custom_layer` option, because that
option _adds_ additional layers to the subscriber, but can't modify the
default formatter layer (at least, not to my knowledge).

I almost always disable timestamps in my Bevy logs, and usually also
tweak other default log formatting (such as `with_span_events`), which
made it so that I always had to disable the default logger. This allows
me to use everything the Bevy logger supports (including tracy support),
while still formatting the default logs the way I like them.

---------

Signed-off-by: Jean Mertz <git@jeanmertz.com>
2025-05-05 23:01:06 +00:00
Antony
bf42cb3532
Add a viewport UI widget (#17253)
# Objective

Add a viewport widget.

## Solution

- Add a new `ViewportNode` component to turn a UI node into a viewport.
- Add `viewport_picking` to pass pointer inputs from other pointers to
the viewport's pointer.
- Notably, this is somewhat functionally different from the viewport
widget in [the editor
prototype](https://github.com/bevyengine/bevy_editor_prototypes/pull/110/files#L124),
which just moves the pointer's location onto the render target. Viewport
widgets have their own pointers.
  - Care is taken to handle dragging in and out of viewports.
- Add `update_viewport_render_target_size` to update the viewport node's
render target's size if the node size changes.
- Feature gate picking-related viewport items behind
`bevy_ui_picking_backend`.

## Testing

I've been using an example I made to test the widget (and added it as
`viewport_node`):

<details><summary>Code</summary>

```rust
//! A simple scene to demonstrate spawning a viewport widget. The example will demonstrate how to
//! pick entities visible in the widget's view.

use bevy::picking::pointer::PointerInteraction;
use bevy::prelude::*;

use bevy::ui::widget::ViewportNode;
use bevy::{
    image::{TextureFormatPixelInfo, Volume},
    window::PrimaryWindow,
};
use bevy_render::{
    camera::RenderTarget,
    render_resource::{
        Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
    },
};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, MeshPickingPlugin))
        .add_systems(Startup, test)
        .add_systems(Update, draw_mesh_intersections)
        .run();
}

#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
struct Shape;

fn test(
    mut commands: Commands,
    window: Query<&Window, With<PrimaryWindow>>,
    mut images: ResMut<Assets<Image>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // Spawn a UI camera
    commands.spawn(Camera3d::default());

    // Set up an texture for the 3D camera to render to
    let window = window.get_single().unwrap();
    let window_size = window.physical_size();
    let size = Extent3d {
        width: window_size.x,
        height: window_size.y,
        ..default()
    };
    let format = TextureFormat::Bgra8UnormSrgb;
    let image = Image {
        data: Some(vec![0; size.volume() * format.pixel_size()]),
        texture_descriptor: TextureDescriptor {
            label: None,
            size,
            dimension: TextureDimension::D2,
            format,
            mip_level_count: 1,
            sample_count: 1,
            usage: TextureUsages::TEXTURE_BINDING
                | TextureUsages::COPY_DST
                | TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        },
        ..default()
    };
    let image_handle = images.add(image);

    // Spawn the 3D camera
    let camera = commands
        .spawn((
            Camera3d::default(),
            Camera {
                // Render this camera before our UI camera
                order: -1,
                target: RenderTarget::Image(image_handle.clone().into()),
                ..default()
            },
        ))
        .id();

    // Spawn something for the 3D camera to look at
    commands
        .spawn((
            Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
            MeshMaterial3d(materials.add(Color::WHITE)),
            Transform::from_xyz(0.0, 0.0, -10.0),
            Shape,
        ))
        // We can observe pointer events on our objects as normal, the
        // `bevy::ui::widgets::viewport_picking` system will take care of ensuring our viewport
        // clicks pass through
        .observe(on_drag_cuboid);

    // Spawn our viewport widget
    commands
        .spawn((
            Node {
                position_type: PositionType::Absolute,
                top: Val::Px(50.0),
                left: Val::Px(50.0),
                width: Val::Px(200.0),
                height: Val::Px(200.0),
                border: UiRect::all(Val::Px(5.0)),
                ..default()
            },
            BorderColor(Color::WHITE),
            ViewportNode::new(camera),
        ))
        .observe(on_drag_viewport);
}

fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
    if matches!(drag.button, PointerButton::Secondary) {
        let mut node = node_query.get_mut(drag.target()).unwrap();

        if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
            node.left = Val::Px(left + drag.delta.x);
            node.top = Val::Px(top + drag.delta.y);
        };
    }
}

fn on_drag_cuboid(drag: Trigger<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
    if matches!(drag.button, PointerButton::Primary) {
        let mut transform = transform_query.get_mut(drag.target()).unwrap();
        transform.rotate_y(drag.delta.x * 0.02);
        transform.rotate_x(drag.delta.y * 0.02);
    }
}

fn draw_mesh_intersections(
    pointers: Query<&PointerInteraction>,
    untargetable: Query<Entity, Without<Shape>>,
    mut gizmos: Gizmos,
) {
    for (point, normal) in pointers
        .iter()
        .flat_map(|interaction| interaction.iter())
        .filter_map(|(entity, hit)| {
            if !untargetable.contains(*entity) {
                hit.position.zip(hit.normal)
            } else {
                None
            }
        })
    {
        gizmos.arrow(point, point + normal.normalize() * 0.5, Color::WHITE);
    }
}
```

</details>

## Showcase


https://github.com/user-attachments/assets/39f44eac-2c2a-4fd9-a606-04171f806dc1

## Open Questions

- <del>Not sure whether the entire widget should be feature gated behind
`bevy_ui_picking_backend` or not? I chose a partial approach since maybe
someone will want to use the widget without any picking being
involved.</del>
- <del>Is `PickSet::Last` the expected set for `viewport_picking`?
Perhaps `PickSet::Input` is more suited.</del>
- <del>Can `dragged_last_frame` be removed in favor of a better dragging
check? Another option that comes to mind is reading `Drag` and `DragEnd`
events, but this seems messier.</del>

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-05 22:57:37 +00:00
Chris Russell
55bb59b844
Stop using ArchetypeComponentId in the executor (#16885)
# Objective

Stop using `ArchetypeComponentId` in the executor. These IDs will grow
even more quickly with relations, and the size may start to degrade
performance.

## Solution

Have systems expose their `FilteredAccessSet<ComponentId>`, and have the
executor use that to determine which systems conflict. This can be
determined statically, so determine all conflicts during initialization
and only perform bit tests when running.

## Testing

I ran many_foxes and didn't see any performance changes. It's probably
worth testing this with a wider range of realistic schedules to see
whether the reduced concurrency has a cost in practice, but I don't know
what sort of test cases to use.

## Migration Guide

The schedule will now prevent systems from running in parallel if there
*could* be an archetype that they conflict on, even if there aren't
actually any. For example, these systems will now conflict even if no
entity has both `Player` and `Enemy` components:
```rust
fn player_system(query: Query<(&mut Transform, &Player)>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy)>) {}
```

To allow them to run in parallel, use `Without` filters, just as you
would to allow both queries in a single system:
```rust
// Either one of these changes alone would be enough
fn player_system(query: Query<(&mut Transform, &Player), Without<Enemy>>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy), Without<Player>>) {}
```
2025-05-05 22:52:44 +00:00
François Mockers
31c2dc591d
ignore files starting with . when loading folders (#11214)
# Objective

- When loading a folder with dot files inside, Bevy crashes:
```
thread 'IO Task Pool (1)' panicked at crates/bevy_asset/src/io/mod.rs:260:10:
asset paths must have extensions
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
- those files are common for other tools to store their
settings/metadata

## Solution

- Ignore files starting with a dot when loading folders
2025-05-05 22:42:01 +00:00
Chris Russell
5f936aefc8
Prevent exclusive systems from being used as observers (#19033)
# Objective

Prevent using exclusive systems as observers. Allowing them is unsound,
because observers are only expected to have `DeferredWorld` access, and
the observer infrastructure will keep pointers that are invalidated by
the creation of `&mut World`.

See
https://github.com/bevyengine/bevy/actions/runs/14778342801/job/41491517847?pr=19011
for a MIRI failure in a recent PR caused by an exclusive system being
used as an observer in a test.

## Solution

Have `Observer::new` panic if `System::is_exclusive()` is true. Document
that method, and methods that call it, as panicking.

(It should be possible to express this in the type system so that the
calls won't even compile, but I did not want to attempt that.)

## Testing

Added a unit test that calls `World::add_observer` with an exclusive
system.
2025-05-05 17:46:25 +00:00
akimakinai
0f6d532a15
Sprite picking docs fix (#19016)
# Objective

- Docs in sprite picking plugin / example contain outdated information.

References:
- Sprite picking now always require `Picking` - #17842
- Transparency pass-through added - #16388

## Solution

- Fix the docs.
2025-05-05 17:45:14 +00:00
JaySpruce
113d1b7dc1
Fix sparse set components ignoring insert_if_new/InsertMode (#19059)
# Objective

I've been tinkering with ECS insertion/removal lately, and noticed that
sparse sets just... don't interact with `InsertMode` at all. Sure
enough, using `insert_if_new` with a sparse component does the same
thing as `insert`.

# Solution

- Add a check in `BundleInfo::write_components` to drop the new value if
the entity already has the component and `InsertMode` is `Keep`.
- Add necessary methods to sparse set internals to fetch the drop
function.

# Testing

Minimal reproduction:
<details>
<summary>Code</summary>

```
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(PostStartup, component_print)
        .run();
}

#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseComponent(u32);

fn setup(mut commands: Commands) {
    let mut entity = commands.spawn_empty();
    entity.insert(SparseComponent(1));
    entity.insert(SparseComponent(2));

    let mut entity = commands.spawn_empty();
    entity.insert(SparseComponent(3));
    entity.insert_if_new(SparseComponent(4));
}

fn component_print(query: Query<&SparseComponent>) {
    for component in &query {
        info!("{}", component.0);
    }
}
```

</details>

Here it is on Bevy Playground (0.15.3): 

https://learnbevy.com/playground?share=2a96a68a81e804d3fdd644a833c1d51f7fa8dd33fc6192fbfd077b082a6b1a41

Output on `main`:
```
2025-05-04T17:50:50.401328Z  INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:50:50.401583Z  INFO system{name="fork::component_print"}: fork: 4
```

Output with this PR :
```
2025-05-04T17:51:33.461835Z  INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:51:33.462091Z  INFO system{name="fork::component_print"}: fork: 3
```
2025-05-05 17:42:36 +00:00
Lucas Franca
54856d088d
Upgrade atomicow version (#19075)
# Objective
`atomicow` `1.0` does not have `std` feature requested by `bevy_asset`,
but `1.1` does

## Solution

Bump version
2025-05-05 17:38:31 +00:00
Greeble
b516e78317
Bump crate-ci/typos from 1.31.1 to 1.32.0 (#19072)
Adopted #19066. Bumps
[crate-ci/typos](https://github.com/crate-ci/typos) from 1.31.1 to
1.32.0.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 17:27:36 +00:00
Rob Parrett
831fe305e4
Update ktx2 to 0.4.0 (#19073)
# Objective

Adopted #19065
Closes #19065

Updates the requirements on [ktx2](https://github.com/BVE-Reborn/ktx2)
to permit the latest version.
- [Release notes](https://github.com/BVE-Reborn/ktx2/releases)
-
[Changelog](https://github.com/BVE-Reborn/ktx2/blob/trunk/CHANGELOG.md)
- [Commits](https://github.com/BVE-Reborn/ktx2/compare/v0.3.0...v0.4.0)

# Overview

- Some renames
- A `u8` became `NonZero<u8>`
- Some methods return a new `Level` struct with a `data` member instead
of raw level data.

# Testing

- Passed CI locally
- Ran several examples which utilize `ktx2` files: `scrolling_fog`,
`mixed_lighting`, `skybox`, `lightmaps`.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:42:36 +00:00
Zachary Harrold
ea7e868f5c
Strip unused features from bevy_asset dependencies (#18979)
# Objective

- Contributes to #18978

## Solution

- Disable default features on all dependencies in `bevy_asset` and
explicitly enable ones that are required.
- Remove `compile_error` caused by enabling `file_watcher` without
`multi_threaded` by including `multi_threaded` in `file_watcher`.

## Testing

- CI

---

## Notes

No breaking changes here, just a little cleaning before the more
controversial changes for `no_std` support.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-05 05:51:01 +00:00
Guillaume Gomez
c286e4f5f3
Update sysinfo version to 0.35.0 (#19028)
This release is mostly about bugfixes and API/code improvements. Pretty
straightforward update. :)
2025-05-05 05:49:23 +00:00
Innokentiy Popov
2c3d20d748
Fix rotate_by implementation for Aabb2d (#19015)
# Objective

Fixes #18969 

## Solution

Also updated `Aabb3d` implementation for consistency.

## Testing

Added tests for `Aabb2d` and `Aabb3d` to verify correct rotation
behavior for angles greater than 90 degrees.
2025-05-04 13:05:27 +00:00
Brezak
e05e74a76a
Implement RelationshipSourceCollection for IndexSet (#18471)
# Objective

`IndexSet` doesn't implement `RelationshipSourceCollection`

## Solution

Implement `MapEntities` for `IndexSet`
Implement `RelationshipSourceCollection` for `IndexSet`

## Testing

`cargo clippy`

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-04 10:17:29 +00:00
Brezak
c6d41a0d34
Implement RelationshipSourceCollection for BTreeSet (#18469)
# Objective

`BTreeSet` doesn't implement `RelationshipSourceCollection`.

## Solution

Implement it.

## Testing

`cargo clippy`

---

## Showcase

You can now use `BTreeSet` in a `RelationshipTarget`

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-04 09:18:07 +00:00
Chris Russell
d28e4908ca
Create a When system param wrapper for skipping systems that fail validation (#18765)
# Objective

Create a `When` system param wrapper for skipping systems that fail
validation.

Currently, the `Single` and `Populated` parameters cause systems to skip
when they fail validation, while the `Res` family causes systems to
error. Generalize this so that any fallible parameter can be used either
to skip a system or to raise an error. A parameter used directly will
always raise an error, and a parameter wrapped in `When<P>` will always
cause the system to be silently skipped.

~~Note that this changes the behavior for `Single` and `Populated`. The
current behavior will be available using `When<Single>` and
`When<Populated>`.~~

Fixes #18516

## Solution

Create a `When` system param wrapper that wraps an inner parameter and
converts all validation errors to `skipped`.

~~Change the behavior of `Single` and `Populated` to fail by default.~~

~~Replace in-engine use of `Single` with `When<Single>`. I updated the
`fallible_systems` example, but not all of the others. The other
examples I looked at appeared to always have one matching entity, and it
seemed more clear to use the simpler type in those cases.~~

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-04 08:41:42 +00:00
tmstorey
c55c69e3fc
Add NonNilUuid support to bevy_reflect (#18604)
# Objective

- If using a `NonNilUuid` in Bevy, it's difficult to reflect it.

## Solution

- Adds `NonNilUuid` using `impl_reflect_opaque!`.

## Testing

- Built with no issues found locally.
- Essentially the same as the `Uuid` support except without `Default`.

Co-authored-by: TM Storey <mail@tmstorey.id.au>
2025-05-04 08:22:57 +00:00
ickshonpe
5e2ecf4178
Text background colors (#18892)
# Objective

Add background colors for text.

Fixes #18889

## Solution

New component `TextBackgroundColor`, add it to any UI `Text` or
`TextSpan` entity to add a background color to its text.
New field on `TextLayoutInfo` `section_rects` holds the list of bounding
rects for each text section.

The bounding rects are generated in `TextPipeline::queue_text` during
text layout, `extract_text_background_colors` extracts the colored
background rects for rendering.

Didn't include `Text2d` support because of z-order issues.

The section rects can also be used to implement interactions targeting
individual text sections.

## Testing
Includes a basic example that can be used for testing:
```
cargo run --example text_background_colors
```
---

## Showcase


![tbcm](https://github.com/user-attachments/assets/e584e197-1a8c-4248-82ab-2461d904a85b)

Using a proportional font with kerning the results aren't so tidy (since
the bounds of adjacent glyphs can overlap) but it still works fine:


![tbc](https://github.com/user-attachments/assets/788bb052-4216-4019-a594-7c1b41164dd5)

---------

Co-authored-by: Olle Lukowski <lukowskiolle@gmail.com>
Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
2025-05-04 08:18:46 +00:00
Martín Maita
8c34cbbb27
Add TextureAtlas convenience methods (#19023)
# Objective

- Add a few useful methods to `TextureAtlas`.

## Solution

- Added `TextureAtlas::with_index()`.
- Added `TextureAtlas::with_layout()`.

## Testing

- CI checks.
2025-05-04 08:11:59 +00:00
Taj Holliday
2affecdb07
Audio sink seek adopted (#18971)
Adopted #13869 

# Objective

Fixes #9076 

## Solution

Using `rodio`'s `try_seek`

## Testing

@ivanstepanovftw added a `seek` system using `AudioSink` to the
`audio_control.rs` example. I got it working with .mp3 files, but rodio
doesn't support seeking for .ogg and .flac files, so I removed it from
the commit (since the assets folder only has .ogg files). Another thing
to note is that `try_seek` fails when using `PlaybackMode::Loop`, as
`rodio::source::buffered::Buffered` doesn't support `try_seek`. I
haven't tested `SpatialAudioSink`.

## Notes

I copied the docs for `try_seek` verbatim from `rodio`, and re-exported
`rodio::source::SeekError`. I'm not completely confident in those
decisions, please let me know if I'm doing anything wrong.

</details>

---------

Co-authored-by: Ivan Stepanov <ivanstepanovftw@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-03 11:29:38 +00:00
Peter S.
cd67bac544
Expose deferred screen edges setting for ios devices (#18729)
# Objective

- This just exposes the preferred [screen edges deferring system
gestures](https://developer.apple.com/documentation/uikit/uiviewcontroller/preferredscreenedgesdeferringsystemgestures)
setting from
[winit](https://docs.rs/winit/latest/winit/platform/ios/trait.WindowExtIOS.html#tymethod.set_preferred_screen_edges_deferring_system_gestures),
making it accessible in bevy apps.

This setting is useful for ios apps that make use of the screen edges,
letting the app have control of the first edge gesture before relegating
to the os.


## Testing

- Tested on simulator and on an iPhone Xs

---

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-04-30 21:24:53 +00:00
ickshonpe
21b62d640b
Change the default visual box for OverflowClipMargin to PaddingBox (#18935)
# Objective

The default should be `OverflowClipBox::PaddingBox` not
`OverflowClipBox::ContentBox`

`padding-box` is the default in CSS. 

## Solution

Set the default to `PaddingBox`.

## Testing

Compare the `overflow` UI example on main vs with this PR. You should
see that on main the outline around the inner node gets clipped. With
this PR by default clipping starts at the inner edge of the border (the
`padding-box`) and the outlines are visible.

Fixes #18934
2025-04-30 21:00:42 +00:00
Brezak
3631a64a3d
Add a method to clear all related entity to EntityCommands and friends (#18907)
# Objective

We have methods to:
- Add related entities
- Replace related entities
- Remove specific related entities

We don't have a method the remove all related entities so.

## Solution

Add a method to remove all related entities.

## Testing

A new test case.
2025-04-30 20:59:29 +00:00
François Mockers
0fa115f911
fix new nightly lint on mikktspace (#18988)
# Objective

- new nightly lint make CI fail

## Solution

- Follow the lint: https://github.com/rust-lang/rust/pull/123239
2025-04-30 05:19:01 +00:00
1.11e-1f64
9fca353782
Make AccessConflicts::is_empty public (#18688)
# Objective

When implementing `SystemParam` for an object which contains a mutable
reference to World, which cannot be derived due to a required lifetime
parameter, it's necessary to check that there aren't any conflicts.

As far as I know, the is_empty method is the only way provided to check
for no conflicts at all
2025-04-28 21:48:46 +00:00
Frédéric Vauchelles
19682aa4c3
Added derive Reflect to UntypedHandle and UntypedAssetId (#18827)
# Objective

- We have the ability to serialize/deserialize `Handle<T>` with
[`TypedReflectSerializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectSerializer.html)
and
[`TypedReflectDeserializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectDeserializer.html),
but it is not possible for `UntypedHandle`.
- `Handle<T>` already has the `Reflect` derive, so it sounds coherent to
also have this derive also on the untyped API

## Solution

- Add the `Reflect` derive macro to both `UntypedHandle` and `
UntypedAssetId`.

## Testing

- I used a custom processor to handle the serialization based on the
example of
[`TypedReflectSerializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectSerializer.html)
(see [source
code](https://docs.rs/bevy_reflect/0.15.3/src/bevy_reflect/serde/ser/serializer.rs.html#149))

Co-authored-by: Frédéric Vauchelles <frederic.vauchelles@outlook.com>
2025-04-28 21:46:36 +00:00
mhsalem36
2556405feb
Fixes #15389, added docs to RawHandleWrapper::_window field (#18832)
# Objective

- Fixes https://github.com/bevyengine/bevy/issues/15389.
- Add documentation for RawHandleWrapper::_window field since It's
needed to drop the window at the correct time.

## Solution

- Added documentation to RawHandleWrapper::_window field as same as
WindowWrapper documentation.

## Testing

- No testing needed since it is documentation. 

---
2025-04-28 21:44:57 +00:00
JoshValjosh
7f9eae2c87
Fully qualify crate paths in BevyManifest (#18938)
# Objective

Subtle, very minor issue. The following code fails to compile on main:
```rs
struct bevy;
#[derive(::bevy::ecs::component::Component)]
struct MyComponent;
```
The derive proc macro is pasting in essentially:
```rs
impl bevy::ecs::component::Component for MyComponent
```
...which normally works, but I've added `struct bevy`, which makes the
proc macro spit out incorrect code.

Very cursed, but to my knowledge has never been encountered in practice.
All the same, it's technically incorrect and should be fixed.

## Solution

The solution is simply to prepend `::` to crate names. Specifically, all
(all?) Bevy's derive macros determine the root crate name using
`BevyManifest`, which does some toml-parsing witchcraft to figure out
whether to qualify names using the umbrella `bevy` crate or individual
`bevy_xxx` crates. I just added a `::` to the spot where we parse the
`syn::Path`. The above example compiles properly after that.

## Testing

- CI should catch any errors since this change should cause compile
errors if for some reason they're being used in a cursed way somewhere
that would make this break something.

## Note

If this does break something for someone, this *really* needs a comment
in `BevyManifest::maybe_get_path` explaining why we can't make this
change.
2025-04-28 21:43:48 +00:00
Gino Valente
31e9a3c411
bevy_reflect: Re-reflect hashbrown types (#18944)
# Objective

Fixes #18943

## Solution

Reintroduces support for `hashbrown`'s `HashMap` and `HashSet` types.
These were inadvertently removed when `bevy_platform` newtyped the
`hashbrown` types.

Since we removed our `hashbrown` dependency, I gated these impls behind
a `hashbrown` feature. Not entirely sure if this is necessary since we
enabled it for `bevy_reflect` through `bevy_platform` anyways. (Complex
features still confuse me a bit so let me know if I can just remove it!)

I also went ahead and preemptively implemented `TypePath` for `PassHash`
while I was here.

## Testing

You can test that it works by adding the following to a Bevy example
based on this PR (you'll also need to include `hashbrown` of course):

```rust
#[derive(Reflect)]
struct Foo(hashbrown::HashMap<String, String>);
```

Then check it compiles with:

```
cargo check --example hello_world --no-default-features --features=bevy_reflect/hashbrown
```
2025-04-28 19:26:53 +00:00
Krzysztof Żywiecki
7f0490655c
Removed conversion from pointer physical coordinates to viewport local coordinates in bevy_picking make_ray function (#18870)
# Objective

- Fixes #18856.

## Solution

After PR #17633, `Camera::viewport_to_world` method corrects
`viewport_position` passed in that input so that it's offset by camera's
viewport. `Camera::viewport_to_world` is used by `make_ray` function
which in turn also offsets pointer position by viewport position, which
causes picking objects to be shifted by viewport position, and it wasn't
removed in the aforementioned PR. This second offsetting in `make_ray`
was removed.

## Testing

- I tested simple_picking example by applying some horizontal offset to
camera's viewport.
- I tested my application that displayed a single rectangle with picking
on two cameras arranged in a row. When using local bevy with this fix,
both cameras can be used for picking correctly.
- I modified split_screen example: I added observer to ground plane that
changes color on hover, and removed UI as it interfered with picking
both on master and my branch. On master, only top left camera was
triggering the observer, and on my branch all cameras could change
plane's color on hover.
- I added viewport offset to mesh_picking, with my changes it works
correctly, while on master picking ray is shifted.
- Sprite picking with viewport offset doesn't work both on master and on
this branch.

These are the only scenarios I tested. I think other picking functions
that use this function should be tested but I couldn't track more uses
of it.

Co-authored-by: Krzysztof Zywiecki <krzysiu@pop-os.Dlink>
2025-04-27 13:54:28 +00:00
Martín Maita
17da7e13f2
Update spin requirement from 0.9.8 to 0.10.0 (#18655)
# Objective

- Closes #18643 

## Solution

- Updated spin requirement from 0.9.8 to 0.10.0.
- Renamed `spin/portable_atomic` feature to `spin/portable-atomic`.

## Testing

- CI checks (already passing in the dependabot PR).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-27 06:18:24 +00:00
Kristoffer Søholm
78d9f05d31
Update cosmic-text to 0.14 (#18651)
# Objective

New cosmic-text version is out, we should use it.

## Solution

Bump the version.

## Testing

Code compiles, text example works
2025-04-26 21:36:24 +00:00
Olle Lukowski
9167f02bdf
Create EntityCommands::remove_if (#18899)
# Objective

Fixes #18857.

## Solution

Add the requested method, and a `try_` variant as well.

## Testing

It compiles, doctests succeed, and is trivial enough that I don't think
it needs a unit test (correct me if I'm wrong though).
2025-04-26 21:32:02 +00:00
ickshonpe
2b7cecd5b3
FontAtlasSet fixes (#18850)
# Objective

Fix problems with  `FontAtlasSet`:

* `FontAtlasSet` derives `Asset` but `FontAtlasSet`s are not Bevy
assets.
* The doc comments state that `FontAtlasSet`s are assets that are
created automatically when fonts are loaded. They aren't, they are
created as needed during text updates.

## Solution
* Removed the `Asset` derive.
* Rewrote the doc comments.
2025-04-26 21:22:40 +00:00
JoshValjosh
9cf1a1afa2
Remove unused query param (#18924)
# Objective

Was copying off `bevy_ui`'s homework writing a picking backend and
noticed the `Has<IsDefaultPickingCamera>` is not used anywhere.

## Testing

Ran a random example.

This shouldn't cause any behavioral changes at all because the
component/archetype access/filter flags should be the same. `Has<X>`
doesn't affect access since it doesn't actually read or write anything,
and it doesn't affect matched archetypes either. Can't think of another
reason any behavior would change.
2025-04-26 21:17:14 +00:00
Rahmat Nazali Salimi
26f0ce272e
Fix minor typo on bevy_ecs example (#18926)
# Objective

A small typo was found on `bevy_ecs/examples/event.rs`.
I know it's very minor but I'd think fixing it would still help others
in the long run.

## Solution

Fix the typo.

## Testing

I don't think this is necessary.
2025-04-26 21:16:09 +00:00
François Mockers
12f71a8936
don't overflow when relations are empty (#18891)
# Objective

- Fixes #18890 

## Solution

- Don't overflow when substracting, bound at 0

## Testing

- Reproducer from the issue now works
2025-04-21 20:38:43 +00:00
charlotte
18e1bf1c3d
Swap order of eviction/extraction when extracting for specialization (#18846)
# Objective

Fixes #18843 

## Solution

We need to account for the material being added and removed in the
course of the same frame. We evict the caches first because the entity
will be re-added if it was marked as needing specialization, which
avoids another check on removed components to see if it was "really"
despawned.
2025-04-15 06:44:01 +00:00
charlotte
6eaa6a6a03
Revert attempt to fix memory leak (#18845)
This reverts commit a9b0b4e7f7.
2025-04-15 01:57:53 +00:00
Carter Anderson
f3f6cad43c
Panic on overlapping one-to-one relationships (#18833)
# Objective

One to one relationships (added in
https://github.com/bevyengine/bevy/pull/18087) can currently easily be
invalidated by having two entities relate to the same target.

Alternative to #18817 (removing one-to-one relationships)

## Solution

Panic if a RelationshipTarget is already targeted. Thanks @urben1680 for
the idea!

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-04-15 01:25:37 +00:00
Patrick Walton
56784de769
Fix the ordering of the systems introduced in #18734. (#18825)
There's still a race resulting in blank materials whenever a material of
type A is added on the same frame that a material of type B is removed.
PR #18734 improved the situation, but ultimately didn't fix the race
because of two issues:

1. The `late_sweep_material_instances` system was never scheduled. This
PR fixes the problem by scheduling that system.

2. `early_sweep_material_instances` needs to be called after *every*
material type has been extracted, not just when the material of *that*
type has been extracted. The `chain()` added during the review process
in PR #18734 broke this logic. This PR reverts that and fixes the
ordering by introducing a new `SystemSet` that contains all material
extraction systems.

I also took the opportunity to switch a manual reference to
`AssetId::<StandardMaterial>::invalid()` to the new
`DUMMY_MESH_MATERIAL` constant for clarity.

Because this is a bug that can affect any application that switches
material types in a single frame, I think this should be uplifted to
Bevy 0.16.
2025-04-14 21:17:48 +00:00
Carter Anderson
59bdaca29d
Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816)
This reverts commit ac52cca033.

Fixes #18815

# Objective

#18782 resulted in using `log` macros instead of `tracing` macros (in
the interest of providing no_std support, specifically no_atomic
support). That tradeoff isn't worth it, especially given that tracing is
likely to get no_atomic support.

## Solution

Revert #18782
2025-04-14 21:15:01 +00:00
JaySpruce
045ef4c307
Add remove_children and remove_related to EntityWorldMut and EntityCommands (#18835)
Fixes #18834.

`EntityWorldMut::remove_children` and `EntityCommands::remove_children`
were removed in the relationships overhaul (#17398) and never got
replaced.

I don't *think* this was intentional (the methods were never mentioned
in the PR or its comments), but I could've missed something.
2025-04-14 20:27:08 +00:00
Matty Weatherley
47f46b5bdf
Expose the output curve type in with_derivative (#18826)
# Objective

I was wrong about how RPITIT works when I wrote this stuff initially,
and in order to actually give people access to all the traits
implemented by the output (e.g. Debug and so on) it's important to
expose the real output type, even if it makes the trait uglier and less
comprehensible. (☹️)

## Solution

Expose the curve output type of the `CurveWithDerivative` trait and its
double-derivative companion. I also added a bunch of trait derives to
`WithDerivative<T>`, since I think that was just an oversight.
2025-04-14 20:18:00 +00:00
JaySpruce
e3384bb8f0
Fix wrong method call in relationship replacement command (#18824)
Fixes a small mix-up from #18058, which added bulk relationship
replacement methods.

`EntityCommands::replace_related_with_difference` calls
`EntityWorldMut::replace_children_with_difference` instead of
`EntityWorldMut::replace_related_with_difference`, which means it always
operates on the `ChildOf` relationship instead of the `R: Relationship`
generic it's provided.

`EntityCommands::replace_children_with_difference` takes an `R:
Relationship` generic that it shouldn't, but it accidentally works
correctly on `main` because it calls the above method.
2025-04-14 20:15:33 +00:00
Alice Cecile
9254297acd
Use never_say_never hack to work around Rust 2024 regression for fn traits (#18804)
# Objective

After #17967, closures which always panic no longer satisfy various Bevy
traits. Principally, this affects observers, systems and commands.

While this may seem pointless (systems which always panic are kind of
useless), it is distinctly annoying when using the `todo!` macro, or
when writing tests that should panic.

Fixes #18778.

## Solution

- Add failing tests to demonstrate the problem
- Add the trick from
[`never_say_never`](https://docs.rs/never-say-never/latest/never_say_never/)
to name the `!` type on stable Rust
- Write looots of docs explaining what the heck is going on and why
we've done this terrible thing

## To do

Unfortunately I couldn't figure out how to avoid conflicting impls, and
I am out of time for today, the week and uh the week after that.
Vacation! If you feel like finishing this for me, please submit PRs to
my branch and I can review and press the button for it while I'm off.

Unless you're Cart, in which case you have write permissions to my
branch!

- [ ] fix for commands
- [ ] fix for systems
- [ ] fix for observers
- [ ] revert https://github.com/bevyengine/bevy-website/pull/2092/

## Testing

I've added a compile test for these failure cases and a few adjacent
non-failing cases (with explicit return types).

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-04-14 19:59:48 +00:00
lcnr
d7ec6a90f2
remove reliance on a trait solver inference bug (#18840)
The parameter `In` of `call_inner` is completely unconstrained by its
arguments and return type. We are only able to infer it by assuming that
the only associated type equal to `In::Param<'_>` is `In::Param<'_>`
itself. It could just as well be some other associated type which only
normalizes to `In::Param<'_>`. This will change with the next-generation
trait solver and was encountered by a crater run
https://github.com/rust-lang/rust/pull/133502-

cc
https://github.com/rust-lang/trait-system-refactor-initiative/issues/168

I couldn't think of a cleaner alternative here. I first tried to just
provide `In` as an explicit type parameter. This is also kinda ugly as I
need to provide a variable number of them and `${ignore(..)}` is
currently still unstable https://github.com/rust-lang/rust/issues/83527.

Sorry for the inconvenience. Also fun that this function exists to avoid
a separate solver bug in the first place 😅
2025-04-14 19:55:31 +00:00
charlotte
6f3ea06060
Make sure the mesh actually exists before we try to specialize. (#18836)
Fixes #18809
Fixes #18823

Meshes despawned in `Last` can still be in visisible entities if they
were visible as of `PostUpdate`. Sanity check that the mesh actually
exists before we specialize. We still want to unconditionally assume
that the entity is in `EntitySpecializationTicks` as its absence from
that cache would likely suggest another bug.
2025-04-14 19:09:02 +00:00
charlotte
a9b0b4e7f7
Mark render assets as modified when removed from the asset server (#18814)
# Objective

Fixes #18808

## Solution

When an asset emits a removed event, mark it as modified in the render
world to ensure any appropriate bookkeeping runs as necessary.
2025-04-11 23:18:26 +00:00
Carter Anderson
e9a0ef49f9
Rename bevy_platform_support to bevy_platform (#18813)
# Objective

The goal of `bevy_platform_support` is to provide a set of platform
agnostic APIs, alongside platform-specific functionality. This is a high
traffic crate (providing things like HashMap and Instant). Especially in
light of https://github.com/bevyengine/bevy/discussions/18799, it
deserves a friendlier / shorter name.

Given that it hasn't had a full release yet, getting this change in
before Bevy 0.16 makes sense.

## Solution

- Rename `bevy_platform_support` to `bevy_platform`.
2025-04-11 23:13:28 +00:00
François Mockers
88f863efed
don't disable std in bevy_dylib (#18807)
# Objective

- `bevy_dylib` currently doesn't build independently
```
cargo build -p bevy_dylib
   Compiling bevy_dylib v0.16.0-rc.4 (/crates/bevy_dylib)
error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait

error: `#[panic_handler]` function required, but not found

error: unwinding panics are not supported without std
  |
  = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
  = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem

error: could not compile `bevy_dylib` (lib) due to 3 previous errors
```

## Solution

- remove `#![no_std]` from `bevy_dylib`

## Testing

- it builds now
2025-04-11 18:44:53 +00:00
Zachary Harrold
ac52cca033
Allow partial support for bevy_log in no_std (#18782)
# Objective

- Fixes #18781

## Solution

- Moved `LogPlugin` into its own file gated behind a new `tracing`
feature.
- Used `log` instead of `tracing` where possible.
- Exposed a new `tracing` feature in `bevy` which enables
`bevy_log/tracing`.
- Gated `LogPlugin` from `DefaultPlugins` on `tracing` feature.

## Testing

- CI

---

## Migration Guide

- If you were previously using `bevy_log` with default features
disabled, enable the new `std` and `tracing` features.
- If you were using `bevy` with the default features disabled, enable
the new `tracing` feature.

## Notes

Almost all of the diffs in this PR come from moving `LogPlugin` into its
own file. This just makes the PR less noisy, since the alternative is
excessive `#[cfg(feature = "tracing")]` directives all over the plugin.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-04-11 01:44:38 +00:00
charlotte
24baf324d6
Allowlist mali drivers for gpu preprocessing support. (#18769)
Fixes #17591

Looking at the arm downloads page, "r48p0" is a version number that
increments, where rXX is the major version and pX seems to be a patch
version. Take the conservative approach here that we know gpu
preprocessing is working on at least version 48 and presumably higher.
The assumption here is that the driver_info string will be reported
similarly on non-pixel devices.
2025-04-11 00:03:54 +00:00
Alice Cecile
31bb878d6d
Fix system param validation for piped systems (#18785)
# Objective

- Piped systems are an edge case that we missed when reworking system
parameter validation.
- Fixes #18755.

## Solution

- Validate the parameters for both systems, ~~combining the errors if
both failed validation~~ by simply using an early out.
- ~~Also fix the same bug for combinator systems while we're here.~~

## Testing

I've added a large number of tests checking the behavior under various
permutations. These are separate tests, rather than one mega test
because a) it's easier to track down bugs that way and b) many of these
are `should_panic` tests, which will halt the evaluation of the rest of
the test!

I've also added a test for exclusive systems being pipeable because we
don't have one and I was very surprised that that works!

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-04-10 23:16:22 +00:00
Carter Anderson
2944f5e79d
Ignore RenderEntity during entity clones (#18798)
# Objective

Fixes #18795

## Solution

Ignore RenderEntity during entity clones
2025-04-10 20:46:34 +00:00
Gilles Henaux
e7558cd0da
Fix documentation: incorrect references to the Update schedule in ExitCondition (#18438)
# Objective

- The referenced `ScheduleLabel` for `OnPrimaryClosed` and `OnAllClosed`
in `ExitCondition` was incorrect

## Solution

- Changed `Update` to `PostUpdate`
2025-04-10 20:31:18 +00:00
Greeble
25a8b9f9f9
Fix newline in PointLightShadowMap comment (#18791)
A clippy failure slipped into #18768, although I'm not sure why CI
didn't catch it.

```sh
> cargo clippy --version
clippy 0.1.85 (4eb161250e 2025-03-15)

> cargo run -p ci
...
error: empty line after doc comment
   --> crates\bevy_pbr\src\light\mod.rs:105:5
    |
105 | /     /// The width and height of each of the 6 faces of the cubemap.
106 | |
    | |_^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
    = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
    = help: if the empty line is unintentional remove it
help: if the documentation should include the empty line include it in the comment
    |
106 |     ///
    |
```
2025-04-10 18:05:11 +00:00
Zachary Harrold
73f03327c2
Add ? (#18783)
# Objective

- Fixes #18780

## Solution

- Add `?`

## Testing

- CI

---

## Notes

_smol_
2025-04-10 00:27:09 +00:00
jf908
ea69012799
Small docs PR for PointLightShadowMap/DirectionalLightShadowMap (#18768)
# Objective

- Improve the docs for `PointLightShadowMap` and
`DirectionalLightShadowMap`

## Solution

- Add example for how to use `PointLightShadowMap` and move the
`DirectionalLightShadowMap` example from `DirectionalLight`.
- Match `PointLight` and `DirectionalLight` docs about shadows.
- Describe what `size` means.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-04-09 22:23:21 +00:00
charlotte
ee7b624024
Fix forward decal depth_fade_factor. (#18772)
Fixes #18758
2025-04-09 22:00:28 +00:00
charlotte
e799625ea5
Add binned 2d/3d Wireframe render phase (#18587)
# Objective

Fixes #16896
Fixes #17737

## Solution

Adds a new render phase, including all the new cold specialization
patterns, for wireframes. There's a *lot* of regrettable duplication
here between 3d/2d.

## Testing

All the examples.

## Migration Guide
- `WireframePlugin` must now be created with
`WireframePlugin::default()`.
2025-04-09 21:34:53 +00:00
Patrick Walton
6c619397d5
Unify RenderMaterialInstances and RenderMeshMaterialIds, and fix an associated race condition. (#18734)
Currently, `RenderMaterialInstances` and `RenderMeshMaterialIds` are
very similar render-world resources: the former maps main world meshes
to typed material asset IDs, and the latter maps main world meshes to
untyped material asset IDs. This is needlessly-complex and wasteful, so
this patch unifies the two in favor of a single untyped
`RenderMaterialInstances` resource.

This patch also fixes a subtle issue that could cause mesh materials to
be incorrect if a `MeshMaterial3d<A>` was removed and replaced with a
`MeshMaterial3d<B>` material in the same frame. The problematic pattern
looks like:

1. `extract_mesh_materials<B>` runs and, seeing the
`Changed<MeshMaterial3d<B>>` condition, adds an entry mapping the mesh
to the new material to the untyped `RenderMeshMaterialIds`.

2. `extract_mesh_materials<A>` runs and, seeing that the entity is
present in `RemovedComponents<MeshMaterial3d<A>>`, removes the entry
from `RenderMeshMaterialIds`.

3. The material slot is now empty, and the mesh will show up as whatever
material happens to be in slot 0 in the material data slab.

This commit fixes the issue by splitting out `extract_mesh_materials`
into *three* phases: *extraction*, *early sweeping*, and *late
sweeping*, which run in that order:

1. The *extraction* system, which runs for each material, updates
`RenderMaterialInstances` records whenever `MeshMaterial3d` components
change, and updates a change tick so that the following system will know
not to remove it.

2. The *early sweeping* system, which runs for each material, processes
entities present in `RemovedComponents<MeshMaterial3d>` and removes each
such entity's record from `RenderMeshInstances` only if the extraction
system didn't update it this frame. This system runs after *all*
extraction systems have completed, fixing the race condition.

3. The *late sweeping* system, which runs only once regardless of the
number of materials in the scene, processes entities present in
`RemovedComponents<ViewVisibility>` and, as in the early sweeping phase,
removes each such entity's record from `RenderMeshInstances` only if the
extraction system didn't update it this frame. At the end, the late
sweeping system updates the change tick.

Because this pattern happens relatively frequently, I think this PR
should land for 0.16.
2025-04-09 21:32:10 +00:00
charlotte
f75078676b
Initialize pre-processing pipelines only when culling is enabled. (#18759)
Better fix for #18463 that still allows enabling mesh preprocessing on
webgpu.

Fixes #18463
2025-04-09 21:31:29 +00:00
Patrick Walton
065a95e0a1
Make the StandardMaterial bindless index table have a fixed size regardless of the features that are enabled. (#18771)
Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in #18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in #18025 to do so. Thus this patch has a dependency on #18025.

Closes #18104.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-04-09 20:39:42 +00:00
Brian Reavis
bc259fad04
Fix get_render_pipeline_state / get_compute_pipeline_state panic (#18752)
This fixes a panic that occurs if one calls
`PipelineCache::get_render_pipeline_state(id)` or
`PipelineCache::get_compute_pipeline_state(id)` with a queued pipeline
id that has not yet been processed by `PipelineCache::process_queue()`.

```
thread 'Compute Task Pool (0)' panicked at [...]/bevy/crates/bevy_render/src/render_resource/pipeline_cache.rs:611:24:
index out of bounds: the len is 0 but the index is 20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
2025-04-09 16:55:19 +00:00
charlotte
e8fd750274
Fix unbatchable meshes. (#18761)
# Objective

Fixes #18550.

Because bin state for unbatchable meshes wasn't being cleared each
frame, the buffer indices for unbatchable meshes would demote from
sparse to dense storage and aggressively leak memory, with all kinds of
weird consequences downstream, namely supplying invalid instance ranges
for render.

## Solution

Clear out the unbatchable mesh bin state when we start a new frame.
2025-04-09 15:35:33 +00:00
Patrick Walton
dc7c8f228f
Add bindless support back to ExtendedMaterial. (#18025)
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
2025-04-09 15:34:44 +00:00
Freyja-moth
714b4a43d6
Change with_related to work with a Bundle and added with_relationships method (#18699)
# Objective

Fixes #18678

## Solution

Moved the current `with_related` method to `with_relationships` and
added a new `with_related` that uses a bundle.

I'm not entirely sold on the name just yet, if anyone has any ideas let
me know.

## Testing

I wasn't able to test these changes because it crashed my computer every
time I tried (fun). But there don't seem to be any tests that use the
old `with_related` method so it should be fine, hopefully

## Showcase

```rust
commands.spawn_empty()
    .with_related::<Relationship>(Name::new("Related thingy"))
    .with_relationships(|rel| {
        rel.spawn(Name::new("Second related thingy"));
    });
```

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-04-09 02:34:49 +00:00
Moony
0410212f3d
Expose the added tick for change detection, both getting and setting. (#18746)
# Objective

- Allow viewing and setting the added tick for change detection aware
data, to allow operations like checking if the value has been modified
since first being added, and spoofing that state (i.e. returning the
value to default in place without a remove/insert dance)

## Solution

- Added corresponding functions matching the existing `changed` API:
  - `fn added(&self) -> Tick`
  - `fn set_added(&mut self)`
  - `fn set_last_added(&mut self, last_added: Tick)`

Discussed on discord @
https://canary.discord.com/channels/691052431525675048/749335865876021248/1358718892465193060

## Testing

- Running the bevy test suite by.. making a PR, heck.
- No new tests were introduced due to triviality (i.e. I don't know what
to test about this API, and the corresponding API for `changed` is
similarly lacking tests.)

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-04-08 21:13:27 +00:00