# Objective
- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).
## Solution
- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.
---
## Changelog
- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.
## Migration Guide
- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.
## Future Work
- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](8e3488c880/crates/bevy_reflect/src/reflect.rs (L337-L342))
and make `FromReflect` a requirement to improve [`FromReflect`
ergonomics](https://github.com/bevyengine/rfcs/pull/59). This is
currently not possible because dynamic types cannot sensibly be
`FromReflect`.
- Since this is an alternative to #5772, #5781 would be made cleaner.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective
- Add custom images as cursors
- Fixes#9557
## Solution
- Change cursor type to accommodate both native and image cursors
- I don't really like this solution because I couldn't use
`Handle<Image>` directly. I would need to import `bevy_assets` and that
causes a circular dependency. Alternatively we could use winit's
`CustomCursor` smart pointers, but that seems hard because the event
loop is needed to create those and is not easily accessable for users.
So now I need to copy around rgba buffers which is sad.
- I use a cache because especially on the web creating cursor images is
really slow
- Sorry to #14196 for yoinking, I just wanted to make a quick solution
for myself and thought that I should probably share it too.
Update:
- Now uses `Handle<Image>`, reads rgba data in `bevy_render` and uses
resources to send the data to `bevy_winit`, where the final cursors are
created.
## Testing
- Added example which works fine at least on Linux Wayland (winit side
has been tested with all platforms).
- I haven't tested if the url cursor works.
## Migration Guide
- `CursorIcon` is no longer a field in `Window`, but a separate
component can be inserted to a window entity. It has been changed to an
enum that can hold custom images in addition to system icons.
- `Cursor` is renamed to `CursorOptions` and `cursor` field of `Window`
is renamed to `cursor_options`
- `CursorIcon` is renamed to `SystemCursorIcon`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
# Objective
The `dynamic_types` example was missing a reference to the newly added
`DynamicSet` type.
## Solution
Add `DynamicSet` to the `dynamic_types` example.
For parity with the other dynamic types, I also implemented
`FromIterator<T: Reflect>`, `FromIterator<Box<dyn Reflect>>`, and
`IntoIterator for &DynamicSet`.
## Testing
You can run the example locally:
```
cargo run --example dynamic_types
```
# Objective
### TL;DR
#14098 added the `FunctionRegistry` but had some last minute
complications due to anonymous functions. It ended up going with a
"required name" approach to ensure anonymous functions would always have
a name.
However, this approach isn't ideal for named functions since, by
definition, they will always have a name.
Therefore, this PR aims to modify function reflection such that we can
make function registration easier for named functions, while still
allowing anonymous functions to be registered as well.
### Context
Function registration (#14098) ran into a little problem: anonymous
functions.
Anonymous functions, including function pointers, have very non-unique
type names. For example, the anonymous function `|a: i32, b: i32| a + b`
has the type name of `fn(i32, i32) -> i32`. This obviously means we'd
conflict with another function like `|a: i32, b: i32| a - b`.
The solution that #14098 landed on was to always require a name during
function registration.
The downside with this is that named functions (e.g. `fn add(a: i32, b:
i32) -> i32 { a + b }`) had to redundantly provide a name. Additionally,
manually constructed `DynamicFunction`s also ran into this ergonomics
issue.
I don't entirely know how the function registry will be used, but I have
a strong suspicion that most of its registrations will either be named
functions or manually constructed `DynamicFunction`s, with anonymous
functions only being used here and there for quick prototyping or adding
small functionality.
Why then should the API prioritize the anonymous function use case by
always requiring a name during registration?
#### Telling Functions Apart
Rust doesn't provide a lot of out-of-the-box tools for reflecting
functions. One of the biggest hurdles in attempting to solve the problem
outlined above would be to somehow tell the different kinds of functions
apart.
Let's briefly recap on the categories of functions in Rust:
| Category | Example |
| ------------------ | ----------------------------------------- |
| Named function | `fn add(a: i32, b: i32) -> i32 { a + b }` |
| Closure | `\|a: i32\| a + captured_variable` |
| Anonymous function | `\|a: i32, b: i32\| a + b` |
| Function pointer | `fn(i32, i32) -> i32` |
My first thought was to try and differentiate these categories based on
their size. However, we can see that this doesn't quite work:
| Category | `size_of` |
| ------------------ | --------- |
| Named function | 0 |
| Closure | 0+ |
| Anonymous function | 0 |
| Function pointer | 8 |
Not only does this not tell anonymous functions from named ones, but it
struggles with pretty much all of them.
My second then was to differentiate based on type name:
| Category | `type_name` |
| ------------------ | ----------------------- |
| Named function | `foo::bar::baz` |
| Closure | `foo::bar::{{closure}}` |
| Anonymous function | `fn() -> String` |
| Function pointer | `fn() -> String` |
This is much better. While it can't distinguish between function
pointers and anonymous functions, this doesn't matter too much since we
only care about whether we can _name_ the function.
So why didn't we implement this in #14098?
#### Relying on `type_name`
While this solution was known about while working on #14098, it was left
out from that PR due to it being potentially controversial.
The [docs](https://doc.rust-lang.org/stable/std/any/fn.type_name.html)
for `std::any::type_name` state:
> The returned string must not be considered to be a unique identifier
of a type as multiple types may map to the same type name. Similarly,
there is no guarantee that all parts of a type will appear in the
returned string: for example, lifetime specifiers are currently not
included. In addition, the output may change between versions of the
compiler.
So that's it then? We can't use `type_name`?
Well, this statement isn't so much a rule as it is a guideline. And Bevy
is no stranger to bending the rules to make things work or to improve
ergonomics. Remember that before `TypePath`, Bevy's scene system was
entirely dependent on `type_name`. Not to mention that `type_name` is
being used as a key into both the `TypeRegistry` and the
`FunctionRegistry`.
Bevy's practices aside, can we reliably use `type_name` for this?
My answer would be "yes".
Anonymous functions are anonymous. They have no name. There's nothing
Rust could do to give them a name apart from generating a random string
of characters. But remember that this is a diagnostic tool, it doesn't
make sense to obfuscate the type by randomizing the output. So changing
it to be anything other than what it is now is very unlikely.
The only changes that I could potentially see happening are:
1. Closures replace `{{closure}}` with the name of their variable
2. Lifetimes are included in the output
I don't think the first is likely to happen, but if it does then it
actually works out in our favor: closures are now named!
The second point is probably the likeliest. However, adding lifetimes
doesn't mean we can't still rely on `type_name` to determine whether or
not a function is named. So we should be okay in this case as well.
## Solution
Parse the `type_name` of the function in the `TypedFunction` impl to
determine if the function is named or anonymous.
This once again makes `FunctionInfo::name` optional. For manual
constructions of `DynamicFunction`, `FunctionInfo::named` or
``FunctionInfo::anonymous` can be used.
The `FunctionRegistry` API has also been reworked to account for this
change.
`FunctionRegistry::register` no longer takes a name and instead takes it
from the supplied function, returning a
`FunctionRegistrationError::MissingName` error if the name is `None`.
This also doubles as a replacement for the old
`FunctionRegistry::register_dynamic` method, which has been removed.
To handle anonymous functions, a `FunctionRegistry::register_with_name`
method has been added. This works in the same way
`FunctionRegistry::register` used to work before this PR.
The overwriting methods have been updated in a similar manner, with
modifications to `FunctionRegistry::overwrite_registration`, the removal
of `FunctionRegistry::overwrite_registration_dynamic`, and the addition
of `FunctionRegistry::overwrite_registration_with_name`.
This PR also updates the methods on `App` in a similar way:
`App::register_function` no longer requires a name argument and
`App::register_function_with_name` has been added to handle anonymous
functions (and eventually closures).
## Testing
You can run the tests locally by running:
```
cargo test --package bevy_reflect --features functions
```
---
## Internal Migration Guide
> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.
> [!note]
> This list is not exhaustive. It only contains some of the most
important changes.
`FunctionRegistry::register` no longer requires a name string for named
functions. Anonymous functions, however, need to be registered using
`FunctionRegistry::register_with_name`.
```rust
// BEFORE
registry
.register(std::any::type_name_of_val(&foo), foo)?
.register("bar", || println!("Hello world!"));
// AFTER
registry
.register(foo)?
.register_with_name("bar", || println!("Hello world!"));
```
`FunctionInfo::name` is now optional. Anonymous functions and closures
will now have their name set to `None` by default. Additionally,
`FunctionInfo::new` has been renamed to `FunctionInfo::named`.
This PR is based on top of #12982
# Objective
- Mesh2d currently only has an alpha blended phase. Most sprites don't
need transparency though.
- For some 2d games it can be useful to have a 2d depth buffer
## Solution
- Add an opaque phase to render Mesh2d that don't need transparency
- This phase currently uses the `SortedRenderPhase` to make it easier to
implement based on the already existing transparent phase. A follow up
PR will switch this to `BinnedRenderPhase`.
- Add a 2d depth buffer
- Use that depth buffer in the transparent phase to make sure that
sprites and transparent mesh2d are displayed correctly
## Testing
I added the mesh2d_transforms example that layers many opaque and
transparent mesh2d to make sure they all get displayed correctly. I also
confirmed it works with sprites by modifying that example locally.
---
## Changelog
- Added `AlphaMode2d`
- Added `Opaque2d` render phase
- Camera2d now have a `ViewDepthTexture` component
## Migration Guide
- `ColorMaterial` now contains `AlphaMode2d`. To keep previous
behaviour, use `AlphaMode::BLEND`. If you know your sprite is opaque,
use `AlphaMode::OPAQUE`
## Follow up PRs
- See tracking issue: #13265
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
# Objective
Adds a new `Monitor` component representing a winit `MonitorHandle` that
can be used to spawn new windows and check for system monitor
information.
Closes#12955.
## Solution
For every winit event, check available monitors and spawn them into the
world as components.
## Testing
TODO:
- [x] Test plugging in and unplugging monitor during app runtime
- [x] Test spawning a window on a second monitor by entity id
- [ ] Since this touches winit, test all platforms
---
## Changelog
- Adds a new `Monitor` component that can be queried for information
about available system monitors.
## Migration Guide
- `WindowMode` variants now take a `MonitorSelection`, which can be set
to `MonitorSelection::Primary` to mirror the old behavior.
---------
Co-authored-by: Pascal Hertleif <pascal@technocreatives.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Pascal Hertleif <killercup@gmail.com>
# Objective
#13152 added support for reflecting functions. Now, we need a way to
register those functions such that they may be accessed anywhere within
the ECS.
## Solution
Added a `FunctionRegistry` type similar to `TypeRegistry`.
This allows a function to be registered and retrieved by name.
```rust
fn foo() -> i32 {
123
}
let mut registry = FunctionRegistry::default();
registry.register("my_function", foo);
let function = registry.get_mut("my_function").unwrap();
let value = function.call(ArgList::new()).unwrap().unwrap_owned();
assert_eq!(value.downcast_ref::<i32>(), Some(&123));
```
Additionally, I added an `AppFunctionRegistry` resource which wraps a
`FunctionRegistryArc`. Functions can be registered into this resource
using `App::register_function` or by getting a mutable reference to the
resource itself.
### Limitations
#### `Send + Sync`
In order to get this registry to work across threads, it needs to be
`Send + Sync`. This means that `DynamicFunction` needs to be `Send +
Sync`, which means that its internal function also needs to be `Send +
Sync`.
In most cases, this won't be an issue because standard Rust functions
(the type most likely to be registered) are always `Send + Sync`.
Additionally, closures tend to be `Send + Sync` as well, granted they
don't capture any `!Send` or `!Sync` variables.
This PR adds this `Send + Sync` requirement, but as mentioned above, it
hopefully shouldn't be too big of an issue.
#### Closures
Unfortunately, closures can't be registered yet. This will likely be
explored and added in a followup PR.
### Future Work
Besides addressing the limitations listed above, another thing we could
look into is improving the lookup of registered functions. One aspect is
in the performance of hashing strings. The other is in the developer
experience of having to call `std::any::type_name_of_val` to get the
name of their function (assuming they didn't give it a custom name).
## Testing
You can run the tests locally with:
```
cargo test --package bevy_reflect
```
---
## Changelog
- Added `FunctionRegistry`
- Added `AppFunctionRegistry` (a `Resource` available from `bevy_ecs`)
- Added `FunctionRegistryArc`
- Added `FunctionRegistrationError`
- Added `reflect_functions` feature to `bevy_ecs` and `bevy_app`
- `FunctionInfo` is no longer `Default`
- `DynamicFunction` now requires its wrapped function be `Send + Sync`
## Internal Migration Guide
> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.
`DynamicFunction` (both those created manually and those created with
`IntoFunction`), now require `Send + Sync`. All standard Rust functions
should meet that requirement. Closures, on the other hand, may not if
they capture any `!Send` or `!Sync` variables from its environment.
# Objective
- Fixes#14595
## Solution
- Use `num_cascades: 1` in WebGL build.
`CascadeShadowConfigBuilder::default()` gives this number in WebGL:
8235daaea0/crates/bevy_pbr/src/light/mod.rs (L241-L248)
## Testing
- Tested the modified example in WebGL with Firefox/Chrome
---------
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
# Objective
This idea came up in the context of a hypothetical "text sections as
entities" where text sections are children of a text bundle.
```rust
commands
.spawn(TextBundle::default())
.with_children(|parent} {
parent.spawn(TextSection::from("Hello"));
});
```
This is a bit cumbersome (but powerful and probably the way things are
headed). [`bsn!`](https://github.com/bevyengine/bevy/discussions/14437)
will eventually make this nicer, but in the mean time, this might
improve ergonomics for the common case where there is only one
`TextSection`.
## Solution
Add a `with_child` method to the `BuildChildren` trait that spawns a
single bundle and adds it as a child to the entity.
```rust
commands
.spawn(TextBundle::default())
.with_child(TextSection::from("Hello"));
```
## Testing
I added some tests, and modified the `button` example to use the new
method.
If any potential co-authors want to improve the tests, that would be
great.
## Alternatives
- Some sort of macro. See
https://github.com/tigregalis/bevy_spans_ent/blob/main/examples/macro.rs#L20.
I don't love this, personally, and it would probably be obsoleted by
`bsn!`.
- Wait for `bsn!`
- Add `with_children_batch` that takes an `Into<Iterator>` of bundles.
```rust
with_children_batch(vec![TextSection::from("Hello")])
```
This is maybe not as useful as it sounds -- it only works with
homogeneous bundles, so no marker components or styles.
- If this doesn't seem valuable, doing nothing is cool with me.
# Objective
- A lot of mid-level rendering apis are hard to figure out because they
don't have any examples
- SpecializedMeshPipeline can be really useful in some cases when you
want more flexibility than a Material without having to go to low level
apis.
## Solution
- Add an example showing how to make a custom `SpecializedMeshPipeline`.
## Testing
- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
---
## Showcase
The examples just spawns 3 triangles in a triangle pattern.
![image](https://github.com/user-attachments/assets/c3098758-94c4-4775-95e5-1d7c7fb9eb86)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Similar to #14537 , this fixes a minor lint issue causing CI failures
when using nightly toolchain.
## Solution
Add `#[allow(dead_code)]` to unused sample code.
## Testing
`cargo run -p ci -- lints` using 1.82 toolchain.
# Objective
- Fixes#11219
## Solution
- Scaling calculations use texture dimensions instead of layout
dimensions.
## Testing
- Did you test these changes? If so, how?
All UI examples look fine.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Example in #11219
## Migration Guide
```diff
let ui_node = ExtractedUiNode {
stack_index,
transform,
color,
rect,
image,
- atlas_size: Some(atlas_size * scale_factor),
+ atlas_scaling: Some(Vec2::splat(scale_factor)),
clip,
flip_x,
flip_y,
camera_entity,
border,
border_radius,
node_type,
},
```
```diff
let computed_slices = ComputedTextureSlices {
slices,
- image_size,
}
```
# Objective
- Make it possible to know *what* changed your component or resource.
- Common need when debugging, when you want to know the last code
location that mutated a value in the ECS.
- This feature would be very useful for the editor alongside system
stepping.
## Solution
- Adds the caller location to column data.
- Mutations now `track_caller` all the way up to the public API.
- Commands that invoke these functions immediately call
`Location::caller`, and pass this into the functions, instead of the
functions themselves attempting to get the caller. This would not work
for commands which are deferred, as the commands are executed by the
scheduler, not the user's code.
## Testing
- The `component_change_detection` example now shows where the component
was mutated:
```
2024-07-28T06:57:48.946022Z INFO component_change_detection: Entity { index: 1, generation: 1 }: New value: MyComponent(0.0)
2024-07-28T06:57:49.004371Z INFO component_change_detection: Entity { index: 1, generation: 1 }: New value: MyComponent(1.0)
2024-07-28T06:57:49.012738Z WARN component_change_detection: Change detected!
-> value: Ref(MyComponent(1.0))
-> added: false
-> changed: true
-> changed by: examples/ecs/component_change_detection.rs:36:23
```
- It's also possible to inspect change location from a debugger:
<img width="608" alt="image"
src="https://github.com/user-attachments/assets/c90ecc7a-0462-457a-80ae-42e7f5d346b4">
---
## Changelog
- Added source locations to ECS change detection behind the
`track_change_detection` flag.
## Migration Guide
- Added `changed_by` field to many internal ECS functions used with
change detection when the `track_change_detection` feature flag is
enabled. Use Location::caller() to provide the source of the function
call.
---------
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective
Use the new `AccumulatedMouseMotion` and `AccumulatedMouseScroll`
resources in place of mouse event handling.
I left the `mouse_input_events` example alone, since by its nature it
demonstrates event detection.
Fixes#14066
## Testing
Ran each example locally before and after changes.
# Objective
Previously, this area of bevy_math used raw translation and rotations to
encode isometries, which did not exist earlier. The goal of this PR is
to make the codebase of bevy_math more harmonious by using actual
isometries (`Isometry2d`/`Isometry3d`) in these places instead — this
will hopefully make the interfaces more digestible for end-users, in
addition to facilitating conversions.
For instance, together with the addition of #14478, this means that a
bounding box for a collider with an isometric `Transform` can be
computed as
```rust
collider.aabb_3d(collider_transform.to_isometry())
```
instead of using manual destructuring.
## Solution
- The traits `Bounded2d` and `Bounded3d` now use `Isometry2d` and
`Isometry3d` (respectively) instead of `translation` and `rotation`
parameters; e.g.:
```rust
/// A trait with methods that return 3D bounding volumes for a shape.
pub trait Bounded3d {
/// Get an axis-aligned bounding box for the shape translated and
rotated by the given isometry.
fn aabb_3d(&self, isometry: Isometry3d) -> Aabb3d;
/// Get a bounding sphere for the shape translated and rotated by the
given isometry.
fn bounding_sphere(&self, isometry: Isometry3d) -> BoundingSphere;
}
```
- Similarly, the `from_point_cloud` constructors for axis-aligned
bounding boxes and bounding circles/spheres now take isometries instead
of separate `translation` and `rotation`; e.g.:
```rust
/// Computes the smallest [`Aabb3d`] containing the given set of points,
/// transformed by the rotation and translation of the given isometry.
///
/// # Panics
///
/// Panics if the given set of points is empty.
#[inline(always)]
pub fn from_point_cloud(
isometry: Isometry3d,
points: impl Iterator<Item = impl Into<Vec3A>>,
) -> Aabb3d { //... }
```
This has a couple additional results:
1. The end-user no longer interacts directly with `Into<Vec3A>` or
`Into<Rot2>` parameters; these conversions all happen earlier now,
inside the isometry types.
2. Similarly, almost all intermediate `Vec3 -> Vec3A` conversions have
been eliminated from the `Bounded3d` implementations for primitives.
This probably has some performance benefit, but I have not measured it
as of now.
## Testing
Existing unit tests help ensure that nothing has been broken in the
refactor.
---
## Migration Guide
The `Bounded2d` and `Bounded3d` traits now take `Isometry2d` and
`Isometry3d` parameters (respectively) instead of separate translation
and rotation arguments. Existing calls to `aabb_2d`, `bounding_circle`,
`aabb_3d`, and `bounding_sphere` will have to be changed to use
isometries instead. A straightforward conversion is to refactor just by
calling `Isometry2d/3d::new`, as follows:
```rust
// Old:
let aabb = my_shape.aabb_2d(my_translation, my_rotation);
// New:
let aabb = my_shape.aabb_2d(Isometry2d::new(my_translation, my_rotation));
```
However, if the old translation and rotation are 3d
translation/rotations originating from a `Transform` or
`GlobalTransform`, then `to_isometry` may be used instead. For example:
```rust
// Old:
let bounding_sphere = my_shape.bounding_sphere(shape_transform.translation, shape_transform.rotation);
// New:
let bounding_sphere = my_shape.bounding_sphere(shape_transform.to_isometry());
```
This discussion also applies to the `from_point_cloud` construction
method of `Aabb2d`/`BoundingCircle`/`Aabb3d`/`BoundingSphere`, which has
similarly been altered to use isometries.
# Objective
Previously, our cubic spline constructors would produce
`CubicCurve`/`RationalCurve` output with no data when they themselves
didn't hold enough control points to produce a well-formed curve.
Attempting to sample the resulting empty "curves" (e.g. by calling
`CubicCurve::position`) would crash the program (😓).
The objectives of this PR are:
1. Ensure that the curve output of `bevy_math`'s spline constructions
are never invalid as data.
2. Provide a type-level guarantee that `CubicCurve` and `RationalCurve`
actually function as curves.
## Solution
This has a few pieces. Firstly, the curve generator traits
`CubicGenerator`, `CyclicCubicGenerator`, and `RationalGenerator` are
now fallible — they have associated error types, and the
curve-generation functions are allowed to fail:
```rust
/// Implement this on cubic splines that can generate a cubic curve from their spline parameters.
pub trait CubicGenerator<P: VectorSpace> {
/// An error type indicating why construction might fail.
type Error;
/// Build a [`CubicCurve`] by computing the interpolation coefficients for each curve segment.
fn to_curve(&self) -> Result<CubicCurve<P>, Self::Error>;
}
```
All existing spline constructions use this together with errors that
indicate when they didn't have the right control data and provide curves
which have at least one segment whenever they return an `Ok` variant.
Next, `CubicCurve` and `RationalCurve` have been blessed with a
guarantee that their internal array of segments (`segments`) is never
empty. In particular, this field is no longer public, so that invalid
curves cannot be built using struct instantiation syntax. To compensate
for this shortfall for users (in particular library authors who might
want to implement their own generators), there is a new method
`from_segments` on these for constructing a curve from a list of
segments, failing if the list is empty:
```rust
/// Create a new curve from a collection of segments. If the collection of segments is empty,
/// a curve cannot be built and `None` will be returned instead.
pub fn from_segments(segments: impl Into<Vec<CubicSegment<P>>>) -> Option<Self> { //... }
```
All existing methods on `CyclicCurve` and `CubicCurve` maintain the
invariant, so the direct construction of invalid values by users is
impossible.
## Testing
Run unit tests from `bevy_math::cubic_splines`. Additionally, run the
`cubic_splines` example and try to get it to crash using small numbers
of control points: it uses the fallible constructors directly, so if
invalid data is ever constructed, it is basically guaranteed to crash.
---
## Migration Guide
The `to_curve` method on Bevy's cubic splines is now fallible (returning
a `Result`), meaning that any existing calls will need to be updated by
handling the possibility of an error variant.
Similarly, any custom implementation of `CubicGenerator` or
`RationalGenerator` will need to be amended to include an `Error` type
and be made fallible itself.
Finally, the fields of `CubicCurve` and `RationalCurve` are now private,
so any direct constructions of these structs from segments will need to
be replaced with the new `CubicCurve::from_segments` and
`RationalCurve::from_segments` methods.
---
## Design
The main thing to justify here is the choice for the curve internals to
remain the same. After all, if they were able to cause crashes in the
first place, it's worth wondering why safeguards weren't put in place on
the types themselves to prevent that.
My view on this is that the problem was really that the internals of
these methods implicitly relied on the assumption that the value they
were operating on was *actually a curve*, when this wasn't actually
guaranteed. Now, it's possible to make a bunch of small changes inside
the curve struct methods to account for that, but I think that's worse
than just guaranteeing that the data is valid upstream — sampling is
about as hot a code path as we're going to get in this area, and hitting
an additional branch every time it happens just to check that the struct
contains valid data is probably a waste of resources.
Another way of phrasing this is that even if we're only interested in
solving the crashes, the curve's validity needs to be checked at some
point, and it's almost certainly better to do this once at the point of
construction than every time the curve is sampled.
In cases where the control data is supplied dynamically, users would
already have to deal with empty curve outputs basically not working.
Anecdotally, I ran into this while writing the `cubic_splines` example,
and I think the diff illustrates the improvement pretty nicely — the
code no longer has to anticipate whether the output will be good or not;
it just has to handle the `Result`.
The cost of all this, of course, is that we have to guarantee that the
new invariant is actually maintained whenever we extend the API.
However, for the most part, I don't expect users to want to do much
surgery on the internals of their curves anyway.
# Objective
- Fix issue #2611
## Solution
- Add `--generate-link-to-definition` to all the `rustdoc-args` arrays
in the `Cargo.toml`s (for docs.rs)
- Add `--generate-link-to-definition` to the `RUSTDOCFLAGS` environment
variable in the docs workflow (for dev-docs.bevyengine.org)
- Document all the workspace crates in the docs workflow (needed because
otherwise only the source code of the `bevy` package will be included,
making the argument useless)
- I think this also fixes#3662, since it fixes the bug on
dev-docs.bevyengine.org, while on docs.rs it has been fixed for a while
on their side.
---
## Changelog
- The source code viewer on docs.rs now includes links to the
definitions.
# Objective
- meshlet example has broken since #14273
## Solution
- disable msaa in meshlet example
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
I just wanted to inspect `HashSet`s in `bevy-inspector-egui` but I
noticed that it didn't work for some reason. A few minutes later I found
myself looking into the bevy reflect impls noticing that `HashSet`s have
been covered only rudimentary up until now.
## Solution
I'm not sure if this is overkill (especially the first bullet), but
here's a list of the changes:
- created a whole new trait and enum variants for `ReflectRef` and the
like called `Set`
- mostly oriented myself at the `Map` trait and made the necessary
changes until RA was happy
- create macro `impl_reflect_for_hashset!` and call it on `std::HashSet`
and `hashbrown::HashSet`
Extra notes:
- no `get_mut` or `get_mut_at` mirroring the `std::HashSet`
- `insert[_boxed]` and `remove` return `bool` mirroring `std::HashSet`,
additionally that bool is reflect as I thought that would be how we
handle things in bevy reflect, but I'm not sure on this
- ser/de are handled via `SeqAccess`
- I'm not sure about the general deduplication property of this impl of
`Set` that is generally expected? I'm also not sure yet if `Map` does
provide this. This mainly refers to the `Dynamic[...]` structs
- I'm not sure if there are other methods missing from the `trait`, I
felt like `contains` or the set-operations (union/diff/...) could've
been helpful, but I wanted to get out the bare minimum for feedback
first
---
## Changelog
### Added
- `Set` trait for `bevy_reflect`
### Changed
- `std::collections::HashSet` and `bevy_utils::hashbrown::HashSet` now
implement a more complete set of reflect functionalities instead of
"just" `reflect_value`
- `TypeInfo` contains a new variant `Set` that contains `SetInfo`
- `ReflectKind` contains a new variant `Set`
- `ReflectRef` contains a new variant `Set`
- `ReflectMut` contains a new variant `Set`
- `ReflectOwned` contains a new variant `Set`
## Migration Guide
- The new `Set` variants on the enums listed in the change section
should probably be considered by people working with this level of the
lib
### Help wanted!
I'm not sure if this change is able to break code. From my understanding
it shouldn't since we just add functionality but I'm not sure yet if
theres anything missing from my impl that would be normally provided by
`impl_reflect_value!`
# Objective
- It's possible to have errors in a draw command, but these errors are
ignored
## Solution
- Return a result with the error
## Changelog
Renamed `RenderCommandResult::Failure` to `RenderCommandResult::Skip`
Added a `reason` string parameter to `RenderCommandResult::Failure`
## Migration Guide
If you were using `RenderCommandResult::Failure` to just ignore an error
and retry later, use `RenderCommandResult::Skip` instead.
This wasn't intentional, but this PR should also help with
https://github.com/bevyengine/bevy/issues/12660 since we can turn a few
unwraps into error messages now.
---------
Co-authored-by: Charlotte McElwain <charlotte.c.mcelwain@gmail.com>
Switches `Msaa` from being a globally configured resource to a per
camera view component.
Closes#7194
# Objective
Allow individual views to describe their own MSAA settings. For example,
when rendering to different windows or to different parts of the same
view.
## Solution
Make `Msaa` a component that is required on all camera bundles.
## Testing
Ran a variety of examples to ensure that nothing broke.
TODO:
- [ ] Make sure android still works per previous comment in
`extract_windows`.
---
## Migration Guide
`Msaa` is no longer configured as a global resource, and should be
specified on each spawned camera if a non-default setting is desired.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
## Objective
Make the docs say the right thing.
## Solution
Edit the docs so they say the right thing.
Seems like overtime the example has changed but the comment did not
change with it. It originally was a AND but is now an OR.
# Objective
When the user renders multiple cameras to the same output texture, it
can sometimes be confusing what `ClearColorConfig` is necessary for each
camera to avoid overwriting the previous camera's output. This is
particular true in cases where the user uses mixed HDR cameras, which
means that their scene is being rendered to different internal textures.
## Solution
When a view has a configured viewport, set the GPU scissor in the
upscaling node so we don't overwrite areas that were written to by other
cameras.
## Testing
Ran the `split_screen` example.
# Objective
Fixes#7433
Alternative to #14323
## Solution
Add `DefaultPlugins` so we actually have tracing spans when using
`trace_tracy` or `trace_chrome`.
## Testing
```
cargo run --release --features trace_tracy --example transform_hierarchy large_tree
```
This now connects to Tracy and sends a bunch of data.
# Objective
- Fixes: https://github.com/bevyengine/bevy/issues/14036
## Solution
- Add a world space transformation for the environment sample direction.
## Testing
- I have tested the newly added `transform` field using the newly added
`rotate_environment_map` example.
https://github.com/user-attachments/assets/2de77c65-14bc-48ee-b76a-fb4e9782dbdb
## Migration Guide
- Since we have added a new filed to the `EnvironmentMapLight` struct,
users will need to include `..default()` or some rotation value in their
initialization code.
# Objective
Fill a gap in the functionality of our curve constructions by allowing
users to easily build cyclic curves from control data.
## Solution
Here I opted for something lightweight and discoverable. There is a new
`CyclicCubicGenerator` trait with a method `to_curve_cyclic` which uses
splines' control data to create curves that are cyclic. For now, its
signature is exactly like that of `CubicGenerator` — `to_curve_cyclic`
just yields a `CubicCurve`:
```rust
/// Implement this on cubic splines that can generate a cyclic cubic curve from their spline parameters.
///
/// This makes sense only when the control data can be interpreted cyclically.
pub trait CyclicCubicGenerator<P: VectorSpace> {
/// Build a cyclic [`CubicCurve`] by computing the interpolation coefficients for each curve segment.
fn to_curve_cyclic(&self) -> CubicCurve<P>;
}
```
This trait has been implemented for `CubicHermite`,
`CubicCardinalSpline`, `CubicBSpline`, and `LinearSpline`:
<img width="753" alt="Screenshot 2024-07-01 at 8 58 27 PM"
src="https://github.com/bevyengine/bevy/assets/2975848/69ae0802-3b78-4fb9-b73a-6f842cf3b33c">
<img width="628" alt="Screenshot 2024-07-01 at 9 00 14 PM"
src="https://github.com/bevyengine/bevy/assets/2975848/2992175a-a96c-40fc-b1a1-5206c3572cde">
<img width="606" alt="Screenshot 2024-07-01 at 8 59 36 PM"
src="https://github.com/bevyengine/bevy/assets/2975848/9e99eb3a-dbe6-42da-886c-3d3e00410d03">
<img width="603" alt="Screenshot 2024-07-01 at 8 59 01 PM"
src="https://github.com/bevyengine/bevy/assets/2975848/d037bc0c-396a-43af-ab5c-fad9a29417ef">
(Each type pictured respectively with the control points rendered as
green spheres; tangents not pictured in the case of the Hermite spline.)
These curves are all parametrized so that the output of `to_curve` and
the output of `to_curve_cyclic` are similar. For instance, in
`CubicCardinalSpline`, the first output segment is a curve segment
joining the first and second control points in each, although it is
constructed differently. In the other cases, the segments from
`to_curve` are a subset of those in `to_curve_cyclic`, with the new
segments appearing at the end.
## Testing
I rendered cyclic splines from control data and made sure they looked
reasonable. Existing tests are intact for splines where previous code
was modified. (Note that the coefficient computation for cyclic spline
segments is almost verbatim identical to that of their non-cyclic
counterparts.)
The Bezier benchmarks also look fine.
---
## Changelog
- Added `CyclicCubicGenerator` trait to `bevy_math::cubic_splines` for
creating cyclic curves from control data.
- Implemented `CyclicCubicGenerator` for `CubicHermite`,
`CubicCardinalSpline`, `CubicBSpline`, and `LinearSpline`.
- `bevy_math` now depends on `itertools`.
---
## Discussion
### Design decisions
The biggest thing here is just the approach taken in the first place:
namely, the cyclic constructions use new methods on the same old
structs. This choice was made to reduce friction and increase
discoverability but also because creating new ones just seemed
unnecessary: the underlying data would have been the same, so creating
something like "`CyclicCubicBSpline`" whose internally-held control data
is regarded as cyclic in nature doesn't really accomplish much — the end
result for the user is basically the same either way.
Similarly, I don't presently see a pressing need for `to_curve_cyclic`
to output something other than a `CubicCurve`, although changing this in
the future may be useful. See below.
A notable omission here is that `CyclicCubicGenerator` is not
implemented for `CubicBezier`. This is not a gap waiting to be filled —
`CubicBezier` just doesn't have enough data to join its start with its
end without just making up the requisite control points wholesale. In
all the cases where `CyclicCubicGenerator` has been implemented here,
the fashion in which the ends are connected is quite natural and follows
the semantics of the associated spline construction.
### Future direction
There are two main things here:
1. We should investigate whether we should do something similar for
NURBS. I just don't know that much about NURBS at the moment, so I
regarded this as out of scope for the PR.
2. We may eventually want to change the output type of
`CyclicCubicGenerator::to_curve_cyclic` to a type which reifies the
cyclic nature of the curve output. This wasn't done in this PR because
I'm unsure how much value a type-level guarantee of cyclicity actually
has, but if some useful features make sense only in the case of cyclic
curves, this might be worth pursuing.
This commit uses the [`offset-allocator`] crate to combine vertex and
index arrays from different meshes into single buffers. Since the
primary source of `wgpu` overhead is from validation and synchronization
when switching buffers, this significantly improves Bevy's rendering
performance on many scenes.
This patch is a more flexible version of #13218, which also used slabs.
Unlike #13218, which used slabs of a fixed size, this commit implements
slabs that start small and can grow. In addition to reducing memory
usage, supporting slab growth reduces the number of vertex and index
buffer switches that need to happen during rendering, leading to
improved performance. To prevent pathological fragmentation behavior,
slabs are capped to a maximum size, and mesh arrays that are too large
get their own dedicated slabs.
As an additional improvement over #13218, this commit allows the
application to customize all allocator heuristics. The
`MeshAllocatorSettings` resource contains values that adjust the minimum
and maximum slab sizes, the cutoff point at which meshes get their own
dedicated slabs, and the rate at which slabs grow. Hopefully-sensible
defaults have been chosen for each value.
Unfortunately, WebGL 2 doesn't support the *base vertex* feature, which
is necessary to pack vertex arrays from different meshes into the same
buffer. `wgpu` represents this restriction as the downlevel flag
`BASE_VERTEX`. This patch detects that bit and ensures that all vertex
buffers get dedicated slabs on that platform. Even on WebGL 2, though,
we can combine all *index* arrays into single buffers to reduce buffer
changes, and we do so.
The following measurements are on Bistro:
Overall frame time improves from 8.74 ms to 5.53 ms (1.58x speedup):
![Screenshot 2024-07-09
163521](https://github.com/bevyengine/bevy/assets/157897/5d83c824-c0ee-434c-bbaf-218ff7212c48)
Render system time improves from 6.57 ms to 3.54 ms (1.86x speedup):
![Screenshot 2024-07-09
163559](https://github.com/bevyengine/bevy/assets/157897/d94e2273-c3a0-496a-9f88-20d394129610)
Opaque pass time improves from 4.64 ms to 2.33 ms (1.99x speedup):
![Screenshot 2024-07-09
163536](https://github.com/bevyengine/bevy/assets/157897/e4ef6e48-d60e-44ae-9a71-b9a731c99d9a)
## Migration Guide
### Changed
* Vertex and index buffers for meshes may now be packed alongside other
buffers, for performance.
* `GpuMesh` has been renamed to `RenderMesh`, to reflect the fact that
it no longer directly stores handles to GPU objects.
* Because meshes no longer have their own vertex and index buffers, the
responsibility for the buffers has moved from `GpuMesh` (now called
`RenderMesh`) to the `MeshAllocator` resource. To access the vertex data
for a mesh, use `MeshAllocator::mesh_vertex_slice`. To access the index
data for a mesh, use `MeshAllocator::mesh_index_slice`.
[`offset-allocator`]: https://github.com/pcwalton/offset-allocator
# Objective
Many functions can be converted to `DynamicFunction` using
`IntoFunction`. Unfortunately, we are limited by Rust itself and the
implementations are far from exhaustive. For example, we can't convert
functions with more than 16 arguments. Additionally, we can't handle
returns with lifetimes not tied to the lifetime of the first argument.
In such cases, users will have to create their `DynamicFunction`
manually.
Let's take the following function:
```rust
fn get(index: usize, list: &Vec<String>) -> &String {
&list[index]
}
```
This function cannot be converted to a `DynamicFunction` via
`IntoFunction` due to the lifetime of the return value being tied to the
second argument. Therefore, we need to construct the `DynamicFunction`
manually:
```rust
DynamicFunction::new(
|mut args, info| {
let list = args
.pop()
.unwrap()
.take_ref::<Vec<String>>(&info.args()[1])?;
let index = args.pop().unwrap().take_owned::<usize>(&info.args()[0])?;
Ok(Return::Ref(get(index, list)))
},
FunctionInfo::new()
.with_name("get")
.with_args(vec![
ArgInfo:🆕:<usize>(0).with_name("index"),
ArgInfo:🆕:<&Vec<String>>(1).with_name("list"),
])
.with_return_info(ReturnInfo:🆕:<&String>()),
);
```
While still a small and straightforward snippet, there's a decent amount
going on here. There's a lot of room for improvements when it comes to
ergonomics and readability.
The goal of this PR is to address those issues.
## Solution
Improve the ergonomics and readability of manually created
`DynamicFunction`s.
Some of the major changes:
1. Removed the need for `&ArgInfo` when reifying arguments (i.e. the
`&info.args()[1]` calls)
2. Added additional `pop` methods on `ArgList` to handle both popping
and casting
3. Added `take` methods on `ArgList` for taking the arguments out in
order
4. Removed the need for `&FunctionInfo` in the internal closure (Change
1 made it no longer necessary)
5. Added methods to automatically handle generating `ArgInfo` and
`ReturnInfo`
With all these changes in place, we get something a lot nicer to both
write and look at:
```rust
DynamicFunction::new(
|mut args| {
let index = args.take::<usize>()?;
let list = args.take::<&Vec<String>>()?;
Ok(Return::Ref(get(index, list)))
},
FunctionInfo::new()
.with_name("get")
.with_arg::<usize>("index")
.with_arg::<&Vec<String>>("list")
.with_return::<&String>(),
);
```
Alternatively, to rely on type inference for taking arguments, you could
do:
```rust
DynamicFunction::new(
|mut args| {
let index = args.take_owned()?;
let list = args.take_ref()?;
Ok(Return::Ref(get(index, list)))
},
FunctionInfo::new()
.with_name("get")
.with_arg::<usize>("index")
.with_arg::<&Vec<String>>("list")
.with_return::<&String>(),
);
```
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect
```
---
## Changelog
- Removed `&ArgInfo` argument from `FromArg::from_arg` trait method
- Removed `&ArgInfo` argument from `Arg::take_***` methods
- Added `ArgValue`
- `Arg` is now a struct containing an `ArgValue` and an argument `index`
- `Arg::take_***` methods now require `T` is also `TypePath`
- Added `Arg::new`, `Arg::index`, `Arg::value`, `Arg::take_value`, and
`Arg::take` methods
- Replaced `ArgId` in `ArgError` with just the argument `index`
- Added `ArgError::EmptyArgList`
- Renamed `ArgList::push` to `ArgList::push_arg`
- Added `ArgList::pop_arg`, `ArgList::pop_owned`, `ArgList::pop_ref`,
and `ArgList::pop_mut`
- Added `ArgList::take_arg`, `ArgList::take_owned`, `ArgList::take_ref`,
`ArgList::take_mut`, and `ArgList::take`
- `ArgList::pop` is now generic
- Renamed `FunctionError::InvalidArgCount` to
`FunctionError::ArgCountMismatch`
- The closure given to `DynamicFunction::new` no longer has a
`&FunctionInfo` argument
- Added `FunctionInfo::with_arg`
- Added `FunctionInfo::with_return`
## Internal Migration Guide
> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.
* The `FromArg::from_arg` trait method and the `Arg::take_***` methods
no longer take a `&ArgInfo` argument.
* What used to be `Arg` is now `ArgValue`. `Arg` is now a struct which
contains an `ArgValue`.
* `Arg::take_***` methods now require `T` is also `TypePath`
* Instances of `id: ArgId` in `ArgError` have been replaced with `index:
usize`
* `ArgList::push` is now `ArgList::push_arg`. It also takes the new
`ArgValue` type.
* `ArgList::pop` has become `ArgList::pop_arg` and now returns
`ArgValue`. `Arg::pop` now takes a generic type and downcasts to that
type. It's recommended to use `ArgList::take` and friends instead since
they allow removing the arguments from the list in the order they were
pushed (rather than reverse order).
* `FunctionError::InvalidArgCount` is now
`FunctionError::ArgCountMismatch`
* The closure given to `DynamicFunction::new` no longer has a
`&FunctionInfo` argument. This argument can be removed.
# Objective
- Continue to pare down the uses on NonSend resources in the engine. In
this case, EventLoopProxy used to be `!Sync`, but is now `Sync` in the
latest version of winit.
## Solution
- New type `EventLoopProxy` as `EventLoopProxyWrapper` to make it into a
normal resource.
- Update the `custom_user_event` example as it no longer needs to
indirectly access the `EventLoopProxy` through a static variable
anymore.
## Testing
- Ran the example. The resource exists just for users to use, so there
aren't any in engine uses for it currently.
---
## Changelog
- make EventLoopProxy into a regular resource.
## Migration Guide
`EventLoopProxy` has been renamed to `EventLoopProxyWrapper` and is now
`Send`, making it an ordinary resource.
Before:
```rust
event_loop_system(event_loop: NonSend<EventLoopProxy<MyEvent>>) {
event_loop.send_event(MyEvent);
}
```
After:
```rust
event_loop_system(event_loop: Res<EventLoopProxy<MyEvent>>) {
event_loop.send_event(MyEvent);
}
```
# Objective
As mentioned in
[this](https://github.com/bevyengine/bevy/pull/13152#issuecomment-2198387297)
comment, creating a function registry (see #14098) is a bit difficult
due to the requirements of `DynamicFunction`. Internally, a
`DynamicFunction` contains a `Box<dyn FnMut>` (the function that reifies
reflected arguments and calls the actual function), which requires `&mut
self` in order to be called.
This means that users would require a mutable reference to the function
registry for it to be useful— which isn't great. And they can't clone
the `DynamicFunction` either because cloning an `FnMut` isn't really
feasible (wrapping it in an `Arc` would allow it to be cloned but we
wouldn't be able to call the clone since we need a mutable reference to
the `FnMut`, which we can't get with multiple `Arc`s still alive,
requiring us to also slap in a `Mutex`, which adds additional overhead).
And we don't want to just replace the `dyn FnMut` with `dyn Fn` as that
would prevent reflecting closures that mutate their environment.
Instead, we need to introduce a new type to split the requirements of
`DynamicFunction`.
## Solution
Introduce new types for representing closures.
Specifically, this PR introduces `DynamicClosure` and
`DynamicClosureMut`. Similar to how `IntoFunction` exists for
`DynamicFunction`, two new traits were introduced: `IntoClosure` and
`IntoClosureMut`.
Now `DynamicFunction` stores a `dyn Fn` with a `'static` lifetime.
`DynamicClosure` also uses a `dyn Fn` but has a lifetime, `'env`, tied
to its environment. `DynamicClosureMut` is most like the old
`DynamicFunction`, keeping the `dyn FnMut` and also typing its lifetime,
`'env`, to the environment
Here are some comparison tables:
| | `DynamicFunction` | `DynamicClosure` | `DynamicClosureMut` |
| - | ----------------- | ---------------- | ------------------- |
| Callable with `&self` | ✅ | ✅ | ❌ |
| Callable with `&mut self` | ✅ | ✅ | ✅ |
| Allows for non-`'static` lifetimes | ❌ | ✅ | ✅ |
| | `IntoFunction` | `IntoClosure` | `IntoClosureMut` |
| - | -------------- | ------------- | ---------------- |
| Convert `fn` functions | ✅ | ✅ | ✅ |
| Convert `fn` methods | ✅ | ✅ | ✅ |
| Convert anonymous functions | ✅ | ✅ | ✅ |
| Convert closures that capture immutable references | ❌ | ✅ | ✅ |
| Convert closures that capture mutable references | ❌ | ❌ | ✅ |
| Convert closures that capture owned values | ❌[^1] | ✅ | ✅ |
[^1]: Due to limitations in Rust, `IntoFunction` can't be implemented
for just functions (unless we forced users to manually coerce them to
function pointers first). So closures that meet the trait requirements
_can technically_ be converted into a `DynamicFunction` as well. To both
future-proof and reduce confusion, though, we'll just pretend like this
isn't a thing.
```rust
let mut list: Vec<i32> = vec![1, 2, 3];
// `replace` is a closure that captures a mutable reference to `list`
let mut replace = |index: usize, value: i32| -> i32 {
let old_value = list[index];
list[index] = value;
old_value
};
// Convert the closure into a dynamic closure using `IntoClosureMut::into_closure_mut`
let mut func: DynamicClosureMut = replace.into_closure_mut();
// Dynamically call the closure:
let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32);
let value = func.call_once(args).unwrap().unwrap_owned();
// Check the result:
assert_eq!(value.take::<i32>().unwrap(), 2);
assert_eq!(list, vec![1, -2, 3]);
```
### `ReflectFn`/`ReflectFnMut`
To make extending the function reflection system easier (the blanket
impls for `IntoFunction`, `IntoClosure`, and `IntoClosureMut` are all
incredibly short), this PR generalizes callables with two new traits:
`ReflectFn` and `ReflectFnMut`.
These traits mimic `Fn` and `FnMut` but allow for being called via
reflection. In fact, their blanket implementations are identical save
for `ReflectFn` being implemented over `Fn` types and `ReflectFnMut`
being implemented over `FnMut` types.
And just as `Fn` is a subtrait of `FnMut`, `ReflectFn` is a subtrait of
`ReflectFnMut`. So anywhere that expects a `ReflectFnMut` can also be
given a `ReflectFn`.
To reiterate, these traits aren't 100% necessary. They were added in
purely for extensibility. If we decide to split things up differently or
add new traits/types in the future, then those changes should be much
simpler to implement.
### `TypedFunction`
Because of the split into `ReflectFn` and `ReflectFnMut`, we needed a
new way to access the function type information. This PR moves that
concept over into `TypedFunction`.
Much like `Typed`, this provides a way to access a function's
`FunctionInfo`.
By splitting this trait out, it helps to ensure the other traits are
focused on a single responsibility.
### Internal Macros
The original function PR (#13152) implemented `IntoFunction` using a
macro which was passed into an `all_tuples!` macro invocation. Because
we needed the same functionality for these new traits, this PR has
copy+pasted that code for `ReflectFn`, `ReflectFnMut`, and
`TypedFunction`— albeit with some differences between them.
Originally, I was going to try and macro-ify the impls and where clauses
such that we wouldn't have to straight up duplicate a lot of this logic.
However, aside from being more complex in general, autocomplete just
does not play nice with such heavily nested macros (tried in both
RustRover and VSCode). And both of those problems told me that it just
wasn't worth it: we need to ensure the crate is easily maintainable,
even at the cost of duplicating code.
So instead, I made sure to simplify the macro code by removing all
fully-qualified syntax and cutting the where clauses down to the bare
essentials, which helps to clean up a lot of the visual noise. I also
tried my best to document the macro logic in certain areas (I may even
add a bit more) to help with maintainability for future devs.
### Documentation
Documentation for this module was a bit difficult for me. So many of
these traits and types are very interconnected. And each trait/type has
subtle differences that make documenting it in a single place, like at
the module level, difficult to do cleanly. Describing the valid
signatures is also challenging to do well.
Hopefully what I have here is okay. I think I did an okay job, but let
me know if there any thoughts on ways to improve it. We can also move
such a task to a followup PR for more focused discussion.
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect
```
---
## Changelog
- Added `DynamicClosure` struct
- Added `DynamicClosureMut` struct
- Added `IntoClosure` trait
- Added `IntoClosureMut` trait
- Added `ReflectFn` trait
- Added `ReflectFnMut` trait
- Added `TypedFunction` trait
- `IntoFunction` now only works for standard Rust functions
- `IntoFunction` no longer takes a lifetime parameter
- `DynamicFunction::call` now only requires `&self`
- Removed `DynamicFunction::call_once`
- Changed the `IntoReturn::into_return` signature to include a where
clause
## Internal Migration Guide
> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.
### `IntoClosure`
`IntoFunction` now only works for standard Rust functions. Calling
`IntoFunction::into_function` on a closure that captures references to
its environment (either mutable or immutable), will no longer compile.
Instead, you will need to use either `IntoClosure::into_closure` to
create a `DynamicClosure` or `IntoClosureMut::into_closure_mut` to
create a `DynamicClosureMut`, depending on your needs:
```rust
let punct = String::from("!");
let print = |value: String| {
println!("{value}{punct}");
};
// BEFORE
let func: DynamicFunction = print.into_function();
// AFTER
let func: DynamicClosure = print.into_closure();
```
### `IntoFunction` lifetime
Additionally, `IntoFunction` no longer takes a lifetime parameter as it
always expects a `'static` lifetime. Usages will need to remove any
lifetime parameters:
```rust
// BEFORE
fn execute<'env, F: IntoFunction<'env, Marker>, Marker>(f: F) {/* ... */}
// AFTER
fn execute<F: IntoFunction<Marker>, Marker>(f: F) {/* ... */}
```
### `IntoReturn`
`IntoReturn::into_return` now has a where clause. Any manual
implementors will need to add this where clause to their implementation.
Currently, volumetric fog is global and affects the entire scene
uniformly. This is inadequate for many use cases, such as local smoke
effects. To address this problem, this commit introduces *fog volumes*,
which are axis-aligned bounding boxes (AABBs) that specify fog
parameters inside their boundaries. Such volumes can also specify a
*density texture*, a 3D texture of voxels that specifies the density of
the fog at each point.
To create a fog volume, add a `FogVolume` component to an entity (which
is included in the new `FogVolumeBundle` convenience bundle). Like light
probes, a fog volume is conceptually a 1×1×1 cube centered on the
origin; a transform can be used to position and resize this region. Many
of the fields on the existing `VolumetricFogSettings` have migrated to
the new `FogVolume` component. `VolumetricFogSettings` on a camera is
still needed to enable volumetric fog. However, by itself
`VolumetricFogSettings` is no longer sufficient to enable volumetric
fog; a `FogVolume` must be present. Applications that wish to retain the
old global fog behavior can simply surround the scene with a large fog
volume.
By way of implementation, this commit converts the volumetric fog shader
from a full-screen shader to one applied to a mesh. The strategy is
different depending on whether the camera is inside or outside the fog
volume. If the camera is inside the fog volume, the mesh is simply a
plane scaled to the viewport, effectively falling back to a full-screen
pass. If the camera is outside the fog volume, the mesh is a cube
transformed to coincide with the boundaries of the fog volume's AABB.
Importantly, in the latter case, only the front faces of the cuboid are
rendered. Instead of treating the boundaries of the fog as a sphere
centered on the camera position, as we did prior to this patch, we
raytrace the far planes of the AABB to determine the portion of each ray
contained within the fog volume. We then raymarch in shadow map space as
usual. If a density texture is present, we modulate the fixed density
value with the trilinearly-interpolated value from that texture.
Furthermore, this patch introduces optional jitter to fog volumes,
intended for use with TAA. This modifies the position of the ray from
frame to frame using interleaved gradient noise, in order to reduce
aliasing artifacts. Many implementations of volumetric fog in games use
this technique. Note that this patch makes no attempt to write a motion
vector; this is because when a view ray intersects multiple voxels
there's no single direction of motion. Consequently, fog volumes can
have ghosting artifacts, but because fog is "ghostly" by its nature,
these artifacts are less objectionable than they would be for opaque
objects.
A new example, `fog_volumes`, has been added. It demonstrates a single
fog volume containing a voxelized representation of the Stanford bunny.
The existing `volumetric_fog` example has been updated to use the new
local volumetrics API.
## Changelog
### Added
* Local `FogVolume`s are now supported, to localize fog to specific
regions. They can optionally have 3D density voxel textures for precise
control over the distribution of the fog.
### Changed
* `VolumetricFogSettings` on a camera no longer enables volumetric fog;
instead, it simply enables the processing of `FogVolume`s within the
scene.
## Migration Guide
* A `FogVolume` is now necessary in order to enable volumetric fog, in
addition to `VolumetricFogSettings` on the camera. Existing uses of
volumetric fog can be migrated by placing a large `FogVolume`
surrounding the scene.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
The borders example is separate from the rounded borders example. If you
find the borders example, you may miss the rounded borders example.
## Solution
Merge the examples in a basic way, since there is enough room to show
all options at the same time.
I also considered renaming the borders and rounded borders examples so
that they would be located next to each other in repo and UI, but it
felt like having a singular example was better.
## Testing
```
cargo run --example borders
```
---
## Showcase
The merged example looks like this:
![screenshot-2024-07-14-at-13 40
10@2x](https://github.com/user-attachments/assets/0f49cc46-1ca0-40d0-abec-020cbf0fb205)
# Objective
```rust
// Currently:
builder.add_after::<FooPlugin, _>(BarPlugin);
// After this PR:
builder.add_after::<FooPlugin>(BarPlugin);
```
This removes some weirdness and better parallels the rest of the
`PluginGroupBuilder` API.
## Solution
Define a helper method `type_id_of_val` to use in `.add_before` and
`.add_after` instead of `TypeId::of::<T>` (which requires the plugin
type to be nameable, preventing `impl Plugin` from being used).
## Testing
Ran `cargo run -p ci lints` successfully.
## Migration Guide
Removed second generic from `PluginGroupBuilder` methods: `add_before`
and `add_after`.
```rust
// Before:
DefaultPlugins
.build()
.add_before::<WindowPlugin, _>(FooPlugin)
.add_after::<WindowPlugin, _>(BarPlugin)
// After:
DefaultPlugins
.build()
.add_before::<WindowPlugin>(FooPlugin)
.add_after::<WindowPlugin>(BarPlugin)
```
---------
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
# Objective
- Fixes https://github.com/bevyengine/bevy/issues/14036
## Solution
- Add a view space transformation for the skybox
## Testing
- I have tested the newly added `transform` field using the `skybox`
example.
```
diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs
index beaf5b268..d16cbe988 100644
--- a/examples/3d/skybox.rs
+++ b/examples/3d/skybox.rs
@@ -81,6 +81,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
Skybox {
image: skybox_handle.clone(),
brightness: 1000.0,
+ rotation: Quat::from_rotation_x(PI * -0.5),
},
));
```
<img width="1280" alt="image"
src="https://github.com/bevyengine/bevy/assets/6300263/1230a608-58ea-492d-a811-90c54c3b43ef">
## Migration Guide
- Since we have added a new filed to the Skybox struct, users will need
to include `..Default::default()` or some rotation value in their
initialization code.
# Objective
Implement FromIterator/IntoIterator for dynamic types where missing
Note:
- can't impl `IntoIterator` for `&Array` & co because of orphan rules
- `into_iter().collect()` is a no-op for `Vec`s because of
specialization
---
## Migration Guide
- Change `DynamicArray::from_vec` to `DynamicArray::from_iter`
# Objective
Fixes#14202
## Solution
Add `on_replaced` component hook and `OnReplaced` observer trigger
## Testing
- Did you test these changes? If so, how?
- Updated & added unit tests
---
## Changelog
- Added new `on_replaced` component hook and `OnReplaced` observer
trigger for performing cleanup on component values when they are
overwritten with `.insert()`
# Objective
- Using bincode to deserialize binary into a MeshletMesh is expensive
(~77ms for a 5mb file).
## Solution
- Write a custom deserializer using bytemuck's Pod types and slice
casting.
- Total asset load time has gone from ~102ms to ~12ms.
- Change some types I never meant to be public to private and other misc
cleanup.
## Testing
- Ran the meshlet example and added timing spans to the asset loader.
---
## Changelog
- Improved `MeshletMesh` loading speed
- The `MeshletMesh` disk format has changed, and
`MESHLET_MESH_ASSET_VERSION` has been bumped
- `MeshletMesh` fields are now private
- Renamed `MeshletMeshSaverLoad` to `MeshletMeshSaverLoader`
- The `Meshlet`, `MeshletBoundingSpheres`, and `MeshletBoundingSphere`
types are now private
- Removed `MeshletMeshSaveOrLoadError::SerializationOrDeserialization`
- Added `MeshletMeshSaveOrLoadError::WrongFileType`
## Migration Guide
- Regenerate your `MeshletMesh` assets, as the disk format has changed,
and `MESHLET_MESH_ASSET_VERSION` has been bumped
- `MeshletMesh` fields are now private
- `MeshletMeshSaverLoad` is now named `MeshletMeshSaverLoader`
- The `Meshlet`, `MeshletBoundingSpheres`, and `MeshletBoundingSphere`
types are now private
- `MeshletMeshSaveOrLoadError::SerializationOrDeserialization` has been
removed
- Added `MeshletMeshSaveOrLoadError::WrongFileType`, match on this
variant if you match on `MeshletMeshSaveOrLoadError`
# Objective
Type data is a **super** useful tool to know about when working with
reflection. However, most users don't fully understand how it works or
that you can use it for more than just object-safe traits.
This is unfortunate because it can be surprisingly simple to manually
create your own type data.
We should have an example detailing how type works, how users can define
their own, and how thy can be used.
## Solution
Added a `type_data` example.
This example goes through all the major points about type data:
- Why we need them
- How they can be defined
- The two ways they can be registered
- A list of common/important type data provided by Bevy
I also thought it might be good to go over the `#[reflect_trait]` macro
as part of this example since it has all the other context, including
how to define type data in places where `#[reflect_trait]` won't work.
Because of this, I removed the `trait_reflection` example.
## Testing
You can run the example locally with the following command:
```
cargo run --example type_data
```
---
## Changelog
- Added the `type_data` example
- Removed the `trait_reflection` example
This commit creates a new built-in postprocessing shader that's designed
to hold miscellaneous postprocessing effects, and starts it off with
chromatic aberration. Possible future effects include vignette, film
grain, and lens distortion.
[Chromatic aberration] is a common postprocessing effect that simulates
lenses that fail to focus all colors of light to a single point. It's
often used for impact effects and/or horror games. This patch uses the
technique from *Inside* ([Gjøl & Svendsen 2016]), which allows the
developer to customize the particular color pattern to achieve different
effects. Unity HDRP uses the same technique, while Unreal has a
hard-wired fixed color pattern.
A new example, `post_processing`, has been added, in order to
demonstrate the technique. The existing `post_processing` shader has
been renamed to `custom_post_processing`, for clarity.
[Chromatic aberration]:
https://en.wikipedia.org/wiki/Chromatic_aberration
[Gjøl & Svendsen 2016]:
https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf
![Screenshot 2024-06-04
180304](https://github.com/bevyengine/bevy/assets/157897/3631c64f-a615-44fe-91ca-7f04df0a54b2)
![Screenshot 2024-06-04
180743](https://github.com/bevyengine/bevy/assets/157897/ee055cbf-4314-49c5-8bfa-8d8a17bd52bb)
## Changelog
### Added
* Chromatic aberration is now available as a built-in postprocessing
effect. To use it, add `ChromaticAberration` to your camera.
# Objective
Add basic bubbling to observers, modeled off `bevy_eventlistener`.
## Solution
- Introduce a new `Traversal` trait for components which point to other
entities.
- Provide a default `TraverseNone: Traversal` component which cannot be
constructed.
- Implement `Traversal` for `Parent`.
- The `Event` trait now has an associated `Traversal` which defaults to
`TraverseNone`.
- Added a field `bubbling: &mut bool` to `Trigger` which can be used to
instruct the runner to bubble the event to the entity specified by the
event's traversal type.
- Added an associated constant `SHOULD_BUBBLE` to `Event` which
configures the default bubbling state.
- Added logic to wire this all up correctly.
Introducing the new associated information directly on `Event` (instead
of a new `BubblingEvent` trait) lets us dispatch both bubbling and
non-bubbling events through the same api.
## Testing
I have added several unit tests to cover the common bugs I identified
during development. Running the unit tests should be enough to validate
correctness. The changes effect unsafe portions of the code, but should
not change any of the safety assertions.
## Changelog
Observers can now bubble up the entity hierarchy! To create a bubbling
event, change your `Derive(Event)` to something like the following:
```rust
#[derive(Component)]
struct MyEvent;
impl Event for MyEvent {
type Traverse = Parent; // This event will propagate up from child to parent.
const AUTO_PROPAGATE: bool = true; // This event will propagate by default.
}
```
You can dispatch a bubbling event using the normal
`world.trigger_targets(MyEvent, entity)`.
Halting an event mid-bubble can be done using
`trigger.propagate(false)`. Events with `AUTO_PROPAGATE = false` will
not propagate by default, but you can enable it using
`trigger.propagate(true)`.
If there are multiple observers attached to a target, they will all be
triggered by bubbling. They all share a bubbling state, which can be
accessed mutably using `trigger.propagation_mut()` (`trigger.propagate`
is just sugar for this).
You can choose to implement `Traversal` for your own types, if you want
to bubble along a different structure than provided by `bevy_hierarchy`.
Implementers must be careful never to produce loops, because this will
cause bevy to hang.
## Migration Guide
+ Manual implementations of `Event` should add associated type `Traverse
= TraverseNone` and associated constant `AUTO_PROPAGATE = false`;
+ `Trigger::new` has new field `propagation: &mut Propagation` which
provides the bubbling state.
+ `ObserverRunner` now takes the same `&mut Propagation` as a final
parameter.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
update the `load_gltf_extras.rs` example to the newest bevy api
## Solution
uses the new type-safe code for loading the scene #0 from the gltf
instead of a path suffix
## Testing
the example runs as expected