Commit Graph

8316 Commits

Author SHA1 Message Date
aecsocket
f232674291
Remove unnecessary PartialReflect bound on DeserializeWithRegistry (#17560)
# Objective

The new `DeserializeWithRegistry` trait in 0.15 was designed to be used
with reflection, and so has a `trait DeserializeWithRegistry:
PartialReflect` bound. However, this bound is not actually necessary for
the trait to function properly. And this `PartialReflect` bound already
exists on:
```rs
impl<T: PartialReflect + for<'de> DeserializeWithRegistry<'de>> FromType<T>
    for ReflectDeserializeWithRegistry
```
So there is no point in constraining the trait itself with this bound as
well.

This lets me use `DeserializeWithRegistry` with non-`Reflect` types,
which I want to do to avoid making a bunch of `FooDeserializer` structs
and `impl DeserializeSeed` on them.

## Solution

Removes this unnecessary bound.

## Testing

Trivial change, does not break compilation or `bevy_reflect` tests.

## Migration Guide

`DeserializeWithRegistry` types are no longer guaranteed to be
`PartialReflect` as well. If you were relying on this type bound, you
should add it to your own bounds manually.
```diff
- impl<T: DeserializeWithRegistry> Foo for T { .. }
+ impl<T: DeserializeWithRegistry + PartialReflect> Foo for T { .. }
```
2025-01-29 17:36:39 +00:00
Jean Mertz
b58eda01e2
feat(ecs): add EntityEntryCommands::entity() method chaining (#17580)
This allows you to continue chaining method calls after calling
`EntityCommands::entry`:

```rust
commands
    .entity(player.entity)
    .entry::<Level>()
    // Modify the component if it exists
    .and_modify(|mut lvl| lvl.0 += 1)
    // Otherwise insert a default value
    .or_insert(Level(0))
    // Return the EntityCommands for the entity
    .entity()
    // And continue chaining method calls
    .insert(Name::new("Player"));
```

---------

Signed-off-by: Jean Mertz <git@jeanmertz.com>
2025-01-29 17:36:02 +00:00
ickshonpe
d4356062bf
UiSurface::upsert_node refactor (#8831)
# Objective

Simplify the `UiSurface::upsert_node` method by directly matching on
HashMap entry states.
2025-01-28 18:05:59 +00:00
ickshonpe
5bbcf646a7
Improved UI camera mapping (#17244)
# Objective

Two more optimisations for UI extraction:
* We only need to query for the camera's render entity when the target
camera changes. If the target camera is the same as for the previous UI
node we can use the previous render entity.
* The cheap checks for visibility and zero size should be performed
first before the camera queries.

## Solution
Add a new system param `UiCameraMap` that resolves the correct render
camera entity and only queries when necessary.

<img width="506" alt="tracee"
src="https://github.com/user-attachments/assets/f57d1e0d-f3a7-49ee-8287-4f01ffc8ba24"
/>

I don't like the `UiCameraMap` + `UiCameraMapper` implementation very
much, maybe someone else can suggest a better construction.

This is partly motivated by #16942 which adds further indirection and
these changes would ameliorate that performance regression.
2025-01-28 18:05:00 +00:00
ickshonpe
a80263a5bf
no-camera many_buttons argument, only emit UI camera warnings once (#17557)
# Objective

* Add a `no-camera` argument to the `many_buttons` stress test example.
* Only emit the UI "no camera found" warnings once.
2025-01-28 18:04:52 +00:00
Vic
b039bf6768
implement UniqueEntityVec (#17549)
# Objective

In #16547, we added `EntitySet`s/`EntitySetIterator`s. We can know
whenever an iterator only contains unique entities, however we do not
yet have the ability to collect and reuse these without either the
unsafe `UniqueEntityIter::from_iterator_unchecked`, or the expensive
`HashSet::from_iter`.
An important piece for being able to do this is a `Vec` that maintains
the uniqueness property, can be collected into, and is itself
`EntitySet`.

A lot of entity collections are already intended to be "unique", but
have no way of expressing that when stored, other than using an
aforementioned `HashSet`. Such a type helps by limiting or even removing
the need for unsafe on the user side when not using a validated `Set`
type, and makes it easier to interface with other infrastructure like
f.e. `RelationshipSourceCollection`s.

## Solution

We implement `UniqueEntityVec`. 

This is a wrapper around `Vec`, that only ever contains unique elements.
It mirrors the API of `Vec`, however restricts any mutation as to not
violate the uniqueness guarantee. Meaning:
- Any inherent method which can introduce new elements or mutate
existing ones is now unsafe, f.e.: `insert`, `retain_mut`
- Methods that are impossible to use safely are omitted, f.e.: `fill`,
`extend_from_within`

A handful of the unsafe methods can do element-wise mutation
(`retain_mut`, `dedup_by`), which can be an unwind safety hazard were
the element-wise operation to panic. For those methods, we require that
each individual execution of the operation upholds uniqueness, not just
the entire method as a whole.

To be safe for mutable usage, slicing and the associated slice methods
require a matching `UniqueEntitySlice` type , which we leave for a
follow-up PR.

Because this type will deref into the `UniqueEntitySlice` type, we also
offer the immutable `Vec` methods on this type (which only amount to a
handful). "as inner" functionality is covered by additional
`as_vec`/`as_mut_vec` methods + `AsRef`/`Borrow` trait impls.
Like `UniqueEntityIter::from_iterator_unchecked`, this type has a
`from_vec_unchecked` method as well.

The canonical way to safely obtain this type however is via
`EntitySetIterator::collect_set` or
`UniqueEntityVec::from_entity_set_iter`. Like mentioned in #17513, these
are named suboptimally until supertrait item shadowing arrives, since a
normal `collect` will still run equality checks.
2025-01-28 06:00:59 +00:00
Sven Niederberger
b25bbb79a0
Image::get_color_at_3d and Image::set_color_at_3d: Support 2D images with layers (#17548)
# Objective

This makes the `Image::get_color_at_3d` and `Image::set_color_at_3d`
methods work with 2D images with more than one layer.

## Solution

- The Z coordinate is interpreted as the layer number.

## Testing

- Added a test: `get_set_pixel_2d_with_layers`.
2025-01-28 05:58:37 +00:00
mgi388
e8cd12daf4
Automatically transform cursor hotspot user asks to flip cursor image (#17540)
# Objective

- As discussed in
https://github.com/bevyengine/bevy/issues/17276#issuecomment-2611203714,
we should transform the cursor's hotspot if the user is asking for the
image to be flipped.
- This becomes more important when a `scale` transform option exists.
It's harder for users to transform the hotspot themselves when using
`scale` because they'd need to look up the image to get its dimensions.
Instead, we let Bevy handle the hotspot transforms and make the
`hotspot` field the "original/source" hotspot.
- Refs #17276.

## Solution

- When the image needs to be transformed, also transform the hotspot. If
the image does not need to be transformed (i.e. fast path), no hotspot
transformation is applied.

## Testing

- Ran the example: `cargo run --example custom_cursor_image
--features=custom_cursor`.
- Add unit tests for the hotspot transform function.
- I also ran the example I have in my `bevy_cursor_kit` crate, which I
think is a good illustration of the reason for this PR.
- In the following videos, there is an arrow pointing up. The button
hover event fires as I move the mouse over it.
- When I press `Y`, the cursor flips. 
- In the first video, on `bevy@main` **before** this PR, notice how the
hotspot is wrong after flipping and no longer hovering the button. The
arrow head and hotspot are no longer synced.
- In the second video, on the branch of **this** PR, notice how the
hotspot gets flipped as soon as I press `Y` and the cursor arrow head is
in the correct position on the screen and still hovering the button.
Speaking back to the objective listed at the start: The user originally
defined the _source_ hotspot for the arrow. Later, they decide they want
to flip the cursor vertically: It's nice that Bevy can automatically
flip the _source_ hotspot for them at the same time it flips the
_source_ image.

First video (main):


https://github.com/user-attachments/assets/1955048c-2f85-4951-bfd6-f0e7cfef0cf8

Second video (this PR):


https://github.com/user-attachments/assets/73cb9095-ecb5-4bfd-af5b-9f772e92bd16
2025-01-28 05:49:46 +00:00
jiang heng
dfac3b9bfd
Fix window close in example cause panic (#17533)
# Objective

Fixes #17532 

## Solution

- check window valide
2025-01-28 05:37:23 +00:00
Luc
51bb4f08a9
expose OverflowAxis::Hidden as Overflow functions (#17528)
# Objective
expose `OverflowAxis::Hidden` as functions of `Overflow`, just as it is
done for `OverflowAxis::Hidden` and `OverflowAxis::Scroll`.
2025-01-28 05:34:50 +00:00
NiseVoid
203d0b4aae
Move bounding_2d example to math folder (#17523)
# Objective

The bounding_2d example was originally placed in 2d_rendering because
there was no folder for bounding or math, but now that this folder exist
it makes no sense for it to be here.

## Solution

Move the example

## Testing

I ran the example
2025-01-28 05:29:05 +00:00
Lucas Franca
644efd6b03
Fix calculation of skybox rotation (#17476)
# Objective

Fixes #16628 

## Solution

Matrices were being applied in the wrong order.

## Testing

Ran `skybox` example with rotations applied to the `Skybox` on the `x`,
`y`, and `z` axis (one at a time).

e.g.
```rust
Skybox {
    image: skybox_handle.clone(),
    brightness: 1000.0,
    rotation: Quat::from_rotation_y(-45.0_f32.to_radians()),
}
```

## Showcase


[Screencast_20250121_151232.webm](https://github.com/user-attachments/assets/3df68714-f5f1-4d8c-8e08-cbab525a8bda)
2025-01-28 05:27:22 +00:00
berry
95174f3c6e
Fix docs mistake in bevy_ecs::world (#17336)
# Objective

- Correct a mistake in the rustdoc for bevy_ecs::world::World.

## Solution

- The rustdoc wrongly stated that "Each component can have up to one
instance of each component type.". This sentence should presumably be
"Each *Entity* can have up to one instance of each component type.".
Applying this change makes the prior sentence "Each [`Entity`] has a set
of components." redundant.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-01-28 05:20:31 +00:00
Sludge
581034a7e9
Reflect and register the wireframe materials (#17334)
# Objective

These were missing, but can trivially be reflected.

## Solution

Do that.
2025-01-28 05:19:34 +00:00
Brendon
99b0d574f9
Update render_resource gpu buffer doc comments (#17118)
Minor improvement to the render_resource doc comments; specifically, the
gpu buffer types
- makes them consistently reference each other
- reorders them to be alphabetical
- removes duplicated entries
2025-01-28 05:13:04 +00:00
MevLyshkin
68b779c31f
Add alpha mode implementation to shader_material_2d (#16603)
## Objective 

Bevy 0.15 introduced new method in `Material2d` trait- `alpha_mode`.
Before that when new material was created it had alpha blending, now it
does not.

## Solution 

While I am okay with it, it could be useful to add the new trait method
implementation to one of the samples so users are more aware of it.

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2025-01-28 05:09:30 +00:00
poopy
15f00278e7
Rename ArgList::push methods to with and add new push methods which take &mut self (#16567)
# Objective

The `ArgList::push` family of methods consume `self` and return a new
`ArgList` which means they can't be used with `&mut ArgList` references.

```rust
fn foo(args: &mut ArgList) {
    args.push_owned(47_i32); // doesn't work :(
}
```

It's typical for `push` methods on other existing types to take `&mut
self`.

## Solution

Renamed the existing push methods to `with_arg`, `with_ref` etc and
added new `push` methods which take `&mut self`.

## Migration Guide

Uses of the `ArgList::push` methods should be replaced with the `with`
counterpart.

<details>

| old | new |
| --- | --- |
| push_arg | with_arg |
| push_ref | with_ref |
| push_mut | with_mut |
| push_owned | with_owned | 
| push_boxed | with_boxed |

</details>
2025-01-28 05:06:50 +00:00
Chris Russell
514a35c656
Share implementation of sort methods. (#16203)
# Objective

The various `Query::sort()` methods have a lot of duplicated code
between them, including some unsafe code. Reduce the duplication to make
the code easier to read and maintain.

## Solution

Extract the duplicated code to a private method, and pass in the sorting
strategy as a closure.

## Testing

I used `cargo-show-asm` to verify that the closures were inlined, but I
didn't run anything through a profiler. The `sort()` method itself even
had identical assembly before and after this change, although the others
did not.
2025-01-28 04:57:54 +00:00
ickshonpe
c0ccc87738
UI material border radius (#15171)
# Objective

I wrote a box shadow UI material naively thinking I could use the border
widths attribute to hold the border radius but it
doesn't work as the border widths are automatically set in the
extraction function. Need to send border radius to the shader seperately
for it to be viable.

## Solution

Add a `border_radius` vertex attribute to the ui material.

This PR also removes the normalization of border widths for custom UI
materials. The regular UI shader doesn't do this so it's a bit confusing
and means you can't use the logic from `ui.wgsl` in your custom UI
materials.

## Testing / Showcase

Made a change to the `ui_material` example to display border radius:

```cargo run --example ui_material```

<img width="569" alt="corners" src="https://github.com/user-attachments/assets/36412736-a9ee-4042-aadd-68b9cafb17cb" />
2025-01-28 04:54:48 +00:00
Al M.
37893a3f9e
Fix Visibility link on b0004 error page (#17562)
Per title. Note the "improve this page" link on the
[site](https://bevyengine.org/learn/errors/b0004/) gives a 404.
2025-01-27 18:06:22 +00:00
Patrick Walton
7aeb1c51a6
Disable clustered decals on Metal. (#17554)
Unfortunately, Apple platforms don't have enough texture bindings to
properly support clustered decals. This should be fixed once `wgpu` has
first-class bindless texture support. In the meantime, we disable them.

Closes #17553.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-01-27 05:39:07 +00:00
Patrick Walton
dda97880c4
Implement experimental GPU two-phase occlusion culling for the standard 3D mesh pipeline. (#17413)
*Occlusion culling* allows the GPU to skip the vertex and fragment
shading overhead for objects that can be quickly proved to be invisible
because they're behind other geometry. A depth prepass already
eliminates most fragment shading overhead for occluded objects, but the
vertex shading overhead, as well as the cost of testing and rejecting
fragments against the Z-buffer, is presently unavoidable for standard
meshes. We currently perform occlusion culling only for meshlets. But
other meshes, such as skinned meshes, can benefit from occlusion culling
too in order to avoid the transform and skinning overhead for unseen
meshes.

This commit adapts the same [*two-phase occlusion culling*] technique
that meshlets use to Bevy's standard 3D mesh pipeline when the new
`OcclusionCulling` component, as well as the `DepthPrepass` component,
are present on the camera. It has these steps:

1. *Early depth prepass*: We use the hierarchical Z-buffer from the
previous frame to cull meshes for the initial depth prepass, effectively
rendering only the meshes that were visible in the last frame.

2. *Early depth downsample*: We downsample the depth buffer to create
another hierarchical Z-buffer, this time with the current view
transform.

3. *Late depth prepass*: We use the new hierarchical Z-buffer to test
all meshes that weren't rendered in the early depth prepass. Any meshes
that pass this check are rendered.

4. *Late depth downsample*: Again, we downsample the depth buffer to
create a hierarchical Z-buffer in preparation for the early depth
prepass of the next frame. This step is done after all the rendering, in
order to account for custom phase items that might write to the depth
buffer.

Note that this patch has no effect on the per-mesh CPU overhead for
occluded objects, which remains high for a GPU-driven renderer due to
the lack of `cold-specialization` and retained bins. If
`cold-specialization` and retained bins weren't on the horizon, then a
more traditional approach like potentially visible sets (PVS) or low-res
CPU rendering would probably be more efficient than the GPU-driven
approach that this patch implements for most scenes. However, at this
point the amount of effort required to implement a PVS baking tool or a
low-res CPU renderer would probably be greater than landing
`cold-specialization` and retained bins, and the GPU driven approach is
the more modern one anyway. It does mean that the performance
improvements from occlusion culling as implemented in this patch *today*
are likely to be limited, because of the high CPU overhead for occluded
meshes.

Note also that this patch currently doesn't implement occlusion culling
for 2D objects or shadow maps. Those can be addressed in a follow-up.
Additionally, note that the techniques in this patch require compute
shaders, which excludes support for WebGL 2.

This PR is marked experimental because of known precision issues with
the downsampling approach when applied to non-power-of-two framebuffer
sizes (i.e. most of them). These precision issues can, in rare cases,
cause objects to be judged occluded that in fact are not. (I've never
seen this in practice, but I know it's possible; it tends to be likelier
to happen with small meshes.) As a follow-up to this patch, we desire to
switch to the [SPD-based hi-Z buffer shader from the Granite engine],
which doesn't suffer from these problems, at which point we should be
able to graduate this feature from experimental status. I opted not to
include that rewrite in this patch for two reasons: (1) @JMS55 is
planning on doing the rewrite to coincide with the new availability of
image atomic operations in Naga; (2) to reduce the scope of this patch.

A new example, `occlusion_culling`, has been added. It demonstrates
objects becoming quickly occluded and disoccluded by dynamic geometry
and shows the number of objects that are actually being rendered. Also,
a new `--occlusion-culling` switch has been added to `scene_viewer`, in
order to make it easy to test this patch with large scenes like Bistro.

[*two-phase occlusion culling*]:
https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501

[Aaltonen SIGGRAPH 2015]:

https://www.advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_combined_final_footer_220dpi.pdf

[Some literature]:

https://gist.github.com/reduz/c5769d0e705d8ab7ac187d63be0099b5?permalink_comment_id=5040452#gistcomment-5040452

[SPD-based hi-Z buffer shader from the Granite engine]:
https://github.com/Themaister/Granite/blob/master/assets/shaders/post/hiz.comp

## Migration guide

* When enqueuing a custom mesh pipeline, work item buffers are now
created with
`bevy::render::batching::gpu_preprocessing::get_or_create_work_item_buffer`,
not `PreprocessWorkItemBuffers::new`. See the
`specialized_mesh_pipeline` example.

## Showcase

Occlusion culling example:
![Screenshot 2025-01-15
175051](https://github.com/user-attachments/assets/1544f301-68a3-45f8-84a6-7af3ad431258)

Bistro zoomed out, before occlusion culling:
![Screenshot 2025-01-16
185425](https://github.com/user-attachments/assets/5114bbdf-5dec-4de9-b17e-7aa77e7b61ed)

Bistro zoomed out, after occlusion culling:
![Screenshot 2025-01-16
184949](https://github.com/user-attachments/assets/9dd67713-656c-4276-9768-6d261ca94300)

In this scene, occlusion culling reduces the number of meshes Bevy has
to render from 1591 to 585.
2025-01-27 05:02:46 +00:00
Patrick Walton
8620cd783c
Make the default directional light shadow cascade settings similar to those of other engines. (#17552)
Currently, our default maximum shadow cascade distance is 1000 m, which
is quite distant compared to that of Unity (150 m), Unreal Engine 5 (200
m), and Godot (100 m). I also adjusted the default first cascade far
bound to be 10 m, which matches that of Unity (10.05 m) and Godot (10
m). Together, these changes should improve the default sharpness of
shadows of directional lights for typical scenes.

## Migration Guide

* The default shadow cascade far distance has been changed from 1000 to
150, and the default first cascade far bound has been changed from 5 to
10, in order to be similar to the defaults of other engines.
2025-01-27 01:48:57 +00:00
Tim Overbeek
eb04f8a476
Simplify derive_from_world (#17534)
# Objective

simplify existing implementation

---------

Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2025-01-26 22:25:29 +00:00
AustinHellerRepo
1612d210fb
added Hash to MouseScrollUnit; (#17538)
# Objective

This allows for the usage of the MouseScrollUnit as a key to a HashSet
and HashMap. I have a need for this, but this basic functionality is
currently missing.

## Solution

Add the derive Hash attribute to the MouseScrollUnit type.

## Testing

- Did you test these changes? If so, how?
No, but I did perform a `cargo build`. My laptop is failing to run
`cargo test` without crashing.
- Are there any parts that need more testing?
If someone could run a `cargo test` for completeness, that would be
great but this is a trivial change.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
They simply need to ensure that the common Hash derive macro works as
expected for the basic MouseScrollUnit type.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Ubuntu 22.04
2025-01-26 22:24:50 +00:00
Jerome Humbert
499510489e
impl Eq + Hash for BindGroup/Layout (#17547)
# Objective

Implement `Eq` and `Hash` for the `BindGroup` and `BindGroupLayout`
wrappers.

## Solution

Implement based on the same assumption that the ID is unique, for
consistency with `PartialEq`.

## Testing

None; this should be straightforward. If there's an issue that would be
a design one.
2025-01-26 22:23:09 +00:00
Patrick Walton
1c765c9ae7
Add support for specular tints and maps per the KHR_materials_specular glTF extension. (#14069)
This commit allows specular highlights to be tinted with a color and for
the reflectance and color tint values to vary across a model via a pair
of maps. The implementation follows the [`KHR_materials_specular`] glTF
extension. In order to reduce the number of samplers and textures in the
default `StandardMaterial` configuration, the maps are gated behind the
`pbr_specular_textures` Cargo feature.

Specular tinting is currently unsupported in the deferred renderer,
because I didn't want to bloat the deferred G-buffers. A possible fix
for this in the future would be to make the G-buffer layout more
configurable, so that specular tints could be supported on an opt-in
basis. As an alternative, Bevy could force meshes with specular tints to
render in forward mode. Both of these solutions require some more
design, so I consider them out of scope for now.

Note that the map is a *specular* map, not a *reflectance* map. In Bevy
and Filament terms, the reflectance values in the specular map range
from [0.0, 0.5], rather than [0.0, 1.0]. This is an unfortunate
[`KHR_materials_specular`] specification requirement that stems from the
fact that glTF is specified in terms of a specular strength model, not
the reflectance model that Filament and Bevy use. A workaround, which is
noted in the `StandardMaterial` documentation, is to set the
`reflectance` value to 2.0, which spreads the specular map range from
[0.0, 1.0] as normal.

The glTF loader has been updated to parse the [`KHR_materials_specular`]
extension. Note that, unless the non-default `pbr_specular_textures` is
supplied, the maps are ignored. The `specularFactor` value is applied as
usual. Note that, as with the specular map, the glTF `specularFactor` is
twice Bevy's `reflectance` value.

This PR adds a new example, `specular_tint`, which demonstrates the
specular tint and map features. Note that this example requires the
[`KHR_materials_specular`] Cargo feature.

[`KHR_materials_specular`]:
https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_specular

## Changelog

### Added

* Specular highlights can now be tinted with the `specular_tint` field
in `StandardMaterial`.
* Specular maps are now available in `StandardMaterial`, gated behind
the `pbr_specular_textures` Cargo feature.
* The `KHR_materials_specular` glTF extension is now supported, allowing
for customization of specular reflectance and specular maps. Note that
the latter are gated behind the `pbr_specular_textures` Cargo feature.
2025-01-26 20:38:46 +00:00
Patrick Walton
fc831c390d
Implement basic clustered decal projectors. (#17315)
This commit adds support for *decal projectors* to Bevy, allowing for
textures to be projected on top of geometry. Decal projectors are
clusterable objects, just as punctual lights and light probes are. This
means that decals are only evaluated for objects within the conservative
bounds of the projector, and they don't require a second pass.

These clustered decals require support for bindless textures and as such
currently don't work on WebGL 2, WebGPU, macOS, or iOS. For an
alternative that doesn't require bindless, see PR #16600. I believe that
both contact projective decals in #16600 and clustered decals are
desirable to have in Bevy. Contact projective decals offer broader
hardware and driver support, while clustered decals don't require the
creation of bounding geometry.

A new example, `decal_projectors`, has been added, which demonstrates
multiple decals on a rotating object. The decal projectors can be scaled
and rotated with the mouse.

There are several limitations of this initial patch that can be
addressed in follow-ups:

1. There's no way to specify the Z-index of decals. That is, the order
in which multiple decals are blended on top of one another is arbitrary.
A follow-up could introduce some sort of Z-index field so that artists
can specify that some decals should be blended on top of others.

2. Decals don't take the normal of the surface they're projected onto
into account. Most decal implementations in other engines have a feature
whereby the angle between the decal projector and the normal of the
surface must be within some threshold for the decal to appear. Often,
artists can specify a fade-off range for a smooth transition between
oblique surfaces and aligned surfaces.

3. There's no distance-based fadeoff toward the end of the projector
range. Many decal implementations have this.

This addresses #2401.
 
## Showcase

![Screenshot 2025-01-11
052913](https://github.com/user-attachments/assets/8fabbafc-60fb-461d-b715-d7977e10fe1f)
2025-01-26 20:13:39 +00:00
Predko Silvestr
deb135c25c
Proportional scaling for the sprite's texture. (#17258)
# Objective

Bevy sprite image mode lacks proportional scaling for the underlying
texture. In many cases, it's required. For example, if it is desired to
support a wide variety of screens with a single texture, it's okay to
cut off some portion of the original texture.

## Solution

I added scaling of the texture during the preparation step. To fill the
sprite with the original texture, I scaled UV coordinates accordingly to
the sprite size aspect ratio and texture size aspect ratio. To fit
texture in a sprite the original `quad` is scaled and then the
additional translation is applied to place the scaled quad properly.


## Testing

For testing purposes could be used `2d/sprite_scale.rs`. Also, I am
thinking that it would be nice to have some tests for a
`crates/bevy_sprite/src/render/mod.rs:sprite_scale`.

---

## Showcase

<img width="1392" alt="image"
src="https://github.com/user-attachments/assets/c2c37b96-2493-4717-825f-7810d921b4bc"
/>
2025-01-24 18:24:02 +00:00
Vic
39a1e2b488
implement EntityIndexMap/Set (#17449)
# Objective

We do not have `EntityIndexMap`/`EntityIndexSet`.

Usual `HashMap`s/`HashSet`s do not guarantee any order, which can be
awkward for some use cases.
The `indexmap` versions remember insertion order, which then also
becomes their iteration order.
They can be thought of as a `HashTable` + `Vec`, which means fast
iteration and removal, indexing by index (not just key), and slicing!
Performance should otherwise be comparable.

## Solution

Because `indexmap` is structured to mirror `hashbrown`, it suffers the
same issue of not having the `Hasher` generic on their iterators. #16912
solved this issue for `EntityHashMap`/`EntityHashSet` with a wrapper
around the hashbrown version, so this PR does the same.

Hopefully these wrappers can be removed again in the future by having
`hashbrown`/`indexmap` adopt that generic in their iterators themselves!
2025-01-24 08:09:34 +00:00
spvky
40007cdb2e
Adds update interval config for FpsOverlayPlugin (#17489)
# Objective
Fixes #17487 

- Adds a new field `refresh_interval` to `FpsOverlayConfig` to allow the
user setting a minimum time before each refresh of the FPS display

## Solution

- Add `refresh_interval` to `FpsOverlayConfig`
- When updating the on screen text, check a duration of
`refresh_interval` has passed, if not, don't update the FPS counter

## Testing

- Created a new bevy project
- Included the `FpsOverlayPlugin` with the default `refresh_interval`
(100 ms)
- Included the `FpsOverlayPlugin` with an obnoxious `refresh_interval`
(2 seconds)
---

---------

Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-01-24 05:57:36 +00:00
Rostyslav Toch
af3a84fc0b
Add many_materials stress test (#17346)
# Objective

- This PR adds a new stress test called `many_materials` to benchmark
the rendering performance of many animated materials.
- Fixes #11588 
- This PR continues the work started in the previous PR #11592, which
was closed due to inactivity.

## Solution

- Created a new example (`examples/stress_tests/many_materials.rs`) that
renders a grid of cubes with animated materials.
- The size of the grid can be configured using the `-n` command-line
argument (or `--grid-size`). The default grid size is 10x10.
- The materials animate by cycling through colors in the HSL color
space.

## Testing

- I have tested these changes locally on my Linux machine.
- Reviewers can test the changes by running the example with different
grid sizes and observing the performance (FPS, frame time).
- I have not tested on other platforms (macOS, Windows, wasm), but I
expect it to work as the code uses standard Bevy features.

---

## Showcase

<details>
  <summary>Click to view showcase</summary>


![image](https://github.com/user-attachments/assets/b15209d4-f832-402b-a527-58e5048971d1)

</details>
2025-01-24 05:46:23 +00:00
Richard Jones
e20ac69cb3
Clarify docs for OnAdd, OnInsert, OnReplace, OnRemove triggers (#17512)
# Objective

- Trouble remembering the difference between `OnAdd` and `OnInsert` for
triggers. Would like a better doc for those triggers so it appears in my
editor tooltip.

## Solution

- Clarify docs for OnAdd, OnInsert, OnRemove, OnReplace. Based on
comments in the
[component_hook.rs](https://github.com/bevyengine/bevy/blob/main/examples/ecs/component_hooks.rs#L73)
example.


## Testing

- None, small doc fix.
2025-01-24 05:40:58 +00:00
Vic
94a238b0ef
implement FromEntitySetIterator (#17513)
# Objective

Some collections are more efficient to construct when we know that every
element is unique in advance.
We have `EntitySetIterator`s from #16547, but currently no API to safely
make use of them this way.

## Solution

Add `FromEntitySetIterator` as a subtrait to `FromIterator`, and
implement it for the `EntityHashSet`/`hashbrown::HashSet` types.
To match the normal `FromIterator`, we also add a
`EntitySetIterator::collect_set` method.
It'd be better if these methods could shadow `from_iter` and `collect`
completely, but https://github.com/rust-lang/rust/issues/89151 is needed
for that.

While currently only `HashSet`s implement this trait, future
`UniqueEntityVec`/`UniqueEntitySlice` functionality comes with more
implementors.

Because `HashMap`s are collected from tuples instead of singular types,
implementing this same optimization for them is more complex, and has to
be done separately.

## Showcase

This is basically a free speedup for collecting `EntityHashSet`s!

```rust
pub fn collect_milk_dippers(dippers: Query<Entity, (With<Milk>, With<Cookies>)>) {
    dippers.iter().collect_set::<EntityHashSet>();
    // or
    EntityHashSet::from_entity_set_iter(dippers);
}

---------

Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
2025-01-24 05:39:35 +00:00
mgi388
14ad25227b
Make CustomCursor variants CustomCursorImage/CustomCursorUrl structs (#17518)
# Objective

- Make `CustomCursor::Image` easier to work with by splitting the enum
variants off into `CustomCursorImage` and `CustomCursorUrl` structs and
deriving `Default` on those structs.
- Refs #17276.

## Testing

- Ran two examples: `cargo run --example custom_cursor_image
--features=custom_cursor` and `cargo run --example window_settings
--features=custom_cursor`
- CI.

---

## Migration Guide

The `CustomCursor` enum's variants now hold instances of
`CustomCursorImage` or `CustomCursorUrl`. Update your uses of
`CustomCursor` accordingly.
2025-01-24 05:39:04 +00:00
ickshonpe
e459dd94ec
Replace checks for empty uinodes (#17520)
# Objective

The `is_empty` checks that are meant to stop zero-sized uinodes from
being extracted are missing from `extract_uinode_background_colors`,
`extract_uinode_images` and `extract_ui_material_nodes`.

## Solution

Put them back.
2025-01-24 05:38:20 +00:00
Emerson Coskey
81a25bb0c7
Procedural atmospheric scattering (#16314)
Implement procedural atmospheric scattering from [Sebastien Hillaire's
2020 paper](https://sebh.github.io/publications/egsr2020.pdf). This
approach should scale well even down to mobile hardware, and is
physically accurate.

## Co-author: @mate-h 

He helped massively with getting this over the finish line, ensuring
everything was physically correct, correcting several places where I had
misunderstood or misapplied the paper, and improving the performance in
several places as well. Thanks!

## Credits

@aevyrie: helped find numerous bugs and improve the example to best show
off this feature :)

Built off of @mtsr's original branch, which handled the transmittance
lut (arguably the most important part)

## Showcase: 


![sunset](https://github.com/user-attachments/assets/2eee1f38-f66d-4772-bb72-163e13c719d8)

![twilight](https://github.com/user-attachments/assets/f7d358b6-898d-4df7-becc-188cd753102d)


## For followup

- Integrate with pcwalton's volumetrics code
- refactor/reorganize for better integration with other effects
- have atmosphere transmittance affect directional lights
- add support for generating skybox/environment map

---------

Co-authored-by: Emerson Coskey <56370779+EmersonCoskey@users.noreply.github.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
Co-authored-by: Emerson Coskey <coskey@emerlabs.net>
Co-authored-by: Máté Homolya <mate.homolya@gmail.com>
2025-01-23 22:52:46 +00:00
Zachary Harrold
d9ba1af87c
Fix Typo in bevy_platform_support's spin Feature (#17516)
# Objective

- Fix typo in `spin/portable-atomic` feature.

## Solution

- Replace with `spin/portable_atomic`

## Testing

- CI

---

## Notes

This is a very annoying design choice the `spin` developers made.
Because the _crate_ is called `portable-atomic` and is optional, Cargo
automatically registers the feature `portable-atomic`. But the
maintainers use `portable_atomic` for their _feature_ which enables the
support. Sneaks through CI because it's a valid feature and will only
cause breakage on atomically challenged platforms (which we currently
aren't testing in CI).

Should we test atomically challenged in CI? Right now I don't think so,
at least not until we've made "normal" `no_std` CI better with the main
`bevy` crate as the test-case rather than each individual crate.
2025-01-23 21:47:21 +00:00
Radislav Myasnikov
94e0e1f031
Updated the 2D examples to make them uniform (#17237)
# Objective

Make the examples look more uniform and more polished.
following the issue #17167

## Solution

- [x] Added a minimal UI explaining how to interact with the examples
only when needed.
- [x] Used the same notation for interactions ex : "Up Arrow: Move
Forward \nLeft / Right Arrow: Turn"
- [x] Set the color to
[GRAY](https://github.com/bevyengine/bevy/pull/17237#discussion_r1907560092)
when it's not visible enough
- [x] Changed some colors to be easy on the eyes
- [x] removed the //camera comment
- [x] Unified the use of capital letters in the examples.
- [x] Simplified the mesh2d_arc offset calculations.

...

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-01-23 16:46:58 +00:00
Zachary Harrold
9bc0ae33c3
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective

- Contributes to #16877

## Solution

- Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils`
and into `bevy_platform_support`
- Refactored the above to match the layout of these types in `std`.
- Updated crates as required.

## Testing

- CI

---

## Migration Guide

- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::hash`:
  - `FixedState`
  - `DefaultHasher`
  - `RandomState`
  - `FixedHasher`
  - `Hashed`
  - `PassHash`
  - `PassHasher`
  - `NoOpHash`
- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::collections`:
  - `HashMap`
  - `HashSet`
- `bevy_utils::hashbrown` has been removed. Instead, import from
`bevy_platform_support::collections` _or_ take a dependency on
`hashbrown` directly.
- `bevy_utils::Entry` has been removed. Instead, import from
`bevy_platform_support::collections::hash_map` or
`bevy_platform_support::collections::hash_set` as appropriate.
- All of the above equally apply to `bevy::utils` and
`bevy::platform_support`.

## Notes

- I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils`
as they might be candidates for micro-crating. They can always be moved
into `bevy_platform_support` at a later date if desired.
2025-01-23 16:46:08 +00:00
Zachary Harrold
04990fcd27
Move spin to bevy_platform_support out of other crates (#17470)
# Objective

- Contributes to #16877

## Solution

- Expanded `bevy_platform_support::sync` module to provide
API-compatible replacements for `std` items such as `RwLock`, `Mutex`,
and `OnceLock`.
- Removed `spin` from all crates except `bevy_platform_support`.

## Testing

- CI

---

## Notes

- The sync primitives, while verbose, entirely rely on `spin` for their
implementation requiring no `unsafe` and not changing the status-quo on
_how_ locks actually work within Bevy. This is just a refactoring to
consolidate the "hacks" and workarounds required to get a consistent
experience when either using `std::sync` or `spin`.
- I have opted to rely on `std::sync` for `std` compatible locks,
maintaining the status quo. However, now that we have these locks
factored out into the own module, it would be trivial to investigate
alternate locking backends, such as `parking_lot`.
- API for these locking types is entirely based on `std`. I have
implemented methods and types which aren't currently in use within Bevy
(e.g., `LazyLock` and `Once`) for the sake of completeness. As the
standard library is highly stable, I don't expect the Bevy and `std`
implementations to drift apart much if at all.

---------

Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
2025-01-23 05:27:02 +00:00
ickshonpe
dd2d84b342
Remove ViewVisibility from UI nodes (#17405)
# Objective

The UI can only target a single view and doesn't support `RenderLayers`,
so there doesn't seem to be any need for UI nodes to require
`ViewVisibility` and `VisibilityClass`.

Fixes #17400

## Solution

Remove the `ViewVisibility` and `VisibilityClass` component requires
from `Node` and change the visibility queries to only query for
`InheritedVisibility`.

## Testing

```cargo run --example many_buttons --release --features "trace_tracy"```

Yellow is this PR, red is main.

`bevy_render::view::visibility::reset_view_visibility`
<img width="531" alt="reset-view" src="https://github.com/user-attachments/assets/a44b215d-96bf-43ec-8669-31530ff98eae" />

`bevy_render::view::visibility::check_visibility`
<img width="445" alt="view_visibility" src="https://github.com/user-attachments/assets/fa111757-da91-434d-88e4-80bdfa29374f" />
2025-01-23 05:26:10 +00:00
Sven Niederberger
68c19defb6
Readback: Add support for texture depth/array layers (#17479)
# Objective

Fixes #16963

## Solution

I am - no pun intended - somewhat out of my depth here but this worked
in my testing. The validation error is gone and the data read from the
GPU looks sensible. I'd greatly appreciate if somebody more familiar
with the matter could double-check this.

## References

Relevant documentation in
[WebGPU](https://gpuweb.github.io/gpuweb/#gputexelcopybufferlayout) and
[wgpu](https://github.com/gfx-rs/wgpu/blob/v23/wgpu-types/src/lib.rs#L6350).

## Testing

<details><summary>Example code for testing</summary>
<p>

```rust
use bevy::{
    image::{self as bevy_image, TextureFormatPixelInfo},
    prelude::*,
    render::{
        render_asset::RenderAssetUsages,
        render_resource::{
            Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
        },
    },
};

fn main() {
    let mut app = App::new();
    app.add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, readback_system);
    app.run();
}

#[derive(Resource)]
struct ImageResource(Handle<Image>);

const TEXTURE_HEIGHT: u32 = 64;
const TEXTURE_WIDTH: u32 = 32;
const TEXTURE_LAYERS: u32 = 4;
const FORMAT: TextureFormat = TextureFormat::Rgba8Uint;

fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
    let layer_pixel_count = (TEXTURE_WIDTH * TEXTURE_HEIGHT) as usize;
    let layer_size = layer_pixel_count * FORMAT.pixel_size();
    let data: Vec<u8> = (0..TEXTURE_LAYERS as u8)
        .flat_map(|layer| (0..layer_size).map(move |_| layer))
        .collect();
    let image_size = data.len();
    println!("{image_size}");
    let image = Image {
        data,
        texture_descriptor: TextureDescriptor {
            label: Some("image"),
            size: Extent3d {
                width: TEXTURE_WIDTH,
                height: TEXTURE_HEIGHT,
                depth_or_array_layers: TEXTURE_LAYERS,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: TextureDimension::D2,
            format: FORMAT,
            usage: TextureUsages::COPY_DST | TextureUsages::COPY_SRC,
            view_formats: &[],
        },
        sampler: bevy_image::ImageSampler::Default,
        texture_view_descriptor: None,
        asset_usage: RenderAssetUsages::RENDER_WORLD,
    };

    commands.insert_resource(ImageResource(images.add(image)));
}

fn readback_system(
    mut commands: Commands,
    keys: Res<ButtonInput<KeyCode>>,
    image: Res<ImageResource>,
) {
    if !keys.just_pressed(KeyCode::KeyR) {
        return;
    }

    commands
        .spawn(bevy::render::gpu_readback::Readback::Texture(
            image.0.clone(),
        ))
        .observe(
            |trigger: Trigger<bevy::render::gpu_readback::ReadbackComplete>,
             mut commands: Commands| {
                info!("readback complete");

                println!("{:#?}", &trigger.0);

                commands.entity(trigger.observer()).despawn();
            },
        );
}

```

</p>
</details>
2025-01-23 05:25:40 +00:00
Patrick Walton
56aa90240e
Only include distance fog in the PBR shader if the view uses it. (#17495)
Right now, we always include distance fog in the shader, which is
unfortunate as it's complex code and is rare. This commit changes it to
be a `#define` instead. I haven't confirmed that removing distance fog
meaningfully reduces VGPR usage, but it can't hurt.
2025-01-23 05:24:54 +00:00
Rob Parrett
17294eebb2
Update async-broadcast (#17500)
# Objective

Dependabot tried up update this earlier, but it was noticed that this
broke wasm builds. A new release has happened since then which includes
a fix for that.

Here's the
[changelog](https://github.com/smol-rs/async-broadcast/blob/master/CHANGELOG.md).

Closes #11830

## Solution

Use `async-broadcast` `0.7.2`.

## Testing

I ran a few some examples involving assets on macos / wasm.
2025-01-23 05:24:34 +00:00
Zachary Harrold
8e6bf0637b
Add no_std support to bevy_diagnostic (#17507)
# Objective

- Contributes to #15460

## Solution

- Added required features
- Switched from `tracing` to `log`
- Fixed imports

## Testing

- CI
2025-01-23 05:20:34 +00:00
Tim Overbeek
da57dfb62f
DeriveWorld for enums (#17496)
# Objective

Fixes #17457 

## Solution

#[derive(FromWorld)] now works with enums by specifying which variant
should be used.

## Showcase

```rust
#[Derive(FromWorld)]
enum Game {
    #[from_world]
    Playing, 
    Stopped
}
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
2025-01-23 04:06:00 +00:00
Thierry Berger
fd2afeefda
Mesh::merge to return a Result (#17475)
# Objective

Make `Mesh::merge` more resilient to use.

Currently, it's difficult to make sure `Mesh::merge` will not panic
(we'd have to check if all attributes are compatible).

- I'd appreciate it for utility function to convert different mesh
representations such as:
https://github.com/dimforge/bevy_rapier/pull/628.

## Solution

- Make `Mesh::merge` return a `Result`.

## Testing

- It builds

## Migration Guide

- `Mesh::merge` now returns a `Result<(), MeshMergeError>`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
2025-01-23 04:05:36 +00:00
Zachary Harrold
9387fcfbf2
Add no_std Support to bevy_a11y (#17505)
# Objective

- Contributes to #15460

## Solution

- Add `std` feature gate
- Fixed partially used serialisation and reflection features.

## Testing

- CI
2025-01-23 03:52:47 +00:00
ickshonpe
434bbe6027
flex_basis doc comment fix (#17502)
# Objective

The doc comment for `Node::flex_basis` which refers to a`size` field
that was replaced by individual `width` and `height` fields sometime
ago.

## Solution

Refer to the individual fields instead.
2025-01-23 02:48:01 +00:00