Commit Graph

62 Commits

Author SHA1 Message Date
JMS55
b89387d820 Merge commit '2bddbdfd7c920d1ea61245dcdb7ff1c155e6b03b' into dlss3 2025-07-15 11:31:14 -04:00
Tim
4e9e78c31e
Split BufferedEvent from Event (#20101)
# Objective

> I think we should axe the shared `Event` trait entirely
It doesn't serve any functional purpose, and I don't think it's useful
pedagogically
@alice-i-cecile on discord

## Solution

- Remove `Event` as a supertrait of `BufferedEvent`
- Remove any `Event` derives that were made unnecessary
- Update release notes

---------

Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
2025-07-14 21:31:48 +00:00
atlv
e0383a0b96
Add zstd release note (#20053)
# Objective

- Add a release note for the new zstd backend.
- Doing so, I realized it was really cumbersome to enable this feature
because we default-enable ruzstd AND make it take precedence if both are
enabled. We can improve ux a bit by making the optional feature take
precedence when both are enabled. This still doesnt remove the unneeded
dependency, but oh well.

Note: it would be nice to have a way to make zstd_c not do anything when
building wasm, but im not sure theres a way to do that, as it seems like
it would need negative features.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-07-14 20:53:26 +00:00
atlv
1e057d8419
Add release note for light textures (#20023)
# Objective

- Fixes #19982

## Solution

- add note

## Testing

- left as an exercise for the reader
2025-07-14 20:47:45 +00:00
JMS55
84936cad55
Fix visibility (re)use in Solari DI (#20113)
# Objective
Fixes the re(use) of visibility in Solari's ReSTIR DI. 

The paper I based things off of didn't (seem) to use visibility in their
resampling
https://yusuketokuyoshi.com/papers/2024/Efficient_Visibility_Reuse_for_Real-time_ReSTIR_(Supplementary_Document).pdf,
only shading, but factoring it into the resampling improves things a
lot.

---

## Showcase
Before:
<img width="2564" height="1500" alt="image"
src="https://github.com/user-attachments/assets/15fa7941-ab68-47bc-9bbc-42ca55359046"
/>

After: 
<img width="2564" height="1500" alt="image"
src="https://github.com/user-attachments/assets/6fe52ed0-7832-41c1-b1cd-e8c8d9825e51"
/>
2025-07-13 19:33:09 +00:00
Talin
ace0114bdd
Changing the notification protocol for core_widgets. (#20086)
Notifications now include the source entity. This is useful for
callbacks that are responsible for more than one widget.

Part of #19236 

This is an incremental change only: I have not altered the fundamental
nature of callbacks, as this is still in discussion. The only change
here is to include the source entity id with the notification.

The existing examples don't leverage this new field, but that will
change when I work on the color sliders PR.

I have been careful not to use the word "events" in describing the
notification message structs because they are not capital-E `Events` at
this time. That may change depending on the outcome of discussions.

@alice-i-cecile
2025-07-13 17:25:11 +00:00
JMS55
e5aa94132c
Solari initial GI (#20020)
# Objective
- Add 1-bounce RT GI

## Solution

- Implement a very very basic version of ReSTIR GI
https://d1qx31qr3h6wln.cloudfront.net/publications/ReSTIR%20GI.pdf
- Pretty much a copy of the ReSTIR DI code, but adjusted for GI. 
- Didn't implement add more spatial samples, or do anything needed for
better quality.
- Didn't try to improve perf at all yet (it's actually faster than DI
though, unfortunately 😅)
- Didn't spend any time cleaning up the shared abstractions between
DI/GI

---

## Showcase
<img width="2564" height="1500" alt="image"
src="https://github.com/user-attachments/assets/1ff7be1c-7d7d-4e53-8aa6-bcec1553db3f"
/>
2025-07-13 17:23:38 +00:00
JMS55
d962a370a6 Release notes 2025-07-11 11:53:44 -04:00
JMS55
27f40ef4ba Merge commit '20dfae9a2d07038bda2921f82af50ded6151c3de' into dlss3 2025-07-11 11:37:26 -04:00
atlv
cfb679a752
Add a release note for scene types refactor (#20051)
Its really barebones but I'm not sure what else to write.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-07-11 07:34:06 +00:00
andriyDev
c9c8964857
Add release notes and a migration guide for RenderStartup. (#20024)
# Objective

- Document #19887 changes.

## Solution

- Just some writing for Mwriting Monday!

---------

Co-authored-by: atlv <email@atlasdostal.com>
2025-07-09 23:40:42 +00:00
Talin
f2d25355c3
SliderPrecision component (#20032)
This PR adds a `SliderPrecision` component that lets you control the
rounding when dragging a slider.

Part of #19236
2025-07-09 20:06:58 +00:00
JMS55
6f9114be60
Merge branch 'main' into dlss3 2025-07-09 11:48:00 -07:00
JMS55
e202226952
Update release-content/release-notes/dlss.md
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-07-09 11:42:19 -07:00
Daniel Skates
d45ae74286
Add frame_time graph to fps_overlay v2 (#19277)
# Objective

- Rebase of https://github.com/bevyengine/bevy/pull/12561 , note that
this is blocked on "up-streaming
[iyes_perf_ui](https://crates.io/crates/iyes_perf_ui)" , but that work
seems to also be stalled

> Frame time is often more important to know than FPS but because of the
temporal nature of it, just seeing a number is not enough. Seeing a
graph that shows the history makes it easier to reason about
performance.

## Solution

> This PR adds a bar graph of the frame time history.
> 
> Each bar is scaled based on the frame time where a bigger frame time
will give a taller and wider bar.
> 
> The color also scales with that frame time where red is at or bellow
the minimum target fps and green is at or above the target maximum frame
rate. Anything between those 2 values will be interpolated between green
and red based on the frame time.
> 
> The algorithm is highly inspired by this article:
https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times

## Testing

- Ran `cargo run --example fps_overlay --features="bevy_dev_tools"`

---------

Co-authored-by: IceSentry <c.giguere42@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-07-09 16:59:21 +00:00
JMS55
8084745c25
Merge branch 'main' into dlss3 2025-07-08 18:57:26 -07:00
JMS55
e2d8a9e08b Fix another link 2025-07-08 21:56:11 -04:00
JMS55
baa26682e9 Switch link to https://github.com/bevyengine/dlss_wgpu 2025-07-08 21:18:41 -04:00
Talin
aa87581cce
Change CoreWidgets plugin to plugin group. (#20036)
What it says on the tin. :)
2025-07-09 01:07:49 +00:00
JMS55
fda09d19c7 Fix typo 2025-07-08 11:56:50 -04:00
JMS55
04f488bc47 Update release notes 2025-07-07 22:28:51 -04:00
JMS55
1b714636ab Merge commit '5e3927ba489f597dd189f63286dc7985840db1b5' into dlss3 2025-07-07 21:40:01 -04:00
ickshonpe
5ea0e4004f
HSL and HSV interpolation for UI gradients (#19992)
# Objective

Add interpolation in HSL and HSV colour spaces for UI gradients.

## Solution
Added new variants to `InterpolationColorSpace`: `Hsl`, `HslLong`,
`Hsv`, and `HsvLong`, along with mix functions to the `gradients` shader
for each of them.

#### Limitations
* Didn't include increasing and decreasing path support, it's not
essential and can be done in a follow up if someone feels like it.

* The colour conversions should really be performed before the colours
are sent to the shader but it would need more changes and performance is
good enough for now.

## Testing

```cargo run --example gradients```
2025-07-07 20:08:51 +00:00
JMS55
cf1a1f7b7f Add note about DlssSupported 2025-07-06 18:07:16 -04:00
JMS55
6b960b0d7f Modify release notes 2025-07-06 18:03:36 -04:00
JMS55
bf56980080 Add release notes 2025-07-06 17:53:36 -04:00
Daniel Skates
560429ebd9
Observer trigger refactor (#19935)
# Objective

- The usage of ComponentId is quite confusing: events are not
components. By newtyping this, we can prevent stupid mistakes, avoid
leaking internal details and make the code clearer for users and engine
devs reading it.
- Adopts https://github.com/bevyengine/bevy/pull/19755

---------

Co-authored-by: oscar-benderstone <oscarbenderstone@gmail.com>
Co-authored-by: Oscar Bender-Stone <88625129+oscar-benderstone@users.noreply.github.com>
2025-07-04 16:27:21 +00:00
ickshonpe
b01de70bdd
bevy_ui_render crate (#18703)
# Objective

Move Bevy UI's rendering into a dedicated crate.

Motivations:
* Allow the UI renderer to be used with other UI frameworks than
`bevy_ui`.
* Allow for using alternative renderers like Vello with `bevy_ui`.
* It's difficult for rendering contributors to make changes and
improvements to the UI renderer as it requires in-depth knowledge of the
UI implementation.

## Solution

Move the `render` and `ui_material` modules from `bevy_ui` into a new
crate `bevy_ui_render`.

## Testing

Important examples to check are `testbed_ui`, `testbed_full_ui`,
`ui_material`, `viewport_node` and `gradients`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-07-03 23:36:35 +00:00
Talin
870490808d
Feathers toggle switches. (#19928)
# Objective

This is the Feathers toggle switch widget (without animation).

Part of #19236 

### Showcase

<img width="143" alt="toggles"
src="https://github.com/user-attachments/assets/c04afc06-5a57-4bc6-8181-99efbd1bebef"
/>
2025-07-03 01:09:31 +00:00
Talin
b980d4ac22
Feathers checkbox (#19900)
Adds checkbox and radio buttons to feathers.

Showcase:

<img width="378" alt="feathers-checkbox-radio"
src="https://github.com/user-attachments/assets/76d35589-6400-49dd-bf98-aeca2f39a472"
/>
2025-07-01 06:59:14 +00:00
Talin
7b6c5f4431
Change core widgets to use callback enum instead of option (#19855)
# Objective

Because we want to be able to support more notification options in the
future (in addition to just using registered one-shot systems), the
`Option<SystemId>` notifications have been changed to a new enum,
`Callback`.

@alice-i-cecile
2025-07-01 03:23:38 +00:00
Talin
9be1c36391
CoreScrollbar widget. (#19803)
# Objective

Part of #19236 


## Demo


![image](https://github.com/user-attachments/assets/8607f672-de8f-4339-bdfc-817b39f32e3e)


https://discord.com/channels/691052431525675048/743663673393938453/1387110701386039317

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-06-30 23:02:03 +00:00
JMS55
56710df934
bevy_solari ReSTIR DI (#19790)
# Objective

- Add temporal and spatial resampling to bevy_solari.

# Showcase
ReSTIR:

![image](https://github.com/user-attachments/assets/9b563713-d0cb-4f33-b402-dfa5a13ef3e2)

Previous RIS: 

![455750793-b70b968d-9c73-4983-9b6b-b60cace9b47a](https://github.com/user-attachments/assets/e62c852b-1f2c-4e86-ab35-a8058e9339d6)
2025-06-29 19:01:32 +00:00
Talin
65bddbd3e4
Bevy Feathers: an opinionated widget toolkit for building Bevy tooling (#19730)
# Objective

This PR introduces Bevy Feathers, an opinionated widget toolkit and
theming system intended for use by the Bevy Editor, World Inspector, and
other tools.

The `bevy_feathers` crate is incomplete and hidden behind an
experimental feature flag. The API is going to change significantly
before release.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-28 19:52:13 +00:00
Jan Hohenheim
fb2bbb043c
Nudge users into migrating to new default glTF coordinate conversion (#19816)
# Objective

*Step towards https://github.com/bevyengine/bevy/issues/19686*

We now have all the infrastructure in place to migrate Bevy's default
behavior when loading glTF files to respect their coordinate system.
Let's start migrating! For motivation, see the issue linked above

## Solution

- Introduce a feature flag called `gltf_convert_coordinates_default`
- Currently,`GltfPlugin::convert_coordinates` defaults to `false`
- If `gltf_convert_coordinates_default` is enabled,
`GltfPlugin::convert_coordinates` will default to `true`
- If `gltf_convert_coordinates_default` is not enabled *and*
`GltfPlugin::convert_coordinates` is false, we assume the user is
implicitly using the old behavior. Print a warning *once* in that case,
but only when a glTF was actually loaded
- A user can opt into the new behavior either
- Globally, by enabling `gltf_convert_coordinates_default` in their
`Cargo.toml`
  - Globally, by enabling `GltfPlugin::convert_coordinates`
  - Per asset, by enabling `GltfLoaderSettings::convert_coordinates`
- A user can explicitly opt out of the new behavior and silence the
warning by
- Enabling `gltf_convert_coordinates_default` in their `Cargo.toml` and
disabling `GltfPlugin::convert_coordinates`
- This PR also moves the existing release note into a migration guide
 
Note that I'm very open to change any features, mechanisms, warning
texts, etc. as needed :)

## Future Work

- This PR leaves all examples fully functional by not enabling this flag
internally yet. A followup PR will enable it as a `dev-dependency` and
migrate all of our examples involving glTFs to the new behavior.
- After 0.17 (and the RC before) lands, we'll gather feedback to see if
anything breaks or the suggested migration is inconvenient in some way
- If all goes well, we'll kill this flag and change the default of
`GltfPlugin::convert_coordinates` to `true` in 0.18


## Testing

- Ran examples with and without the flag

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Co-authored-by: AlephCubed <76791009+AlephCubed@users.noreply.github.com>
2025-06-28 18:35:41 +00:00
Talin
9f551bb1e2
Core radio button and radio group (#19778)
# Objective

Core Radio Button and Radio Group widgets. Part of #19236
2025-06-24 00:38:31 +00:00
Jan Hohenheim
f3d94f3958
Allow setting correct glTF coordinate conversions globally (#19685)
# Objective

- Followup to https://github.com/bevyengine/bevy/pull/19633
- As discussed, it's a bit cumbersome to specify that you want the
correct orientation every single time
- Also, glTFs loaded from third parties will still be loaded incorrectly

## Solution

- Allow opting into the new behavior globally or per-asset
- Also improved some docs while on it :)

## Testing

- Ran the animation examples
- Ran the test scene from the last PR with all configuration
combinations
2025-06-24 00:23:34 +00:00
Conner Petzold
3f187cf752
Add TilemapChunk rendering (#18866)
# Objective

An attempt to start building a base for first-party tilemaps (#13782).

The objective is to create a very simple tilemap chunk rendering plugin
that can be used as a building block for 3rd-party tilemap crates, and
eventually a first-party tilemap implementation.

## Solution

- Introduces two user-facing components, `TilemapChunk` and
`TilemapChunkIndices`, and a new material `TilemapChunkMaterial`.
- `TilemapChunk` holds the chunk and tile sizes, and the tileset image
- The tileset image is expected to be a layered image for use with
`texture_2d_array`, with the assumption that atlases or multiple images
would go through an asset loader/processor. Not sure if that should be
part of this PR or not..
- `TilemapChunkIndices` holds a 1d representation of all of the tile's
Option<u32> index into the tileset image.
- Indices are fixed to the size of tiles in a chunk (though maybe this
should just be an assertion instead?)
  - Indices are cloned and sent to the shader through a u32 texture.

## Testing

- Initial testing done with the `tilemap_chunk` example, though I need
to include some way to update indices as part of it.
- Tested wasm with webgl2 and webgpu
- I'm thinking it would probably be good to do some basic perf testing.

---

## Showcase

```rust
let chunk_size = UVec2::splat(64);
let tile_size = UVec2::splat(16);
let indices: Vec<Option<u32>> = (0..chunk_size.x * chunk_size.y)
    .map(|_| rng.gen_range(0..5))
    .map(|i| if i == 0 { None } else { Some(i - 1) })
    .collect();

commands.spawn((
    TilemapChunk {
        chunk_size,
        tile_size,
        tileset,
    },
    TilemapChunkIndices(indices),
));
```

![Screenshot 2025-04-17 at 11 54
56 PM](https://github.com/user-attachments/assets/850a53c1-16fc-405d-aad2-8ef5a0060fea)
2025-06-23 23:55:10 +00:00
Rob Parrett
d3ad66f033
Fix some typos (#19788)
# Objective

- Notice a word duplication typo
- Small quest to fix similar or nearby typos with my faithful companion
`\b(\w+)\s+\1\b`

## Solution

Fix em
2025-06-23 22:32:46 +00:00
ickshonpe
45a3f3d138
Color interpolation in OKLab, OKLCH spaces for UI gradients (#19330)
# Objective

Add support for interpolation in OKLab and OKLCH color spaces for UI
gradients.

## Solution
* New `InterpolationColorSpace` enum with `OkLab`, `OkLch`, `OkLchLong`,
`Srgb` and `LinearRgb` variants.
  * Added a color space specialization to the gradients pipeline.
* Added support for interpolation in OkLCH and OkLAB color spaces to the
gradients shader. OKLCH interpolation supports both short and long hue
paths. This is mostly based on the conversion functions from
`bevy_color` except that interpolation in polar space uses radians.
  * Added `color_space` fields to each gradient type.

## Testing

The `gradients` example has been updated to demonstrate the different
color interpolation methods.
Press space to cycle through the different options.

---

## Showcase


![color_spaces](https://github.com/user-attachments/assets/e10f8342-c3c8-487e-b386-7acdf38d638f)
2025-06-21 15:06:35 +00:00
Talin
9fdddf7089
Core Checkbox (#19665)
# Objective

This is part of the "core widgets" effort:
https://github.com/bevyengine/bevy/issues/19236.

## Solution

This adds the "core checkbox" widget type.

## Testing

Tested using examples core_widgets and core_widgets_observers.

Note to reviewers: I reorganized the code in the examples, so the diffs
are large because of code moves.
2025-06-20 16:37:18 +00:00
Kristoffer Søholm
2119838e27
Add support for ButtonInput<Key> (#19684)
# Objective

While `KeyCode` is very often the correct way to interact with keyboard
input there are a bunch of cases where it isn't, notably most of the
symbols (e.g. plus, minus, different parentheses). Currently the only
way to get these is to read from `EventReader<KeyboardInput>`, but then
you'd have to redo the `ButtonInput` logic for pressed/released to e.g.
make zoom functionality that depends on plus/minus keys.

This has led to confusion previously, like
https://github.com/bevyengine/bevy/issues/3278

## Solution

Add a `ButtonInput<Key>` resource.

## Testing

Modified the `keyboard_input` example to test it.

## Open questions

I'm not 100% sure this is the right way forward, since it duplicates the
key processing logic and might make people use the shorter
`ButtonInput<Key>` even when it's not appropriate.

Another option is to add a new struct with both `Key` and `KeyCode`, and
use `ButtonInput` with that instead. That would make it more
explanatory, but that is a lot of churn.

The third alternative is to not do this because it's too niche.

I'll add more documentation and take it out of draft if we want to move
forward with it.
2025-06-18 20:15:03 +00:00
Lucas Franca
6f08bb84d2
Exposes Observer's system's name (#19611)
# Objective

Fixes #18726
Alternative to and closes #18797

## Solution

Create a method `Observer::system_name` to expose the name of the
`Observer`'s system

## Showcase

```rust
// Returns `my_crate::my_observer`
let observer = Observer::new(my_observer);
println!(observer.system_name());

// Returns `my_crate::method::{{closure}}`
let observer = Observer::new(|_trigger: Trigger<...>|);
println!(observer.system_name());

// Returns `custom_name`
let observer = Observer::new(IntoSystem::into_system(my_observer).with_name("custom_name"));
println!(observer.system_name());
```

## TODO
- [ ] Achieve cart's approval

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-17 22:38:31 +00:00
Jan Hohenheim
9b743d2a43
Allow users to fix glTF coordinate system imports (#19633)
# Objective

*Fixes #5670 as an opt-in for now*

glTF uses the following coordinate system:

- forward: Z
- up: Y
- right: -X

and Bevy uses:

- forward: -Z
- up: Y
- right: X

For the longest time, Bevy has simply ignored this distinction. That
caused issues when working across programs, as most software respects
the
glTF coordinate system when importing and exporting glTFs. Your scene
might have looked correct in Blender, Maya, TrenchBroom, etc. but
everything would be flipped when importing it into Bevy!

## Solution

Add an option to the glTF loader to perform coordinate conversion. Note
that this makes a distinction in the camera nodes, as glTF uses a
different coordinate system for them.

## Follow Ups

- Add global glTF loader settings, similar to the image loader, so that
users can make third-party crates also load their glTFs with corrected
coordinates
- Decide on a migration strategy to make this the future default
  - Create an issue
- Get feedback from Patrick Walton and Cart (not pinging them here to
not spam them)
  - Include this pic for reference of how Blender assumes -Y as forward:

![image](https://github.com/user-attachments/assets/8f5ae364-48f0-46e4-922b-50bccb8d58b3)

## Testing

I ran all glTF animation examples with the new setting enabled to
validate that they look the same, just flipped.

Also got a nice test scene from Chris that includes a camera inside the
glTF. Thanks @ChristopherBiscardi!

Blender (-Y forward): 

![image](https://github.com/user-attachments/assets/129013f1-a025-488a-8764-c7ee5e7019a1)

Bevy (-Z forward, but the model looks the wrong way):

![image](https://github.com/user-attachments/assets/842e00e0-48ce-4ca7-a88e-ea458ecbf852)

Bevy with `convert_coordinates` enabled (-Z forward):

![image](https://github.com/user-attachments/assets/e97f3797-75a0-4d2b-ac54-130ba69f0a3c)

Validation that the axes are correct with F3D's glTF viewer (+Z
forward):

![image](https://github.com/user-attachments/assets/b9f02adf-a7b0-4a18-821f-fdd04426d3bd)
2025-06-16 21:47:34 +00:00
Alice Cecile
b7d2cb8547
Provide access to the original target of entity-events in observers (#19663)
# Objective

Getting access to the original target of an entity-event is really
helpful when working with bubbled / propagated events.

`bevy_picking` special-cases this, but users have requested this for all
sorts of bubbled events.

The existing naming convention was also very confusing. Fixes
https://github.com/bevyengine/bevy/issues/17112, but also see #18982.

## Solution

1. Rename `ObserverTrigger::target` -> `current_target`.
1. Store `original_target: Option<Entity>` in `ObserverTrigger`.
1. Wire it up so this field gets set correctly.
1. Remove the `target` field on the `Pointer` events from
`bevy_picking`.

Closes https://github.com/bevyengine/bevy/pull/18710, which attempted
the same thing. Thanks @emfax!

## Testing

I've modified an existing test to check that the entities returned
during event bubbling / propagation are correct.

## Notes to reviewers

It's a little weird / sad that you can no longer access this infromation
via the buffered events for `Pointer`. That said, you already couldn't
access any bubbled target. We should probably remove the `BufferedEvent`
form of `Pointer` to reduce confusion and overhead, but I didn't want to
do so here.

Observer events can be trivially converted into buffered events (write
an observer with an EventWriter), and I suspect that that is the better
migration if you want the controllable timing or performance
characteristics of buffered events for your specific use case.

## Future work

It would be nice to not store this data at all (and not expose any
methods) if propagation was disabled. That involves more trait
shuffling, and I don't think we should do it here for reviewability.

---------

Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
2025-06-15 20:53:25 +00:00
Joona Aalto
38c3423693
Event Split: Event, EntityEvent, and BufferedEvent (#19647)
# Objective

Closes #19564.

The current `Event` trait looks like this:

```rust
pub trait Event: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

The `Event` trait is used by both buffered events
(`EventReader`/`EventWriter`) and observer events. If they are observer
events, they can optionally be targeted at specific `Entity`s or
`ComponentId`s, and can even be propagated to other entities.

However, there has long been a desire to split the trait semantically
for a variety of reasons, see #14843, #14272, and #16031 for discussion.
Some reasons include:

- It's very uncommon to use a single event type as both a buffered event
and targeted observer event. They are used differently and tend to have
distinct semantics.
- A common footgun is using buffered events with observers or event
readers with observer events, as there is no type-level error that
prevents this kind of misuse.
- #19440 made `Trigger::target` return an `Option<Entity>`. This
*seriously* hurts ergonomics for the general case of entity observers,
as you need to `.unwrap()` each time. If we could statically determine
whether the event is expected to have an entity target, this would be
unnecessary.

There's really two main ways that we can categorize events: push vs.
pull (i.e. "observer event" vs. "buffered event") and global vs.
targeted:

|              | Push            | Pull                        |
| ------------ | --------------- | --------------------------- |
| **Global**   | Global observer | `EventReader`/`EventWriter` |
| **Targeted** | Entity observer | -                           |

There are many ways to approach this, each with their tradeoffs.
Ultimately, we kind of want to split events both ways:

- A type-level distinction between observer events and buffered events,
to prevent people from using the wrong kind of event in APIs
- A statically designated entity target for observer events to avoid
accidentally using untargeted events for targeted APIs

This PR achieves these goals by splitting event traits into `Event`,
`EntityEvent`, and `BufferedEvent`, with `Event` being the shared trait
implemented by all events.

## `Event`, `EntityEvent`, and `BufferedEvent`

`Event` is now a very simple trait shared by all events.

```rust
pub trait Event: Send + Sync + 'static {
    // Required for observer APIs
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

You can call `trigger` for *any* event, and use a global observer for
listening to the event.

```rust
#[derive(Event)]
struct Speak {
    message: String,
}

// ...

app.add_observer(|trigger: On<Speak>| {
    println!("{}", trigger.message);
});

// ...

commands.trigger(Speak {
    message: "Y'all like these reworked events?".to_string(),
});
```

To allow an event to be targeted at entities and even propagated
further, you can additionally implement the `EntityEvent` trait:

```rust
pub trait EntityEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This lets you call `trigger_targets`, and to use targeted observer APIs
like `EntityCommands::observe`:

```rust
#[derive(Event, EntityEvent)]
#[entity_event(traversal = &'static ChildOf, auto_propagate)]
struct Damage {
    amount: f32,
}

// ...

let enemy = commands.spawn((Enemy, Health(100.0))).id();

// Spawn some armor as a child of the enemy entity.
// When the armor takes damage, it will bubble the event up to the enemy.
let armor_piece = commands
    .spawn((ArmorPiece, Health(25.0), ChildOf(enemy)))
    .observe(|trigger: On<Damage>, mut query: Query<&mut Health>| {
        // Note: `On::target` only exists because this is an `EntityEvent`.
        let mut health = query.get(trigger.target()).unwrap();
        health.0 -= trigger.amount();
    });

commands.trigger_targets(Damage { amount: 10.0 }, armor_piece);
```

> [!NOTE]
> You *can* still also trigger an `EntityEvent` without targets using
`trigger`. We probably *could* make this an either-or thing, but I'm not
sure that's actually desirable.

To allow an event to be used with the buffered API, you can implement
`BufferedEvent`:

```rust
pub trait BufferedEvent: Event {}
```

The event can then be used with `EventReader`/`EventWriter`:

```rust
#[derive(Event, BufferedEvent)]
struct Message(String);

fn write_hello(mut writer: EventWriter<Message>) {
    writer.write(Message("I hope these examples are alright".to_string()));
}

fn read_messages(mut reader: EventReader<Message>) {
    // Process all buffered events of type `Message`.
    for Message(message) in reader.read() {
        println!("{message}");
    }
}
```

In summary:

- Need a basic event you can trigger and observe? Derive `Event`!
- Need the event to be targeted at an entity? Derive `EntityEvent`!
- Need the event to be buffered and support the
`EventReader`/`EventWriter` API? Derive `BufferedEvent`!

## Alternatives

I'll now cover some of the alternative approaches I have considered and
briefly explored. I made this section collapsible since it ended up
being quite long :P

<details>

<summary>Expand this to see alternatives</summary>

### 1. Unified `Event` Trait

One option is not to have *three* separate traits (`Event`,
`EntityEvent`, `BufferedEvent`), and to instead just use associated
constants on `Event` to determine whether an event supports targeting
and buffering or not:

```rust
pub trait Event: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    const TARGETED: bool = false;
    const BUFFERED: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

Methods can then use bounds like `where E: Event<TARGETED = true>` or
`where E: Event<BUFFERED = true>` to limit APIs to specific kinds of
events.

This would keep everything under one `Event` trait, but I don't think
it's necessarily a good idea. It makes APIs harder to read, and docs
can't easily refer to specific types of events. You can also create
weird invariants: what if you specify `TARGETED = false`, but have
`Traversal` and/or `AUTO_PROPAGATE` enabled?

### 2. `Event` and `Trigger`

Another option is to only split the traits between buffered events and
observer events, since that is the main thing people have been asking
for, and they have the largest API difference.

If we did this, I think we would need to make the terms *clearly*
separate. We can't really use `Event` and `BufferedEvent` as the names,
since it would be strange that `BufferedEvent` doesn't implement
`Event`. Something like `ObserverEvent` and `BufferedEvent` could work,
but it'd be more verbose.

For this approach, I would instead keep `Event` for the current
`EventReader`/`EventWriter` API, and call the observer event a
`Trigger`, since the "trigger" terminology is already used in the
observer context within Bevy (both as a noun and a verb). This is also
what a long [bikeshed on
Discord](https://discord.com/channels/691052431525675048/749335865876021248/1298057661878898791)
seemed to land on at the end of last year.

```rust
// For `EventReader`/`EventWriter`
pub trait Event: Send + Sync + 'static {}

// For observers
pub trait Trigger: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    const TARGETED: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

The problem is that "event" is just a really good term for something
that "happens". Observers are rapidly becoming the more prominent API,
so it'd be weird to give them the `Trigger` name and leave the good
`Event` name for the less common API.

So, even though a split like this seems neat on the surface, I think it
ultimately wouldn't really work. We want to keep the `Event` name for
observer events, and there is no good alternative for the buffered
variant. (`Message` was suggested, but saying stuff like "sends a
collision message" is weird.)

### 3. `GlobalEvent` + `TargetedEvent`

What if instead of focusing on the buffered vs. observed split, we
*only* make a distinction between global and targeted events?

```rust
// A shared event trait to allow global observers to work
pub trait Event: Send + Sync + 'static {
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}

// For buffered events and non-targeted observer events
pub trait GlobalEvent: Event {}

// For targeted observer events
pub trait TargetedEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This is actually the first approach I implemented, and it has the neat
characteristic that you can only use non-targeted APIs like `trigger`
with a `GlobalEvent` and targeted APIs like `trigger_targets` with a
`TargetedEvent`. You have full control over whether the entity should or
should not have a target, as they are fully distinct at the type-level.

However, there's a few problems:

- There is no type-level indication of whether a `GlobalEvent` supports
buffered events or just non-targeted observer events
- An `Event` on its own does literally nothing, it's just a shared trait
required to make global observers accept both non-targeted and targeted
events
- If an event is both a `GlobalEvent` and `TargetedEvent`, global
observers again have ambiguity on whether an event has a target or not,
undermining some of the benefits
- The names are not ideal

### 4. `Event` and `EntityEvent`

We can fix some of the problems of Alternative 3 by accepting that
targeted events can also be used in non-targeted contexts, and simply
having the `Event` and `EntityEvent` traits:

```rust
// For buffered events and non-targeted observer events
pub trait Event: Send + Sync + 'static {
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}

// For targeted observer events
pub trait EntityEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This is essentially identical to this PR, just without a dedicated
`BufferedEvent`. The remaining major "problem" is that there is still
zero type-level indication of whether an `Event` event *actually*
supports the buffered API. This leads us to the solution proposed in
this PR, using `Event`, `EntityEvent`, and `BufferedEvent`.

</details>

## Conclusion

The `Event` + `EntityEvent` + `BufferedEvent` split proposed in this PR
aims to solve all the common problems with Bevy's current event model
while keeping the "weirdness" factor minimal. It splits in terms of both
the push vs. pull *and* global vs. targeted aspects, while maintaining a
shared concept for an "event".

### Why I Like This

- The term "event" remains as a single concept for all the different
kinds of events in Bevy.
- Despite all event types being "events", they use fundamentally
different APIs. Instead of assuming that you can use an event type with
any pattern (when only one is typically supported), you explicitly opt
in to each one with dedicated traits.
- Using separate traits for each type of event helps with documentation
and clearer function signatures.
- I can safely make assumptions on expected usage.
- If I see that an event is an `EntityEvent`, I can assume that I can
use `observe` on it and get targeted events.
- If I see that an event is a `BufferedEvent`, I can assume that I can
use `EventReader` to read events.
- If I see both `EntityEvent` and `BufferedEvent`, I can assume that
both APIs are supported.

In summary: This allows for a unified concept for events, while limiting
the different ways to use them with opt-in traits. No more guess-work
involved when using APIs.

### Problems?

- Because `BufferedEvent` implements `Event` (for more consistent
semantics etc.), you can still use all buffered events for non-targeted
observers. I think this is fine/good. The important part is that if you
see that an event implements `BufferedEvent`, you know that the
`EventReader`/`EventWriter` API should be supported. Whether it *also*
supports other APIs is secondary.
- I currently only support `trigger_targets` for an `EntityEvent`.
However, you can technically target components too, without targeting
any entities. I consider that such a niche and advanced use case that
it's not a huge problem to only support it for `EntityEvent`s, but we
could also split `trigger_targets` into `trigger_entities` and
`trigger_components` if we wanted to (or implement components as
entities :P).
- You can still trigger an `EntityEvent` *without* targets. I consider
this correct, since `Event` implements the non-targeted behavior, and
it'd be weird if implementing another trait *removed* behavior. However,
it does mean that global observers for entity events can technically
return `Entity::PLACEHOLDER` again (since I got rid of the
`Option<Entity>` added in #19440 for ergonomics). I think that's enough
of an edge case that it's not a huge problem, but it is worth keeping in
mind.
- ~~Deriving both `EntityEvent` and `BufferedEvent` for the same type
currently duplicates the `Event` implementation, so you instead need to
manually implement one of them.~~ Changed to always requiring `Event` to
be derived.

## Related Work

There are plans to implement multi-event support for observers,
especially for UI contexts. [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)
API looked like this:

```rust
// Truncated for brevity
trigger: Trigger<(
    OnAdd<Pressed>,
    OnRemove<Pressed>,
    OnAdd<InteractionDisabled>,
    OnRemove<InteractionDisabled>,
    OnInsert<Hovered>,
)>,
```

I believe this shouldn't be in conflict with this PR. If anything, this
PR might *help* achieve the multi-event pattern for entity observers
with fewer footguns: by statically enforcing that all of these events
are `EntityEvent`s in the context of `EntityCommands::observe`, we can
avoid misuse or weird cases where *some* events inside the trigger are
targeted while others are not.
2025-06-15 16:46:34 +00:00
Talin
30aa36eaf4
Core slider (#19584)
# Objective

This is part of the "core widgets" effort: #19236. 

## Solution

This PR adds the "core slider" widget to the collection.

## Testing

Tested using examples `core_widgets` and `core_widgets_observers`.

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-06-15 00:53:31 +00:00
JMS55
bab31e3777
Initial raytraced lighting progress (bevy_solari) (#19058)
# Bevy Solari 
<img
src="https://github.com/user-attachments/assets/94061fc8-01cf-4208-b72a-8eecad610d76"
width="100" />

## Preface
- See release notes.
- Please talk to me in #rendering-dev on discord or open a github
discussion if you have questions about the long term plan, and keep
discussion in this PR limited to the contents of the PR :)

## Connections
- Works towards #639, #16408.
- Spawned https://github.com/bevyengine/bevy/issues/18993.
- Need to fix RT stuff in naga_oil first
https://github.com/bevyengine/naga_oil/pull/116.

## This PR

After nearly two years, I've revived the raytraced lighting effort I
first started in https://github.com/bevyengine/bevy/pull/10000.

Unlike that PR, which has realtime techniques, I've limited this PR to:
* `RaytracingScenePlugin` - BLAS and TLAS building, geometry and texture
binding, sampling functions.
* `PathtracingPlugin` - A non-realtime path tracer intended to serve as
a testbed and reference.

## What's implemented?

![image](https://github.com/user-attachments/assets/06522007-c205-46eb-8178-823f19917def)

* BLAS building on mesh load
* Emissive lights
* Directional lights with soft shadows
* Diffuse (lambert, not Bevy's diffuse BRDF) and emissive materials
* A reference path tracer with:
  * Antialiasing
  * Direct light sampling (next event estimation) with 0/1 MIS weights
  * Importance-sampled BRDF bounces
  * Russian roulette 

## What's _not_ implemented?
* Anything realtime, including a real-time denoiser
* Integration with Bevy's rasterized gbuffer
* Specular materials
* Non-opaque geometry
* Any sort of CPU or GPU optimizations
* BLAS compaction, proper bindless, and further RT APIs are things that
we need wgpu to add
* PointLights, SpotLights, or skyboxes / environment lighting 
* Support for materials other than StandardMaterial (and only a subset
of properties are supported)
* Skinned/morphed or otherwise animating/deformed meshes
* Mipmaps
* Adaptive self-intersection ray bias
* A good way for developers to detect whether the user's GPU supports RT
or not, and fallback to baked lighting.
* Documentation and actual finalized APIs (literally everything is
subject to change)

## End-user Usage
* Have a GPU that supports RT with inline ray queries
* Add `SolariPlugin` to your app
* Ensure any `Mesh` asset you want to use for raytracing has
`enable_raytracing: true` (defaults to true), and that it uses the
standard uncompressed position/normal/uv_0/tangent vertex attribute set,
triangle list topology, and 32-bit indices.
* If you don't want to build a BLAS and use the mesh for RT, set
enable_raytracing to false.
* Add the `RaytracingMesh3d` component to your entity (separate from
`Mesh3d` or `MeshletMesh3d`).

## Testing

- Did you test these changes? If so, how? 
  - Ran the solari example.
- Are there any parts that need more testing?
  - Other test scenes probably. Normal mapping would be good to test.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - See the solari.rs example for how to setup raytracing.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - Windows 11, NVIDIA RTX 3080.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-06-12 21:26:10 +00:00
Joona Aalto
e5dc177b4b
Rename Trigger to On (#19596)
# Objective

Currently, the observer API looks like this:

```rust
app.add_observer(|trigger: Trigger<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

Future plans for observers also include "multi-event observers" with a
trigger that looks like this (see [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)):

```rust
trigger: Trigger<(
    OnAdd<Pressed>,
    OnRemove<Pressed>,
    OnAdd<InteractionDisabled>,
    OnRemove<InteractionDisabled>,
    OnInsert<Hovered>,
)>,
```

In scenarios like this, there is a lot of repetition of `On`. These are
expected to be very high-traffic APIs especially in UI contexts, so
ergonomics and readability are critical.

By renaming `Trigger` to `On`, we can make these APIs read more cleanly
and get rid of the repetition:

```rust
app.add_observer(|trigger: On<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

```rust
trigger: On<(
    Add<Pressed>,
    Remove<Pressed>,
    Add<InteractionDisabled>,
    Remove<InteractionDisabled>,
    Insert<Hovered>,
)>,
```

Names like `On<Add<Pressed>>` emphasize the actual event listener nature
more than `Trigger<OnAdd<Pressed>>`, and look cleaner. This *also* frees
up the `Trigger` name if we want to use it for the observer event type,
splitting them out from buffered events (bikeshedding this is out of
scope for this PR though).

For prior art:
[`bevy_eventlistener`](https://github.com/aevyrie/bevy_eventlistener)
used
[`On`](https://docs.rs/bevy_eventlistener/latest/bevy_eventlistener/event_listener/struct.On.html)
for its event listener type. Though in our case, the observer is the
event listener, and `On` is just a type containing information about the
triggered event.

## Solution

Steal from `bevy_event_listener` by @aevyrie and use `On`.

- Rename `Trigger` to `On`
- Rename `OnAdd` to `Add`
- Rename `OnInsert` to `Insert`
- Rename `OnReplace` to `Replace`
- Rename `OnRemove` to `Remove`
- Rename `OnDespawn` to `Despawn`

## Discussion

### Naming Conflicts??

Using a name like `Add` might initially feel like a very bad idea, since
it risks conflict with `core::ops::Add`. However, I don't expect this to
be a big problem in practice.

- You rarely need to actually implement the `Add` trait, especially in
modules that would use the Bevy ECS.
- In the rare cases where you *do* get a conflict, it is very easy to
fix by just disambiguating, for example using `ops::Add`.
- The `Add` event is a struct while the `Add` trait is a trait (duh), so
the compiler error should be very obvious.

For the record, renaming `OnAdd` to `Add`, I got exactly *zero* errors
or conflicts within Bevy itself. But this is of course not entirely
representative of actual projects *using* Bevy.

You might then wonder, why not use `Added`? This would conflict with the
`Added` query filter, so it wouldn't work. Additionally, the current
naming convention for observer events does not use past tense.

### Documentation

This does make documentation slightly more awkward when referring to
`On` or its methods. Previous docs often referred to `Trigger::target`
or "sends a `Trigger`" (which is... a bit strange anyway), which would
now be `On::target` and "sends an observer `Event`".

You can see the diff in this PR to see some of the effects. I think it
should be fine though, we may just need to reword more documentation to
read better.
2025-06-12 18:22:33 +00:00
Joona Aalto
33c6f45a35
Rename some pointer events and components (#19574)
# Objective

#19366 implemented core button widgets, which included the `Depressed`
state component.

`Depressed` was chosen instead of `Pressed` to avoid conflict with the
`Pointer<Pressed>` event, but it is problematic and awkward in many
ways:

- Using the word "depressed" for such a high-traffic type is not great
due to the obvious connection to "depressed" as in depression.
- "Depressed" is not what I would search for if I was looking for a
component like this, and I'm not aware of any other engine or UI
framework using the term.
- `Depressed` is not a very natural pair to the `Pointer<Pressed>`
event.
- It might be because I'm not a native English speaker, but I have very
rarely heard someone say "a button is depressed". Seeing it, my mind
initially goes from "depression??" to "oh, de-pressed, meaning released"
and definitely not "is pressed", even though that *is* also a valid
meaning for it.

A related problem is that the current `Pointer<Pressed>` and
`Pointer<Released>` event names use a different verb tense than all of
our other observer events such as `Pointer<Click>` or
`Pointer<DragStart>`. By fixing this and renaming `Pressed` (and
`Released`), we can then use `Pressed` instead of `Depressed` for the
state component.

Additionally, the `IsHovered` and `IsDirectlyHovered` components added
in #19366 use an inconsistent naming; the other similar components don't
use an `Is` prefix. It also makes query filters like `Has<IsHovered>`
and `With<IsHovered>` a bit more awkward.

This is partially related to Cart's [picking concept
proposal](https://gist.github.com/cart/756e48a149db2838028be600defbd24a?permalink_comment_id=5598154).

## Solution

- Rename `Pointer<Pressed>` to `Pointer<Press>`
- Rename `Pointer<Released>` to `Pointer<Release>`
- Rename `Depressed` to `Pressed`
- Rename `IsHovered` to `Hovered`
- Rename `IsDirectlyHovered` to `DirectlyHovered`
2025-06-10 21:57:28 +00:00