# Objective
This example uses `println` from a system, which we don't advise people
do. It also gives no context for the debug prints, which I assumed to be
stray debug code at first.
## Solution
Use `info!`, and add a small amount of context so the console output
looks deliberate.
## Testing
`cargo run --example morph_targets`
# Objective
On mobile devices, it's best to use the OS's native logging due to the
difficulty of accessing the console. This is already done for Android.
This is an updated version of
https://github.com/bevyengine/bevy/pull/4462.
## Solution
This PR uses Absolucy's
[tracing-oslog](https://github.com/Absolucy/tracing-oslog) ([ZLib
license](https://github.com/Absolucy/tracing-oslog/blob/main/LICENSE.md))
for iOS in order to use Apple's `os_log`.
## Testing
I ran `examples/mobile` with the logging from `examples/app/logs.rs` on
an iOS device, I then checked the logs could be filtered in the MacOS
Console.app.
## Changelog
- Change bevy_log to use Apple's os_log on iOS.
## Questions for Reviewers
It's worth noting that the dependency this adds hasn't had bug fixes
released in a few years, so we may want to consider one or more of:
1. a feature flag to opt-in, and it would also allow `os_log` on MacOS
2. merge as-is and have some (minor?) upstream bugs
3. hold off on this PR until a suitable alternative dependency arises
4. maintain our own implementation
## Future work
In a follow-up PR it might be good to make the `subsystem` field have a
better default value, like [this
one](https://github.com/bevyengine/bevy/blob/main/examples/mobile/bevy_mobile_example.xcodeproj/project.pbxproj#L363).
That value can be retrieved programmatically if we bind another system
API (For posterity in Swift this is `Bundle.main.bundleIdentifier`, but
the C/ObjC equivalent is likely easier to bind). This would almost
always be the correct value, while the current default is unlikely to
ever be correct.
---------
Co-authored-by: Dusty DeWeese <dustin.deweese@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Since #15641, `loading_screen` panics.
```
called `Result::unwrap()` on an `Err` value: MultipleEntities("bevy_ecs::query::state::QueryState<&mut bevy_render::view::visibility::Visibility, bevy_ecs::query::filter::With<loading_screen::LoadingScreen>>")
```
Before that PR, the camera did not have a `Visibility` component. But
`Visibility` is now a required component of `Camera`. So the query
matches multiple entities.
## Solution
Minimal change to make the example behave like it used to.
Plus a tiny drive-by cleanup to remove a redundant unwrap.
## Testing
`cargo run --example loading_screen`
# Objective
In the Render World, there are a number of collections that are derived
from Main World entities and are used to drive rendering. The most
notable are:
- `VisibleEntities`, which is generated in the `check_visibility` system
and contains visible entities for a view.
- `ExtractedInstances`, which maps entity ids to asset ids.
In the old model, these collections were trivially kept in sync -- any
extracted phase item could look itself up because the render entity id
was guaranteed to always match the corresponding main world id.
After #15320, this became much more complicated, and was leading to a
number of subtle bugs in the Render World. The main rendering systems,
i.e. `queue_material_meshes` and `queue_material2d_meshes`, follow a
similar pattern:
```rust
for visible_entity in visible_entities.iter::<With<Mesh2d>>() {
let Some(mesh_instance) = render_mesh_instances.get_mut(visible_entity) else {
continue;
};
// Look some more stuff up and specialize the pipeline...
let bin_key = Opaque2dBinKey {
pipeline: pipeline_id,
draw_function: draw_opaque_2d,
asset_id: mesh_instance.mesh_asset_id.into(),
material_bind_group_id: material_2d.get_bind_group_id().0,
};
opaque_phase.add(
bin_key,
*visible_entity,
BinnedRenderPhaseType::mesh(mesh_instance.automatic_batching),
);
}
```
In this case, `visible_entities` and `render_mesh_instances` are both
collections that are created and keyed by Main World entity ids, and so
this lookup happens to work by coincidence. However, there is a major
unintentional bug here: namely, because `visible_entities` is a
collection of Main World ids, the phase item being queued is created
with a Main World id rather than its correct Render World id.
This happens to not break mesh rendering because the render commands
used for drawing meshes do not access the `ItemQuery` parameter, but
demonstrates the confusion that is now possible: our UI phase items are
correctly being queued with Render World ids while our meshes aren't.
Additionally, this makes it very easy and error prone to use the wrong
entity id to look up things like assets. For example, if instead we
ignored visibility checks and queued our meshes via a query, we'd have
to be extra careful to use `&MainEntity` instead of the natural
`Entity`.
## Solution
Make all collections that are derived from Main World data use
`MainEntity` as their key, to ensure type safety and avoid accidentally
looking up data with the wrong entity id:
```rust
pub type MainEntityHashMap<V> = hashbrown::HashMap<MainEntity, V, EntityHash>;
```
Additionally, we make all `PhaseItem` be able to provide both their Main
and Render World ids, to allow render phase implementors maximum
flexibility as to what id should be used to look up data.
You can think of this like tracking at the type level whether something
in the Render World should use it's "primary key", i.e. entity id, or
needs to use a foreign key, i.e. `MainEntity`.
## Testing
##### TODO:
This will require extensive testing to make sure things didn't break!
Additionally, some extraction logic has become more complicated and
needs to be checked for regressions.
## Migration Guide
With the advent of the retained render world, collections that contain
references to `Entity` that are extracted into the render world have
been changed to contain `MainEntity` in order to prevent errors where a
render world entity id is used to look up an item by accident. Custom
rendering code may need to be changed to query for `&MainEntity` in
order to look up the correct item from such a collection. Additionally,
users who implement their own extraction logic for collections of main
world entity should strongly consider extracting into a different
collection that uses `MainEntity` as a key.
Additionally, render phases now require specifying both the `Entity` and
`MainEntity` for a given `PhaseItem`. Custom render phases should ensure
`MainEntity` is available when queuing a phase item.
# Objective
Fixes#15824
## Solution
Looks like this was just a goof in migrating the example itself.
Added back in the rotation component of the transform that got dropped.
## Testing
`cargo run --example auto_exposure`
# Objective
- Closes#15716
- Closes#15718
## Solution
- Replace `Handle<MeshletMesh>` with a new `MeshletMesh3d` component
- As expected there were some random things that needed fixing:
- A couple tests were storing handles just to prevent them from being
dropped I believe, which seems to have been unnecessary in some.
- The `SpriteBundle` still had a `Handle<Image>` field. I've removed
this.
- Tests in `bevy_sprite` incorrectly added a `Handle<Image>` field
outside of the `Sprite` component.
- A few examples were still inserting `Handle`s, switched those to their
corresponding wrappers.
- 2 examples that were still querying for `Handle<Image>` were changed
to query `Sprite`
## Testing
- I've verified that the changed example work now
## Migration Guide
`Handle` can no longer be used as a `Component`. All existing Bevy types
using this pattern have been wrapped in their own semantically
meaningful type. You should do the same for any custom `Handle`
components your project needs.
The `Handle<MeshletMesh>` component is now `MeshletMesh3d`.
The `WithMeshletMesh` type alias has been removed. Use
`With<MeshletMesh3d>` instead.
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Continue migration of bevy APIs to required components, following
guidance of https://hackmd.io/@bevy/required_components/
## Solution
- Make `Sprite` require `Transform` and `Visibility` and
`SyncToRenderWorld`
- move image and texture atlas handles into `Sprite`
- deprecate `SpriteBundle`
- remove engine uses of `SpriteBundle`
## Testing
ran cargo tests on bevy_sprite and tested several sprite examples.
---
## Migration Guide
Replace all uses of `SpriteBundle` with `Sprite`. There are several new
convenience constructors: `Sprite::from_image`,
`Sprite::from_atlas_image`, `Sprite::from_color`.
WARNING: use of `Handle<Image>` and `TextureAtlas` as components on
sprite entities will NO LONGER WORK. Use the fields on `Sprite` instead.
I would have removed the `Component` impls from `TextureAtlas` and
`Handle<Image>` except it is still used within ui. We should fix this
moving forward with the migration.
# Objective
- Closes#15752
Calling the functions `App::observe` and `World::observe` doesn't make
sense because you're not "observing" the `App` or `World`, you're adding
an observer that listens for an event that occurs *within* the `World`.
We should rename them to better fit this.
## Solution
Renames:
- `App::observe` -> `App::add_observer`
- `World::observe` -> `World::add_observer`
- `Commands::observe` -> `Commands::add_observer`
- `EntityWorldMut::observe_entity` -> `EntityWorldMut::observe`
(Note this isn't a breaking change as the original rename was introduced
earlier this cycle.)
## Testing
Reusing current tests.
# Objective
- The `scrolling_fog` example has a camera with the
`TemporalAntiAliasing` component, but it's missing the `Msaa::Off`
component, which leads to this warning being printed on current `main`:
```
WARN bevy_core_pipeline::taa: Temporal anti-aliasing requires MSAA to be disabled
```
## Solution
- This PR adds the `Msaa::Off` component to the example to explicitly
disable MSAA in favor of TAA.
# Objective
Getting closer to the end! Another part of the required components
migration: reflection probes.
## Solution
As per the [proposal added by
Cart](https://hackmd.io/@bevy/required_components/%2FNmpIh0tGSiayGlswbfcEzw)
(Proposal 2), make `LightProbe` require `Transform` and `Visibility`,
and deprecate `ReflectionProbeBundle`.
Note that this proposal wasn't officially blessed yet, but it is the
only existing one that really works, so I implemented it here for
consideration.
## Testing
I ran the reflection probe example, and it appears to work.
---
## Migration Guide
`ReflectionProbeBundle` has been deprecated in favor of inserting the
`LightProbe` and `EnvironmentMapLight` components directly. Inserting
them will now automatically insert `Transform` and `Visibility`
components.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Closes#15717
## Solution
- Wrap the handle in a new wrapper component: `AnimationGraphHandle`.
## Testing
Searched for all instances of `AnimationGraph` in the examples and
updated and tested those
## Migration Guide
`Handle<AnimationGraph>` is no longer a component. Instead, use the
`AnimationGraphHandle` component which contains a
`Handle<AnimationGraph>`.
# Objective
- After #15711 which added a column to the example, the point of a curve
was too close to the next curve
## Solution
- Make it closer to its own
# Objective
Fixes#15560
Fixes (most of) #15570
Currently a lot of examples (and presumably some user code) depend on
toggling certain render features by adding/removing a single component
to an entity, e.g. `SpotLight` to toggle a light. Because of the
retained render world this no longer works: Extract will add any new
components, but when it is removed the entity persists unchanged in the
render world.
## Solution
Add `SyncComponentPlugin<C: Component>` that registers
`SyncToRenderWorld` as a required component for `C`, and adds a
component hook that will clear all components from the render world
entity when `C` is removed. We add this plugin to
`ExtractComponentPlugin` which fixes most instances of the problem. For
custom extraction logic we can manually add `SyncComponentPlugin` for
that component.
We also rename `WorldSyncPlugin` to `SyncWorldPlugin` so we start a
naming convention like all the `Extract` plugins.
In this PR I also fixed a bunch of breakage related to the retained
render world, stemming from old code that assumed that `Entity` would be
the same in both worlds.
I found that using the `RenderEntity` wrapper instead of `Entity` in
data structures when referring to render world entities makes intent
much clearer, so I propose we make this an official pattern.
## Testing
Run examples like
```
cargo run --features pbr_multi_layer_material_textures --example clearcoat
cargo run --example volumetric_fog
```
and see that they work, and that toggles work correctly. But really we
should test every single example, as we might not even have caught all
the breakage yet.
---
## Migration Guide
The retained render world notes should be updated to explain this edge
case and `SyncComponentPlugin`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Trashtalk217 <trashtalk217@gmail.com>
This PR improves the `fps_overlay` example by adding:
1. The ability to increase the overlay font size (previously, only
decreasing the size was supported).
2. A toggle for overlay color between red and green (previously, it only
changed from green to red without toggling back).
# Objective
Simplify the API surrounding easing curves. Broaden the base of types
that support easing.
## Solution
There is now a single library function, `easing_curve`, which constructs
a unit-parametrized easing curve between two values based on an
`EaseFunction`:
```rust
/// Given a `start` and `end` value, create a curve parametrized over [the unit interval]
/// that connects them, using the given [ease function] to determine the form of the
/// curve in between.
///
/// [the unit interval]: Interval::UNIT
/// [ease function]: EaseFunction
pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... }
```
As this shows, the type of the output curve is generic only in `T`. In
particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e.,
a standard "well-behaved" reflectable type), `EasingCurve<T>` is also
`Reflect`, and there is no special field handling nonsense. Therefore,
`EasingCurve` is the kind of thing that would be able to be easily
changed in an editor. This is made possible by storing the actual
`EaseFunction` on `EasingCurve<T>` instead of indirecting through some
kind of function type (which generally leads to issues with reflection).
The types that can be eased are those that implement a trait `Ease`:
```rust
/// A type whose values can be eased between.
///
/// This requires the construction of an interpolation curve that actually extends
/// beyond the curve segment that connects two values, because an easing curve may
/// extrapolate before the starting value and after the ending value. This is
/// especially common in easing functions that mimic elastic or springlike behavior.
pub trait Ease: Sized {
/// Given `start` and `end` values, produce a curve with [unlimited domain]
/// that:
/// - takes a value equivalent to `start` at `t = 0`
/// - takes a value equivalent to `end` at `t = 1`
/// - has constant speed everywhere, including outside of `[0, 1]`
///
/// [unlimited domain]: Interval::EVERYWHERE
fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>;
}
```
(I know, I know, yet *another* interpolation trait. See 'Future
direction'.)
The other existing easing functions from the previous version of this
module have also become new members of `EaseFunction`: `Linear`,
`Steps`, and `Elastic` (which maybe needs a different name). The latter
two are parametrized.
## Testing
Tested using the `easing_functions` example. I also axed the
`cubic_curve` example which was of questionable value and replaced it
with `eased_motion`, which uses this API in the context of animation:
https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2
---
## Future direction
Morally speaking, `Ease` is incredibly similar to `StableInterpolate`.
Probably, we should just merge `StableInterpolate` into `Ease`, and then
make `SmoothNudge` an automatic extension trait of `Ease`. The reason I
didn't do that is that `StableInterpolate` is not implemented for
`VectorSpace` because of concerns about the `Color` types, and I wanted
to avoid controversy. I think that may be a good idea though.
As Alice mentioned before, we should also probably get rid of the
`interpolation` dependency.
The parametrized `Elastic` variant probably also needs some additional
work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep
it.
# Objective
- Closes#15720
## Solution
Wrap the handle in a new wrapper component: `UiMaterialHandle`
It's not possible to match the naming convention of `MeshMaterial3d/2d`
here with the trait already being called `UiMaterial`
Should we consider renaming to `Material3d/2dHandle` and `Mesh3d/2d` to
`Mesh3d/2dHandle`?
- This shouldn't have any merge conflicts with #15591
## Testing
Tested the `ui_material` example
## Migration Guide
Let's defer the migration guide to the required component port. I just
want to yeet the `Component` impl on `Handle` in the meantime :)
# Objective
- Prepare for streaming by storing vertex data per-meshlet, rather than
per-mesh (this means duplicating vertices per-meshlet)
- Compress vertex data to reduce the cost of this
## Solution
The important parts are in from_mesh.rs, the changes to the Meshlet type
in asset.rs, and the changes in meshlet_bindings.wgsl. Everything else
is pretty secondary/boilerplate/straightforward changes.
- Positions are quantized in centimeters with a user-provided power of 2
factor (ideally auto-determined, but that's a TODO for the future),
encoded as an offset relative to the minimum value within the meshlet,
and then stored as a packed list of bits using the minimum number of
bits needed for each vertex position channel for that meshlet
- E.g. quantize positions (lossly, throws away precision that's not
needed leading to using less bits in the bitstream encoding)
- Get the min/max quantized value of each X/Y/Z channel of the quantized
positions within a meshlet
- Encode values relative to the min value of the meshlet. E.g. convert
from [min, max] to [0, max - min]
- The new max value in the meshlet is (max - min), which only takes N
bits, so we only need N bits to store each channel within the meshlet
(lossless)
- We can store the min value and that it takes N bits per channel in the
meshlet metadata, and reconstruct the position from the bitstream
- Normals are octahedral encoded and than snorm2x16 packed and stored as
a single u32.
- Would be better to implement the precise variant of octhedral encoding
for extra precision (no extra decode cost), but decided to keep it
simple for now and leave that as a followup
- Tried doing a quantizing and bitstream encoding scheme like I did for
positions, but struggled to get it smaller. Decided to go with this for
simplicity for now
- UVs are uncompressed and take a full 64bits per vertex which is
expensive
- In the future this should be improved
- Tangents, as of the previous PR, are not explicitly stored and are
instead derived from screen space gradients
- While I'm here, split up MeshletMeshSaverLoader into two separate
types
Other future changes include implementing a smaller encoding of triangle
data (3 u8 indices = 24 bits per triangle currently), and more
disk-oriented compression schemes.
References:
* "A Deep Dive into UE5's Nanite Virtualized Geometry"
https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf#page=128
(also available on youtube)
* "Towards Practical Meshlet Compression"
https://arxiv.org/pdf/2404.06359
* "Vertex quantization in Omniforce Game Engine"
https://daniilvinn.github.io/2024/05/04/omniforce-vertex-quantization.html
## Testing
- Did you test these changes? If so, how?
- Converted the stanford bunny, and rendered it with a debug material
showing normals, and confirmed that it's identical to what's on main.
EDIT: See additional testing in the comments below.
- Are there any parts that need more testing?
- Could use some more size comparisons on various meshes, and testing
different quantization factors. Not sure if 4 is a good default. EDIT:
See additional testing in the comments below.
- Also did not test runtime performance of the shaders. EDIT: See
additional testing in the comments below.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Use my unholy script, replacing the meshlet example
https://paste.rs/7xQHk.rs (must make MeshletMesh fields pub instead of
pub crate, must add lz4_flex as a dev-dependency) (must compile with
meshlet and meshlet_processor features, mesh must have only positions,
normals, and UVs, no vertex colors or tangents)
---
## Migration Guide
- TBD by JMS55 at the end of the release
# Objective
UI box shadow support
Adds a new component `BoxShadow`:
```rust
pub struct BoxShadow {
/// The shadow's color
pub color: Color,
/// Horizontal offset
pub x_offset: Val,
/// Vertical offset
pub y_offset: Val,
/// Horizontal difference in size from the occluding uninode
pub spread_radius: Val,
/// Blurriness of the shadow
pub blur_radius: Val,
}
```
To use `BoxShadow`, add the component to any Bevy UI node and a shadow
will be drawn beneath that node.
Also adds a resource `BoxShadowSamples` that can be used to adjust the
shadow quality.
#### Notes
* I'm not super happy with the field names. Maybe we need a `struct Size
{ width: Val, height: Val }` type or something.
* The shader isn't very optimised but I don't see that it's too
important for now as the number of shadows being rendered is not going
to be massive most of the time. I think it's more important to get the
API and geometry correct with this PR.
* I didn't implement an inset property, it's not essential and can
easily be added in a follow up.
* Shadows are only rendered for uinodes, not for images or text.
* Batching isn't supported, it would need out-of-the-scope-of-this-pr
changes to the way the UI handles z-ordering for it to be effective.
# Showcase
```cargo run --example box_shadow -- --samples 4```
<img width="391" alt="br" src="https://github.com/user-attachments/assets/4e8add96-dc93-46e0-9e35-d995eb0943ad">
```cargo run --example box_shadow -- --samples 10```
<img width="391" alt="s10"
src="https://github.com/user-attachments/assets/ecb384c9-4012-4cd6-9dea-5180904bf28e">
## Objective
Add a way to stream BRP requests when the data changes.
## Solution
#### BRP Side (reusable for other transports)
Add a new method handler type that returns a optional value. This
handler is run in update and if a value is returned it will be sent on
the message channel. Custom watching handlers can be added with
`RemotePlugin::with_watching_method`.
#### HTTP Side
If a request comes in with `+watch` in the method, it will respond with
`text/event-stream` rather than a single response.
## Testing
I tested with the podman HTTP client. This client has good support for
SSE's if you want to test it too.
## Parts I want some opinions on
- For separating watching methods I chose to add a `+watch` suffix to
the end kind of like `content-type` headers. A get would be
`bevy/get+watch`.
- Should watching methods send an initial response with everything or
only respond when a change happens? Currently the later is what happens.
## Future work
- The `bevy/query` method would also benefit from this but that
condition will be quite complex so I will leave that to later.
---------
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
# Objective
Several of our APIs (namely gizmos and bounding) use isometries on
current Bevy main. This is nicer than separate properties in a lot of
cases, but users have still expressed usability concerns.
One problem is that in a lot of cases, you only care about e.g.
translation, so you end up with this:
```rust
gizmos.cross_2d(
Isometry2d::from_translation(Vec2::new(-160.0, 120.0)),
12.0,
FUCHSIA,
);
```
The isometry adds quite a lot of length and verbosity, and isn't really
that relevant since only the translation is important here.
It would be nice if you could use the translation directly, and only
supply an isometry if both translation and rotation are needed. This
would make the following possible:
```rust
gizmos.cross_2d(Vec2::new(-160.0, 120.0), 12.0, FUCHSIA);
```
removing a lot of verbosity.
## Solution
Implement `From<Vec2>` and `From<Rot2>` for `Isometry2d`, and
`From<Vec3>`, `From<Vec3A>`, and `From<Quat>` for `Isometry3d`. These
are lossless conversions that fit the semantics of `From`.
This makes the proposed API possible! The methods must now simply take
an `impl Into<IsometryNd>`, and this works:
```rust
gizmos.cross_2d(Vec2::new(-160.0, 120.0), 12.0, FUCHSIA);
```
# Objective
- Adds better comments and includes an example where the aspect ratio
between `size` and `full_size` differ
- Fixes#15576
## Solution
- Viewports are dynamically scaled to window size
## Testing
- Tested with moving the window around and by manually setting the
window scaling factor
- Just to make sure nothing else is going on, someone on macOS should
also test this
## Showcase
Since calculating padding from window size is a hassle, the example now
looks a bit more squished together:
![image](https://github.com/user-attachments/assets/68609cf2-5a67-49bd-8e0b-910bfd17f4d8)
# Objective
Enhance the [custom skinned mesh
example](https://bevyengine.org/examples/animation/custom-skinned-mesh/)
to show some variety and clarify what the transform does to the mesh.
## Solution
https://github.com/user-attachments/assets/c919db74-6e77-4f33-ba43-0f40a88042b3
Add variety and clarity with the following changes:
- vary transform changes,
- use a UV texture,
- and show transform changes via gizmos.
(Maybe it'd be worth turning on wireframe rendering to show what happens
to the mesh. I think it'd be nice visually but might make the code a
little noisy.)
## Testing
I exercised it on my x86 macOS computer. It'd be good to have it
validated on Windows, Linux, and WASM.
---
## Showcase
- Custom skinned mesh example varies the transforms changes and uses a
UV test texture.
# Objective
- Alpha blending can easily fail in many situations and requires sorting
on the cpu
## Solution
- Implement order independent transparency (OIT) as an alternative to
alpha blending
- The implementation uses 2 passes
- The first pass records all the fragments colors and position to a
buffer that is the size of N layers * the render target resolution.
- The second pass sorts the fragments, blends them and draws them to the
screen. It also currently does manual depth testing because early-z
fails in too many cases in the first pass.
## Testing
- We've been using this implementation at foresight in production for
many months now and we haven't had any issues related to OIT.
---
## Showcase
![image](https://github.com/user-attachments/assets/157f3e32-adaf-4782-b25b-c10313b9bc43)
![image](https://github.com/user-attachments/assets/bef23258-0c22-4b67-a0b8-48a9f571c44f)
## Future work
- Add an example showing how to use OIT for a custom material
- Next step would be to implement a per-pixel linked list to reduce
memory use
- I'd also like to investigate using a BinnedRenderPhase instead of a
SortedRenderPhase. If it works, it would make the transparent pass
significantly faster.
---------
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
Co-authored-by: Charlotte McElwain <charlotte.c.mcelwain@gmail.com>
This example was missed during the port to required components for
meshes and materials.
Easy fix, I checked that it works as it did in the PR that added the
example (#13912).
# Objective
- Followup to #15675
- Add an example showcasing the functions
## Solution
- Add an example showcasing the functions
- Some of the functions from the interpolation crate are messed up,
fixed in #15706
![ease](https://github.com/user-attachments/assets/1f3b2b80-23d2-45c7-8b08-95b2e870aa02)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
- Rename `Pickable` to `PickingBehavior` to counter the easily-made
assumption that the component is required. It is optional
- Fix and clarify documentation
- The docs in `crates/bevy_ui/src/picking_backend.rs` were incorrect
about the necessity of `Pickable`
- Plus two minor code quality changes in this commit
(7c2e75f48d)
Closes#15632
# Objective
After merging retained rendering world #15320, we now have a good way of
creating a link between worlds (*HIYAA intensifies*). This means that
`get_or_spawn` is no longer necessary for that function. Entity should
be opaque as the warning above `get_or_spawn` says. This is also part of
#15459.
I'm deprecating `get_or_spawn_batch` in a different PR in order to keep
the PR small in size.
## Solution
Deprecate `get_or_spawn` and replace it with `get_entity` in most
contexts. If it's possible to query `&RenderEntity`, then the entity is
synced and `render_entity.id()` is initialized in the render world.
## Migration Guide
If you are given an `Entity` and you want to do something with it, use
`Commands.entity(...)` or `World.entity(...)`. If instead you want to
spawn something use `Commands.spawn(...)` or `World.spawn(...)`. If you
are not sure if an entity exists, you can always use `get_entity` and
match on the `Option<...>` that is returned.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
If you want to draw / generate images from the CPU, such as:
- to create procedurally-generated assets
- for games whose artstyle is best implemented by poking pixels directly
from the CPU, instead of using shaders
It is currently very unergonomic to do in Bevy, because you have to deal
with the raw bytes inside `image.data`, take care of the pixel format,
etc.
## Solution
This PR adds some helper methods to `Image` for pixel manipulation.
These methods allow you to use Bevy's user-friendly `Color` struct to
read and write the colors of pixels, at arbitrary coordinates (specified
as `UVec3` to support any texture dimension). They handle
encoding/decoding to the `Image`s `TextureFormat`, incl. any sRGB
conversion.
While we are at it, also add methods to help with direct access to the
raw bytes. It is now easy to compute the offset where the bytes of a
specific pixel coordinate are found, or to just get a Rust slice to
access them.
Caveat: `Color` roundtrips are obviously going to be lossy for non-float
`TextureFormat`s. Using `set_color_at` followed by `get_color_at` will
return a different value, due to the data conversions involved (such as
`f32` -> `u8` -> `f32` for the common `Rgba8UnormSrgb` texture format).
Be careful when comparing colors (such as checking for a color you wrote
before)!
Also adding a new example: `cpu_draw` (under `2d`), to showcase these
new APIs.
---
## Changelog
### Added
- `Image` APIs for easy access to the colors of specific pixels.
---------
Co-authored-by: Pascal Hertleif <killercup@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: ltdk <usr@ltdk.xyz>
Wayland only supports pre-multiplied alpha. Behavior on X11 seems
unchanged.
# Objective
- Fix#10929 on wayland.
## Solution
- Request pre-multiplied alpha.
## Testing
- Ran the example locally.
# Objective
- `bevy_render` should not depend on `bevy_winit`
- Fixes#15565
## Solution
- `bevy_render` no longer depends on `bevy_winit`
- The following is behind the `custom_cursor` feature
- Move custom cursor code from `bevy_render` to `bevy_winit` behind the
`custom_cursor` feature
- `bevy_winit` now depends on `bevy_render` (for `Image` and
`TextureFormat`)
- `bevy_winit` now depends on `bevy_asset` (for `Assets`, `Handle` and
`AssetId`)
- `bevy_winit` now depends on `bytemuck` (already in tree)
- Custom cursor code in `bevy_winit` reworked to use `AssetId` (other
than that it is taken over 1:1)
- Rework `bevy_winit` custom cursor interface visibility now that the
logic is all contained in `bevy_winit`
## Testing
- I ran the screenshot and window_settings examples
- Tested on linux wayland so far
---
## Migration Guide
`CursorIcon` and `CustomCursor` previously provided by
`bevy::render::view::cursor` is now available from `bevy::winit`.
A new feature `custom_cursor` enables this functionality (default
feature).
# Objective
Add support for events that can be triggered from animation clips. This
is useful when you need something to happen at a specific time in an
animation. For example, playing a sound every time a characters feet
hits the ground when walking.
Closes#15494
## Solution
Added a new field to `AnimationClip`: `events`, which contains a list of
`AnimationEvent`s. These are automatically triggered in
`animate_targets` and `trigger_untargeted_animation_events`.
## Testing
Added a couple of tests and example (`animation_events.rs`) to make sure
events are triggered when expected.
---
## Showcase
`Events` need to also implement `AnimationEvent` and `Reflect` to be
used with animations.
```rust
#[derive(Event, AnimationEvent, Reflect)]
struct SomeEvent;
```
Events can be added to an `AnimationClip` by specifying a time and
event.
```rust
// trigger an event after 1.0 second
animation_clip.add_event(1.0, SomeEvent);
```
And optionally, providing a target id.
```rust
let id = AnimationTargetId::from_iter(["shoulder", "arm", "hand"]);
animation_clip.add_event_to_target(id, 1.0, HandEvent);
```
I modified the `animated_fox` example to show off the feature.
![CleanShot 2024-10-05 at 02 41
57](https://github.com/user-attachments/assets/0bb47db7-24f9-4504-88f1-40e375b89b1b)
---------
Co-authored-by: Matty <weatherleymatthew@gmail.com>
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Add a background colour to each text node in the `text_debug` example to
visualize their bounds.
## Showcase
<img width="961" alt="deb"
src="https://github.com/user-attachments/assets/deec3e15-b0f0-411f-9af1-597587ac2a83">
In the bottom right you can see the empty space at the bottom of the
text node, making it much more obvious that there is a bug causing the
size of the bounds to be calculated incorrectly.
# Objective
Yet another PR for migrating stuff to required components. This time,
cameras!
## Solution
As per the [selected
proposal](https://hackmd.io/tsYID4CGRiWxzsgawzxG_g#Combined-Proposal-1-Selected),
deprecate `Camera2dBundle` and `Camera3dBundle` in favor of `Camera2d`
and `Camera3d`.
Adding a `Camera` without `Camera2d` or `Camera3d` now logs a warning,
as suggested by Cart [on
Discord](https://discord.com/channels/691052431525675048/1264881140007702558/1291506402832945273).
I would personally like cameras to work a bit differently and be split
into a few more components, to avoid some footguns and confusing
semantics, but that is more controversial, and shouldn't block this core
migration.
## Testing
I ran a few 2D and 3D examples, and tried cameras with and without
render graphs.
---
## Migration Guide
`Camera2dBundle` and `Camera3dBundle` have been deprecated in favor of
`Camera2d` and `Camera3d`. Inserting them will now also insert the other
components required by them automatically.
*Additive blending* is an ubiquitous feature in game engines that allows
animations to be concatenated instead of blended. The canonical use case
is to allow a character to hold a weapon while performing arbitrary
poses. For example, if you had a character that needed to be able to
walk or run while attacking with a weapon, the typical workflow is to
have an additive blend node that combines walking and running animation
clips with an animation clip of one of the limbs performing a weapon
attack animation.
This commit adds support for additive blending to Bevy. It builds on top
of the flexible infrastructure in #15589 and introduces a new type of
node, the *add node*. Like blend nodes, add nodes combine the animations
of their children according to their weights. Unlike blend nodes,
however, add nodes don't normalize the weights to 1.0.
The `animation_masks` example has been overhauled to demonstrate the use
of additive blending in combination with masks. There are now controls
to choose an animation clip for every limb of the fox individually.
This patch also fixes a bug whereby masks were incorrectly accumulated
with `insert()` during the graph threading phase, which could cause
corruption of computed masks in some cases.
Note that the `clip` field has been replaced with an `AnimationNodeType`
enum, which breaks `animgraph.ron` files. The `Fox.animgraph.ron` asset
has been updated to the new format.
Closes#14395.
## Showcase
https://github.com/user-attachments/assets/52dfe05f-fdb3-477a-9462-ec150f93df33
## Migration Guide
* The `animgraph.ron` format has changed to accommodate the new
*additive blending* feature. You'll need to change `clip` fields to
instances of the new `AnimationNodeType` enum.
## Solution
- Removed superfluous `Pickable` components
- Slightly simplified the code for updating the text color
- Removed the `Pointer<Click>` observer from the mesh entirely since
that doesn't support picking yet
# Objective
System param validation warnings should be configurable and default to
"warn once" (per system).
Fixes: #15391
## Solution
`SystemMeta` is given a new `ParamWarnPolicy` field.
The policy decides whether warnings will be emitted by each system param
when it fails validation.
The policy is updated by the system after param validation fails.
Example warning:
```
2024-09-30T18:10:04.740749Z WARN bevy_ecs::system::function_system: System fallible_params::do_nothing_fail_validation will not run because it requested inaccessible system parameter Single<(), (With<Player>, With<Enemy>)>
```
Currently, only the first invalid parameter is displayed.
Warnings can be disabled on function systems using
`.param_never_warn()`.
(there is also `.with_param_warn_policy(policy)`)
## Testing
Ran `fallible_params` example.
---------
Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
This is an updated version of #15530. Review comments were addressed.
This commit changes the animation graph evaluation to be operate in a
more sensible order and updates the semantics of blend nodes to conform
to [the animation composition RFC]. Prior to this patch, a node graph
like this:
```
┌─────┐
│ │
│ 1 │
│ │
└──┬──┘
│
┌───────┴───────┐
│ │
▼ ▼
┌─────┐ ┌─────┐
│ │ │ │
│ 2 │ │ 3 │
│ │ │ │
└──┬──┘ └──┬──┘
│ │
┌───┴───┐ ┌───┴───┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │ │ │
│ 4 │ │ 6 │ │ 5 │ │ 7 │
│ │ │ │ │ │ │ │
└─────┘ └─────┘ └─────┘ └─────┘
```
Would be evaluated as (((4 ⊕ 5) ⊕ 6) ⊕ 7), with the blend (lerp/slerp)
operation notated as ⊕. As quaternion multiplication isn't commutative,
this is very counterintuitive and will especially lead to trouble with
the forthcoming additive blending feature (#15198).
This patch fixes the issue by changing the evaluation order to
postorder, with children of a node evaluated in ascending order by node
index.
To do so, this patch revamps `AnimationCurve` to be based on an
*evaluation stack* and a *blend register*. During target evaluation, the
graph evaluator traverses the graph in postorder. When encountering a
clip node, the evaluator pushes the possibly-interpolated value onto the
evaluation stack. When encountering a blend node, the evaluator pops
values off the stack into the blend register, accumulating weights as
appropriate. When the graph is completely evaluated, the top element on
the stack is *committed* to the property of the component.
A new system, the *graph threading* system, is added in order to cache
the sorted postorder traversal to avoid the overhead of sorting children
at animation evaluation time. Mask evaluation has been moved to this
system so that the graph only has to be traversed at most once per
frame. Unlike the `ActiveAnimation` list, the *threaded graph* is cached
from frame to frame and only has to be regenerated when the animation
graph asset changes.
This patch currently regresses the `animate_target` performance in
`many_foxes` by around 50%, resulting in an FPS loss of about 2-3 FPS.
I'd argue that this is an acceptable price to pay for a much more
intuitive system. In the future, we can mitigate the regression with a
fast path that avoids consulting the graph if only one animation is
playing. However, in the interest of keeping this patch simple, I didn't
do so here.
[the animation composition RFC]:
https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md
# Objective
- Describe the objective or issue this PR addresses.
- If you're fixing a specific issue, say "Fixes #X".
## Solution
- Describe the solution used to achieve the objective above.
## 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
> This section is optional. If this PR does not include a visual change
or does not add a new feature, you can delete this section.
- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR adds a new feature or public API, consider adding a brief
pseudo-code snippet of it in action
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- If you want, you could even include a before/after comparison!
- If the Migration Guide adequately covers the changes, you can delete
this section
While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:
<details>
<summary>Click to view showcase</summary>
```rust
println!("My super cool code.");
```
</details>
## Migration Guide
> This section is optional. If there are no breaking changes, you can
delete this section.
- If this PR is a breaking change (relative to the last release of
Bevy), describe how a user might need to migrate their code to support
these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable
design choice is not a breaking change.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
The components needed for `DirectionalLight` are added automatically
since #15554
`create_point_light` already existed and returns a `PointLight` with
these same settings
Early implementation. I still have to fix the documentation and consider
writing a small migration guide.
Questions left to answer:
* [x] should thickness be an overridable constant?
* [x] is there a better way to implement `Eq`/`Hash` for `SSAOMethod`?
* [x] do we want to keep the linear sampler for the depth texture?
* [x] is there a better way to separate the logic than preprocessor
macros?
![vbao](https://github.com/bevyengine/bevy/assets/4136413/2a8a0389-2add-4c2e-be37-e208e52dcd25)
## Migration guide
SSAO algorithm was changed from GTAO to VBAO (visibility bitmasks). A
new field, `constant_object_thickness`, was added to
`ScreenSpaceAmbientOcclusion`. `ScreenSpaceAmbientOcclusion` also lost
its `Eq` and `Hash` implementations.
---------
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
As discussed in #15521
- Partial revert of #14897, reverting the change to the methods to
consume `self`
- The `insert_if` method is kept
The migration guide of #14897 should be removed
Closes#15521
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Fixes#14826
- For context, see #15238
## Solution
Add a `GhostNode` component to `bevy_ui` and update all the relevant
systems to use it to traverse for UI children.
- [x] `ghost_hierarchy` module
- [x] Add `GhostNode`
- [x] Add `UiRootNodes` system param for iterating (ghost-aware) UI root
nodes
- [x] Add `UiChildren` system param for iterating (ghost-aware) UI
children
- [x] Update `layout::ui_layout_system`
- [x] Use ghost-aware root nodes for camera updates
- [x] Update and remove children in taffy
- [x] Initial spawn
- [x] Detect changes on nested UI children
- [x] Use ghost-aware children traversal in
`update_uinode_geometry_recursive`
- [x] Update the rest of the UI systems to use the ghost hierarchy
- [x] `stack::ui_stack_system`
- [x] `update::`
- [x] `update_clipping_system`
- [x] `update_target_camera_system`
- [x] `accessibility::calc_name`
## Testing
- [x] Added a new example `ghost_nodes` that can be used as a testbed.
- [x] Added unit tests for _some_ of the traversal utilities in
`ghost_hierarchy`
- [x] Ensure this fulfills the needs for currently known use cases
- [x] Reactivity libraries (test with `bevy_reactor`)
- [ ] Text spans (mentioned by koe [on
discord](https://discord.com/channels/691052431525675048/1285371432460881991/1285377442998915246))
---
## Performance
[See comment
below](https://github.com/bevyengine/bevy/pull/15341#issuecomment-2385456820)
## Migration guide
Any code that previously relied on `Parent`/`Children` to iterate UI
children may now want to use `bevy_ui::UiChildren` to ensure ghost nodes
are skipped, and their first descendant Nodes included.
UI root nodes may now be children of ghost nodes, which means
`Without<Parent>` might not query all root nodes. Use
`bevy_ui::UiRootNodes` where needed to iterate root nodes instead.
## Potential future work
- Benchmarking/optimizations of hierarchies containing lots of ghost
nodes
- Further exploration of UI hierarchies and markers for root nodes/leaf
nodes to create better ergonomics for things like `UiLayer` (world-space
ui)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
Again, a step forward in the migration to required components: a bunch
of camera rendering cormponents!
Note that this does not include the camera components themselves yet,
because the naming and API for `Camera` hasn't been fully decided yet.
## Solution
As per the [selected
proposals](https://hackmd.io/@bevy/required_components/%2FpiqD9GOdSFKZZGzzh3C7Uw):
- Deprecate `MotionBlurBundle` in favor of the `MotionBlur` component
- Deprecate `TemporalAntiAliasBundle` in favor of the
`TemporalAntiAliasing` component
- Deprecate `ScreenSpaceAmbientOcclusionBundle` in favor of the
`ScreenSpaceAmbientOcclusion` component
- Deprecate `ScreenSpaceReflectionsBundle` in favor of the
`ScreenSpaceReflections` component
---
## Migration Guide
`MotionBlurBundle`, `TemporalAntiAliasBundle`,
`ScreenSpaceAmbientOcclusionBundle`, and `ScreenSpaceReflectionsBundle`
have been deprecated in favor of the `MotionBlur`,
`TemporalAntiAliasing`, `ScreenSpaceAmbientOcclusion`, and
`ScreenSpaceReflections` components instead. Inserting them will now
also insert the other components required by them automatically.
# Objective
What's that? Another PR for the grand migration to required components?
This time, audio!
## Solution
Deprecate `AudioSourceBundle`, `AudioBundle`, and `PitchBundle`, as per
the [chosen
proposal](https://hackmd.io/@bevy/required_components/%2Fzxgp-zMMRUCdT7LY1ZDQwQ).
However, we cannot call the component `AudioSource`, because that's what
the stored asset is called. I deliberated on a few names, like
`AudioHandle`, or even just `Audio`, but landed on `AudioPlayer`, since
it's probably the most accurate and "nice" name for this. Open to
alternatives though.
---
## Migration Guide
Replace all insertions of `AudioSoucreBundle`, `AudioBundle`, and
`PitchBundle` with the `AudioPlayer` component. The other components
required by it will now be inserted automatically.
In cases where the generics cannot be inferred, you may need to specify
them explicitly. For example:
```rust
commands.spawn(AudioPlayer::<AudioSource>(asset_server.load("sounds/sick_beats.ogg")));
```
# Objective
A step in the migration to required components: scenes!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2FPJtNGVMMQhyM0zIvCJSkbA):
- Deprecate `SceneBundle` and `DynamicSceneBundle`.
- Add `SceneRoot` and `DynamicSceneRoot` components, which wrap a
`Handle<Scene>` and `Handle<DynamicScene>` respectively.
## Migration Guide
Asset handles for scenes and dynamic scenes must now be wrapped in the
`SceneRoot` and `DynamicSceneRoot` components. Raw handles as components
no longer spawn scenes.
Additionally, `SceneBundle` and `DynamicSceneBundle` have been
deprecated. Instead, use the scene components directly.
Previously:
```rust
let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf"));
commands.spawn(SceneBundle {
scene: model_scene,
transform: Transform::from_xyz(-4.0, 0.0, -3.0),
..default()
});
```
Now:
```rust
let model_scene = asset_server.load(GltfAssetLabel::Scene(0).from_asset("model.gltf"));
commands.spawn((
SceneRoot(model_scene),
Transform::from_xyz(-4.0, 0.0, -3.0),
));
```
# Objective
- Improve code quality in preparation for
https://github.com/bevyengine/bevy/discussions/15014
## Solution
- Rename BreakLineOn to LineBreak.
## Migration Guide
`BreakLineOn` was renamed to `LineBreak`, and paramters named
`linebreak_behavior` were renamed to `linebreak`.
# Objective
Add two features to switch bevy to use `NativeActivity` or
`GameActivity` on Android, use `GameActivity` by default.
Also close #12058 and probably #12026 .
## Solution
Add two features to the corresponding crates so you can toggle it, like
what `winit` and `android-activity` crate did.
---
## Changelog
Removed default `NativeActivity` feature implementation for Android,
added two new features to enable `NativeActivity` and `GameActivity`,
and use `GameActivity` by default.
## Migration Guide
Because `cargo-apk` is not compatible with `GameActivity`,
building/running using `cargo apk build/run -p bevy_mobile_example` is
no longer possible.
Users should follow the new workflow described in document.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
Co-authored-by: Rich Churcher <rich.churcher@gmail.com>
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Another part of the migration to required components: fog volumes!
## Solution
Deprecate `FogVolumeBundle` and make `FogVolume` require `Transform` and
`Visibility`, as per the [chosen
proposal](https://hackmd.io/@bevy/required_components/%2FcO7JPSAQR5G0J_j5wNwtOQ).
---
## Migration Guide
Replace all insertions of `FogVolumeBundle` with the `Visibility`
component. The other components required by it will now be inserted
automatically.
# Objective
The `NestedLoader` API as it stands right now is somewhat lacking:
- It consists of several types `NestedLoader`, `UntypedNestedLoader`,
`DirectNestedLoader`, and `UntypedDirectNestedLoader`, where a typestate
pattern on `NestedLoader` would be make it more obvious what it does,
and allow centralising the documentation
- The term "untyped" in the asset loader code is overloaded. It can mean
either:
- we have literally no idea what the type of this asset will be when we
load it (I dub this "unknown type")
- we know what type of asset it will be, but we don't know it statically
- we only have a TypeId (I dub this "dynamic type" / "erased")
- There is no way to get an `UntypedHandle` (erased) given a `TypeId`
## Solution
Changes `NestedLoader` into a type-state pattern, adding two type
params:
- `T` determines the typing
- `StaticTyped`, the default, where you pass in `A` statically into `fn
load<A>() -> ..`
- `DynamicTyped`, where you give a `TypeId`, giving you a
`UntypedHandle`
- `UnknownTyped`, where you have literally no idea what type of asset
you're loading, giving you a `Handle<LoadedUntypedAsset>`
- `M` determines the "mode" (bikeshedding TBD, I couldn't come up with a
better name)
- `Deferred`, the default, won't load the asset when you call `load`,
but it does give you a `Handle` to it (this is nice since it can be a
sync fn)
- `Immediate` will load the asset as soon as you call it, and give you
access to it, but you must be in an async context to call it
Changes some naming of internals in `AssetServer` to fit the new
definitions of "dynamic type" and "unknown type". Note that I didn't do
a full pass over this code to keep the diff small. That can probably be
done in a new PR - I think the definiton I laid out of unknown type vs.
erased makes it pretty clear where each one applies.
<details>
<summary>Old issue</summary>
The only real problem I have with this PR is the requirement to pass in
`type_name` (from `core::any::type_name`) into Erased. Users might not
have that type name, only the ID, and it just seems sort of weird to
*have* to give an asset type name. However, the reason we need it is
because of this:
```rs
pub(crate) fn get_or_create_path_handle_erased(
&mut self,
path: AssetPath<'static>,
type_id: TypeId,
type_name: &str,
loading_mode: HandleLoadingMode,
meta_transform: Option<MetaTransform>,
) -> (UntypedHandle, bool) {
let result = self.get_or_create_path_handle_internal(
path,
Some(type_id),
loading_mode,
meta_transform,
);
// it is ok to unwrap because TypeId was specified above
unwrap_with_context(result, type_name).unwrap()
}
pub(crate) fn unwrap_with_context<T>(
result: Result<T, GetOrCreateHandleInternalError>,
type_name: &str,
) -> Option<T> {
match result {
Ok(value) => Some(value),
Err(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified) => None,
Err(GetOrCreateHandleInternalError::MissingHandleProviderError(_)) => {
panic!("Cannot allocate an Asset Handle of type '{type_name}' because the asset type has not been initialized. \
Make sure you have called app.init_asset::<{type_name}>()")
}
}
}
```
This `unwrap_with_context` is literally the only reason we need the
`type_name`. Potentially, this can be turned into an `impl
Into<Option<&str>>`, and output a different error message if the type
name is missing. Since if we are loading an asset where we only know the
type ID, by definition we can't output that error message, since we
don't have the type name. I'm open to suggestions on this.
</details>
## Testing
Not sure how to test this, since I kept most of the actual NestedLoader
logic the same. The only new API is loading an `UntypedHandle` when in
the `DynamicTyped, Immediate` state.
## Migration Guide
Code which uses `bevy_asset`'s `LoadContext::loader` / `NestedLoader`
will see some naming changes:
- `untyped` is replaced by `with_unknown_type`
- `with_asset_type` is replaced by `with_static_type`
- `with_asset_type_id` is replaced by `with_dynamic_type`
- `direct` is replaced by `immediate` (the opposite of "immediate" is
"deferred")
# Objective
- This PR fixes#12488
## Solution
- This PR adds a new property to `Camera` that emulates the
functionality of the
[setViewOffset()](https://threejs.org/docs/#api/en/cameras/PerspectiveCamera.setViewOffset)
API in three.js.
- When set, the perspective and orthographic projections will restrict
the visible area of the camera to a part of the view frustum defined by
`offset` and `size`.
## Testing
- In the new `camera_sub_view` example, a fixed, moving and control sub
view is created for both perspective and orthographic projection
- Run the example with `cargo run --example camera_sub_view`
- The code can be tested by adding a `SubCameraView` to a camera
---
## Showcase
![image](https://github.com/user-attachments/assets/75ac45fc-d75d-4664-8ef6-ff7865297c25)
- Left Half: Perspective Projection
- Right Half: Orthographic Projection
- Small boxes in order:
- Sub view of the left half of the full image
- Sub view moving from the top left to the bottom right of the full
image
- Sub view of the full image (acting as a control)
- Large box: No sub view
<details>
<summary>Shortened camera setup of `camera_sub_view` example</summary>
```rust
// Main perspective Camera
commands.spawn(Camera3dBundle {
transform,
..default()
});
// Perspective camera left half
commands.spawn(Camera3dBundle {
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view camera to the left half of the full image
full_size: uvec2(500, 500),
offset: ivec2(0, 0),
size: uvec2(250, 500),
}),
order: 1,
..default()
},
transform,
..default()
});
// Perspective camera moving
commands.spawn((
Camera3dBundle {
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view camera to a fifth of the full view and
// move it in another system
full_size: uvec2(500, 500),
offset: ivec2(0, 0),
size: uvec2(100, 100),
}),
order: 2,
..default()
},
transform,
..default()
},
MovingCameraMarker,
));
// Perspective camera control
commands.spawn(Camera3dBundle {
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view to the full image, to ensure that it matches
// the projection without sub view
full_size: uvec2(450, 450),
offset: ivec2(0, 0),
size: uvec2(450, 450),
}),
order: 3,
..default()
},
transform,
..default()
});
// Main orthographic camera
commands.spawn(Camera3dBundle {
projection: OrthographicProjection {
...
}
.into(),
camera: Camera {
order: 4,
..default()
},
transform,
..default()
});
// Orthographic camera left half
commands.spawn(Camera3dBundle {
projection: OrthographicProjection {
...
}
.into(),
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view camera to the left half of the full image
full_size: uvec2(500, 500),
offset: ivec2(0, 0),
size: uvec2(250, 500),
}),
order: 5,
..default()
},
transform,
..default()
});
// Orthographic camera moving
commands.spawn((
Camera3dBundle {
projection: OrthographicProjection {
...
}
.into(),
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view camera to a fifth of the full view and
// move it in another system
full_size: uvec2(500, 500),
offset: ivec2(0, 0),
size: uvec2(100, 100),
}),
order: 6,
..default()
},
transform,
..default()
},
MovingCameraMarker,
));
// Orthographic camera control
commands.spawn(Camera3dBundle {
projection: OrthographicProjection {
...
}
.into(),
camera: Camera {
sub_camera_view: Some(SubCameraView {
// Set the sub view to the full image, to ensure that it matches
// the projection without sub view
full_size: uvec2(450, 450),
offset: ivec2(0, 0),
size: uvec2(450, 450),
}),
order: 7,
..default()
},
transform,
..default()
});
```
</details>
# Objective
Another step in the migration to required components: lights!
Note that this does not include `EnvironmentMapLight` or reflection
probes yet, because their API hasn't been fully chosen yet.
## Solution
As per the [selected
proposals](https://hackmd.io/@bevy/required_components/%2FLLnzwz9XTxiD7i2jiUXkJg):
- Deprecate `PointLightBundle` in favor of the `PointLight` component
- Deprecate `SpotLightBundle` in favor of the `PointLight` component
- Deprecate `DirectionalLightBundle` in favor of the `DirectionalLight`
component
## Testing
I ran some examples with lights.
---
## Migration Guide
`PointLightBundle`, `SpotLightBundle`, and `DirectionalLightBundle` have
been deprecated. Use the `PointLight`, `SpotLight`, and
`DirectionalLight` components instead. Adding them will now insert the
other components required by them automatically.
# Objective
Fixes#15541
A bunch of lifetimes were added during the Assets V2 rework, but after
moving to async traits in #12550 they can be elided. That PR mentions
that this might be the case, but apparently it wasn't followed up on at
the time.
~~I ended up grepping for `<'a` and finding a similar case in
`bevy_reflect` which I also fixed.~~ (edit: that one was needed
apparently)
Note that elided lifetimes are unstable in `impl Trait`. If that gets
stabilized then we can elide even more.
## Solution
Remove the extra lifetimes.
## Testing
Everything still compiles. If I have messed something up there is a
small risk that some user code stops compiling, but all the examples
still work at least.
---
## Migration Guide
The traits `AssetLoader`, `AssetSaver` and `Process` traits from
`bevy_asset` now use elided lifetimes. If you implement these then
remove the named lifetime.
# Objective
This PR extends and reworks the material from #15282 by allowing
arbitrary curves to be used by the animation system to animate arbitrary
properties. The goals of this work are to:
- Allow far greater flexibility in how animations are allowed to be
defined in order to be used with `bevy_animation`.
- Delegate responsibility over keyframe interpolation to `bevy_math` and
the `Curve` libraries and reduce reliance on keyframes in animation
definitions generally.
- Move away from allowing the glTF spec to completely define animations
on a mechanical level.
## Solution
### Overview
At a high level, curves have been incorporated into the animation system
using the `AnimationCurve` trait (closely related to what was
`Keyframes`). From the top down:
1. In `animate_targets`, animations are driven by `VariableCurve`, which
is now a thin wrapper around a `Box<dyn AnimationCurve>`.
2. `AnimationCurve` is something built out of a `Curve`, and it tells
the animation system how to use the curve's output to actually mutate
component properties. The trait looks like this:
```rust
/// A low-level trait that provides control over how curves are actually applied to entities
/// by the animation system.
///
/// Typically, this will not need to be implemented manually, since it is automatically
/// implemented by [`AnimatableCurve`] and other curves used by the animation system
/// (e.g. those that animate parts of transforms or morph weights). However, this can be
/// implemented manually when `AnimatableCurve` is not sufficiently expressive.
///
/// In many respects, this behaves like a type-erased form of [`Curve`], where the output
/// type of the curve is remembered only in the components that are mutated in the
/// implementation of [`apply`].
///
/// [`apply`]: AnimationCurve::apply
pub trait AnimationCurve: Reflect + Debug + Send + Sync {
/// Returns a boxed clone of this value.
fn clone_value(&self) -> Box<dyn AnimationCurve>;
/// The range of times for which this animation is defined.
fn domain(&self) -> Interval;
/// Write the value of sampling this curve at time `t` into `transform` or `entity`,
/// as appropriate, interpolating between the existing value and the sampled value
/// using the given `weight`.
fn apply<'a>(
&self,
t: f32,
transform: Option<Mut<'a, Transform>>,
entity: EntityMutExcept<'a, (Transform, AnimationPlayer, Handle<AnimationGraph>)>,
weight: f32,
) -> Result<(), AnimationEvaluationError>;
}
```
3. The conversion process from a `Curve` to an `AnimationCurve` involves
using wrappers which communicate the intent to animate a particular
property. For example, here is `TranslationCurve`, which wraps a
`Curve<Vec3>` and uses it to animate `Transform::translation`:
```rust
/// This type allows a curve valued in `Vec3` to become an [`AnimationCurve`] that animates
/// the translation component of a transform.
pub struct TranslationCurve<C>(pub C);
```
### Animatable Properties
The `AnimatableProperty` trait survives in the transition, and it can be
used to allow curves to animate arbitrary component properties. The
updated documentation for `AnimatableProperty` explains this process:
<details>
<summary>Expand AnimatableProperty example</summary
An `AnimatableProperty` is a value on a component that Bevy can animate.
You can implement this trait on a unit struct in order to support
animating
custom components other than transforms and morph weights. Use that type
in
conjunction with `AnimatableCurve` (and perhaps
`AnimatableKeyframeCurve`
to define the animation itself). For example, in order to animate font
size of a
text section from 24 pt. to 80 pt., you might use:
```rust
#[derive(Reflect)]
struct FontSizeProperty;
impl AnimatableProperty for FontSizeProperty {
type Component = Text;
type Property = f32;
fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> {
Some(&mut component.sections.get_mut(0)?.style.font_size)
}
}
```
You can then create an `AnimationClip` to animate this property like so:
```rust
let mut animation_clip = AnimationClip::default();
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableKeyframeCurve::new(
[
(0.0, 24.0),
(1.0, 80.0),
]
)
.map(AnimatableCurve::<FontSizeProperty, _>::from_curve)
.expect("Failed to create font size curve")
);
```
Here, the use of `AnimatableKeyframeCurve` creates a curve out of the
given keyframe time-value
pairs, using the `Animatable` implementation of `f32` to interpolate
between them. The
invocation of `AnimatableCurve::from_curve` with `FontSizeProperty`
indicates that the `f32`
output from that curve is to be used to animate the font size of a
`Text` component (as
configured above).
</details>
### glTF Loading
glTF animations are now loaded into `Curve` types of various kinds,
depending on what is being animated and what interpolation mode is being
used. Those types get wrapped into and converted into `Box<dyn
AnimationCurve>` and shoved inside of a `VariableCurve` just like
everybody else.
### Morph Weights
There is an `IterableCurve` abstraction which allows sampling these from
a contiguous buffer without allocating. Its only reason for existing is
that Rust disallows you from naming function types, otherwise we would
just use `Curve` with an iterator output type. (The iterator involves
`Map`, and the name of the function type would have to be able to be
named, but it is not.)
A `WeightsCurve` adaptor turns an `IterableCurve` into an
`AnimationCurve`, so it behaves like everything else in that regard.
## Testing
Tested by running existing animation examples. Interpolation logic has
had additional tests added within the `Curve` API to replace the tests
in `bevy_animation`. Some kinds of out-of-bounds errors have become
impossible.
Performance testing on `many_foxes` (`animate_targets`) suggests that
performance is very similar to the existing implementation. Here are a
couple trace histograms across different runs (yellow is this branch,
red is main).
<img width="669" alt="Screenshot 2024-09-27 at 9 41 50 AM"
src="https://github.com/user-attachments/assets/5ba4e9ac-3aea-452e-aaf8-1492acc2d7fc">
<img width="673" alt="Screenshot 2024-09-27 at 9 45 18 AM"
src="https://github.com/user-attachments/assets/8982538b-04cf-46b5-97b2-164c6bc8162e">
---
## Migration Guide
Most user code that does not directly deal with `AnimationClip` and
`VariableCurve` will not need to be changed. On the other hand,
`VariableCurve` has been completely overhauled. If you were previously
defining animation curves in code using keyframes, you will need to
migrate that code to use curve constructors instead. For example, a
rotation animation defined using keyframes and added to an animation
clip like this:
```rust
animation_clip.add_curve_to_target(
animation_target_id,
VariableCurve {
keyframe_timestamps: vec![0.0, 1.0, 2.0, 3.0, 4.0],
keyframes: Keyframes::Rotation(vec![
Quat::IDENTITY,
Quat::from_axis_angle(Vec3::Y, PI / 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
Quat::IDENTITY,
]),
interpolation: Interpolation::Linear,
},
);
```
would now be added like this:
```rust
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableKeyframeCurve::new([0.0, 1.0, 2.0, 3.0, 4.0].into_iter().zip([
Quat::IDENTITY,
Quat::from_axis_angle(Vec3::Y, PI / 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 2.),
Quat::from_axis_angle(Vec3::Y, PI / 2. * 3.),
Quat::IDENTITY,
]))
.map(RotationCurve)
.expect("Failed to build rotation curve"),
);
```
Note that the interface of `AnimationClip::add_curve_to_target` has also
changed (as this example shows, if subtly), and now takes its curve
input as an `impl AnimationCurve`. If you need to add a `VariableCurve`
directly, a new method `add_variable_curve_to_target` accommodates that
(and serves as a one-to-one migration in this regard).
### For reviewers
The diff is pretty big, and the structure of some of the changes might
not be super-obvious:
- `keyframes.rs` became `animation_curves.rs`, and `AnimationCurve` is
based heavily on `Keyframes`, with the adaptors also largely following
suite.
- The Curve API adaptor structs were moved from `bevy_math::curve::mod`
into their own module `adaptors`. There are no functional changes to how
these adaptors work; this is just to make room for the specialized
reflection implementations since `mod.rs` was getting kind of cramped.
- The new module `gltf_curves` holds the additional curve constructions
that are needed by the glTF loader. Note that the loader uses a mix of
these and off-the-shelf `bevy_math` curve stuff.
- `animatable.rs` no longer holds logic related to keyframe
interpolation, which is now delegated to the existing abstractions in
`bevy_math::curve::cores`.
---------
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
- Adopted from #14449
- Still fixes#12144.
## Migration Guide
The retained render world is a complex change: migrating might take one
of a few different forms depending on the patterns you're using.
For every example, we specify in which world the code is run. Most of
the changes affect render world code, so for the average Bevy user who's
using Bevy's high-level rendering APIs, these changes are unlikely to
affect your code.
### Spawning entities in the render world
Previously, if you spawned an entity with `world.spawn(...)`,
`commands.spawn(...)` or some other method in the rendering world, it
would be despawned at the end of each frame. In 0.15, this is no longer
the case and so your old code could leak entities. This can be mitigated
by either re-architecting your code to no longer continuously spawn
entities (like you're used to in the main world), or by adding the
`bevy_render::world_sync::TemporaryRenderEntity` component to the entity
you're spawning. Entities tagged with `TemporaryRenderEntity` will be
removed at the end of each frame (like before).
### Extract components with `ExtractComponentPlugin`
```
// main world
app.add_plugins(ExtractComponentPlugin::<ComponentToExtract>::default());
```
`ExtractComponentPlugin` has been changed to only work with synced
entities. Entities are automatically synced if `ComponentToExtract` is
added to them. However, entities are not "unsynced" if any given
`ComponentToExtract` is removed, because an entity may have multiple
components to extract. This would cause the other components to no
longer get extracted because the entity is not synced.
So be careful when only removing extracted components from entities in
the render world, because it might leave an entity behind in the render
world. The solution here is to avoid only removing extracted components
and instead despawn the entire entity.
### Manual extraction using `Extract<Query<(Entity, ...)>>`
```rust
// in render world, inspired by bevy_pbr/src/cluster/mod.rs
pub fn extract_clusters(
mut commands: Commands,
views: Extract<Query<(Entity, &Clusters, &Camera)>>,
) {
for (entity, clusters, camera) in &views {
// some code
commands.get_or_spawn(entity).insert(...);
}
}
```
One of the primary consequences of the retained rendering world is that
there's no longer a one-to-one mapping from entity IDs in the main world
to entity IDs in the render world. Unlike in Bevy 0.14, Entity 42 in the
main world doesn't necessarily map to entity 42 in the render world.
Previous code which called `get_or_spawn(main_world_entity)` in the
render world (`Extract<Query<(Entity, ...)>>` returns main world
entities). Instead, you should use `&RenderEntity` and
`render_entity.id()` to get the correct entity in the render world. Note
that this entity does need to be synced first in order to have a
`RenderEntity`.
When performing manual abstraction, this won't happen automatically
(like with `ExtractComponentPlugin`) so add a `SyncToRenderWorld` marker
component to the entities you want to extract.
This results in the following code:
```rust
// in render world, inspired by bevy_pbr/src/cluster/mod.rs
pub fn extract_clusters(
mut commands: Commands,
views: Extract<Query<(&RenderEntity, &Clusters, &Camera)>>,
) {
for (render_entity, clusters, camera) in &views {
// some code
commands.get_or_spawn(render_entity.id()).insert(...);
}
}
// in main world, when spawning
world.spawn(Clusters::default(), Camera::default(), SyncToRenderWorld)
```
### Looking up `Entity` ids in the render world
As previously stated, there's now no correspondence between main world
and render world `Entity` identifiers.
Querying for `Entity` in the render world will return the `Entity` id in
the render world: query for `MainEntity` (and use its `id()` method) to
get the corresponding entity in the main world.
This is also a good way to tell the difference between synced and
unsynced entities in the render world, because unsynced entities won't
have a `MainEntity` component.
---------
Co-authored-by: re0312 <re0312@outlook.com>
Co-authored-by: re0312 <45868716+re0312@users.noreply.github.com>
Co-authored-by: Periwink <charlesbour@gmail.com>
Co-authored-by: Anselmo Sampietro <ans.samp@gmail.com>
Co-authored-by: Emerson Coskey <56370779+ecoskey@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
# Objective
`ui_stack_system` generates a tree of `StackingContexts` which it then
flattens to get the `UiStack`.
But there's no need to construct a new tree. We can query for nodes with
a global `ZIndex`, add those nodes to the root nodes list and then build
the `UiStack` from a walk of the existing layout tree, ignoring any
branches that have a global `Zindex`.
Fixes#9877
## Solution
Split the `ZIndex` enum into two separate components, `ZIndex` and
`GlobalZIndex`
Query for nodes with a `GlobalZIndex`, add those nodes to the root nodes
list and then build the `UiStack` from a walk of the existing layout
tree, filtering branches by `Without<GlobalZIndex>` so we don't revisit
nodes.
```
cargo run --profile stress-test --features trace_tracy --example many_buttons
```
<img width="672" alt="ui-stack-system-walk-split-enum"
src="https://github.com/bevyengine/bevy/assets/27962798/11e357a5-477f-4804-8ada-c4527c009421">
(Yellow is this PR, red is main)
---
## Changelog
`Zindex`
* The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZIndex` values are equal.
`ui_stack_system`
* Instead of generating a tree of `StackingContexts`, query for nodes
with a `GlobalZIndex`, add those nodes to the root nodes list and then
build the `UiStack` from a walk of the existing layout tree, filtering
branches by `Without<GlobalZIndex` so we don't revisit nodes.
## Migration Guide
The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZindex` values are equal.
---------
Co-authored-by: Gabriel Bourgeois <gabriel.bourgeoisv4si@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
# Objective
Add a `Populated` system parameter that acts like `Query`, but prevents
system from running if there are no matching entities.
Fixes: #15302
## Solution
Implement the system param which newtypes the `Query`.
The only change is new validation, which fails if query is empty.
The new system param is used in `fallible_params` example.
## Testing
Ran `fallible_params` example.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- The shader_instancing example can be misleading since it doesn't
explain that bevy has built in automatic instancing.
## Solution
- Explain that bevy has built in instancing and that this example is for
advanced users.
- Add a new automatic_instancing example that shows how to use the built
in automatic instancing
- Rename the shader_instancing example to custom_shader_instancing to
highlight that this is a more advanced implementation
---------
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
# Objective
Adds a new `Readback` component to request for readback of a
`Handle<Image>` or `Handle<ShaderStorageBuffer>` to the CPU in a future
frame.
## Solution
We track the `Readback` component and allocate a target buffer to write
the gpu resource into and map it back asynchronously, which then fires a
trigger on the entity in the main world. This proccess is asynchronous,
and generally takes a few frames.
## Showcase
```rust
let mut buffer = ShaderStorageBuffer::from(vec![0u32; 16]);
buffer.buffer_description.usage |= BufferUsages::COPY_SRC;
let buffer = buffers.add(buffer);
commands
.spawn(Readback::buffer(buffer.clone()))
.observe(|trigger: Trigger<ReadbackComplete>| {
info!("Buffer data from previous frame {:?}", trigger.event());
});
```
---------
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
# Objective
Mostly covers the first point in
https://github.com/bevyengine/bevy/issues/13713#issuecomment-2364786694
The idea here is that a lot of people want to load their own texture
atlases, and many of them do this by deserializing some custom version
of `TextureAtlasLayout`. This makes that a little easier by providing
`serde` impls for them.
## Solution
In order to make `TextureAtlasLayout` serializable, the custom texture
mappings that are added by `TextureAtlasBuilder` were separated into
their own type, `TextureAtlasSources`. The inner fields are made public
so people can create their own version of this type, although because it
embeds asset IDs, it's not as easily serializable. In particular,
atlases that are loaded directly (e.g. sprite sheets) will not have a
copy of this map, and so, don't need to construct it at all.
As an aside, since this is the very first thing in `bevy_sprite` with
`serde` impls, I've added a `serialize` feature to the crate and made
sure it gets activated when the `serialize` feature is enabled on the
parent `bevy` crate.
## Testing
I was kind of shocked that there isn't anywhere in the code besides a
single example that actually used this functionality, so, it was
relatively straightforward to do.
In #13713, among other places, folks have mentioned adding custom
serialization into their pipelines. It would be nice to hear from people
whether this change matches what they're doing in their code, and if
it's relatively seamless to adapt to. I suspect that the answer is yes,
but, that's mainly the only other kind of testing that can be added.
## Migration Guide
`TextureAtlasBuilder` no longer stores a mapping back to the original
images in `TextureAtlasLayout`; that functionality has been added to a
new struct, `TextureAtlasSources`, instead. This also means that the
signature for `TextureAtlasBuilder::finish` has changed, meaning that
calls of the form:
```rust
let (atlas_layout, image) = builder.build()?;
```
Will now change to the form:
```rust
let (atlas_layout, atlas_sources, image) = builder.build()?;
```
And instead of performing a reverse-lookup from the layout, like so:
```rust
let atlas_layout_handle = texture_atlases.add(atlas_layout.clone());
let index = atlas_layout.get_texture_index(&my_handle);
let handle = TextureAtlas {
layout: atlas_layout_handle,
index,
};
```
You can perform the lookup from the sources instead:
```rust
let atlas_layout = texture_atlases.add(atlas_layout);
let index = atlas_sources.get_texture_index(&my_handle);
let handle = TextureAtlas {
layout: atlas_layout,
index,
};
```
Additionally, `TextureAtlasSources` also has a convenience method,
`handle`, which directly combines the index and an existing
`TextureAtlasLayout` handle into a new `TextureAtlas`:
```rust
let atlas_layout = texture_atlases.add(atlas_layout);
let handle = atlas_sources.handle(atlas_layout, &my_handle);
```
## Extra notes
In the future, it might make sense to combine the three types returned
by `TextureAtlasBuilder` into their own struct, just so that people
don't need to assign variable names to all three parts. In particular,
when creating a version that can be loaded directly (like #11873), we
could probably use this new type.
# Objective
- fixes https://github.com/bevyengine/bevy/issues/13473
## Solution
- When a single mesh is assigned multiple materials, it is divided into
several primitive nodes, with each primitive assigned a unique material.
Presently, these primitives are named using the format Mesh.index, which
complicates querying. To improve this, we can assign a specific name to
each primitive based on the material’s name, since each primitive
corresponds to one material exclusively.
## Testing
- I have included a simple example which shows how to query a material
and mesh part based on the new name component.
## Changelog
- adds `GltfMaterialName` component to the mesh entity of the gltf
primitive node.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fixes#15394
## Solution
Observers now validate params.
System registry has a new error variant for when system running fails
due to invalid parameters.
Run once now returns a `Result<Out, RunOnceError>` instead of `Out`.
This is more inline with system registry, which also returns a result.
I'll address warning messages in #15500.
## Testing
Added one test for each case.
---
## Migration Guide
- `RunSystemOnce::run_system_once` and
`RunSystemOnce::run_system_once_with` now return a `Result<Out>` instead
of just `Out`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
* Save 16 bytes per vertex by calculating tangents in the shader at
runtime, rather than storing them in the vertex data.
* Based on https://jcgt.org/published/0009/03/04,
https://www.jeremyong.com/graphics/2023/12/16/surface-gradient-bump-mapping.
* Fixed visbuffer resolve to use the updated algorithm that flips ddy
correctly
* Added some more docs about meshlet material limitations, and some
TODOs about transforming UV coordinates for the future.
![image](https://github.com/user-attachments/assets/222d8192-8c82-4d77-945d-53670a503761)
For testing add a normal map to the bunnies with StandardMaterial like
below, and then test that on both main and this PR (make sure to
download the correct bunny for each). Results should be mostly
identical.
```rust
normal_map_texture: Some(asset_server.load_with_settings(
"textures/BlueNoise-Normal.png",
|settings: &mut ImageLoaderSettings| settings.is_srgb = false,
)),
```
# Objective
Add the following system params:
- `QuerySingle<D, F>` - Valid if only one matching entity exists,
- `Option<QuerySingle<D, F>>` - Valid if zero or one matching entity
exists.
As @chescock pointed out, we don't need `Mut` variants.
Fixes: #15264
## Solution
Implement the type and both variants of system params.
Also implement `ReadOnlySystemParam` for readonly queries.
Added a new ECS example `fallible_params` which showcases `SingleQuery`
usage.
In the future we might want to add `NonEmptyQuery`,
`NonEmptyEventReader` and `Res` to it (or maybe just stop at mentioning
it).
## Testing
Tested with the example.
There is a lot of warning spam so we might want to implement #15391.
## Objective
Closes#15408 (somewhat)
## Solution
- Moved the existing HTTP transport to its own module with its own
plugin (`RemoteHttpPlugin`) (disabled on WASM)
- Swapped out the `smol` crate for the smaller crates it re-exports to
make it easier to keep out non-wasm code (HTTP transport needs
`async-io` which can't build on WASM)
- Added a new public `BrpSender` resource holding the matching sender
for the `BrpReceiver`' (formally `BrpMailbox`). This allows other crates
to send `BrpMessage`'s to the "mailbox".
## Testing
TODO
---------
Co-authored-by: Matty <weatherleymatthew@gmail.com>
# Objective
- Significantly improve the ergonomics of gamepads and allow new
features
Gamepads are a bit unergonomic to work with, they use resources but
unlike other inputs, they are not limited to a single gamepad, to get
around this it uses an identifier (Gamepad) to interact with anything
causing all sorts of issues.
1. There are too many: Gamepads, GamepadSettings, GamepadInfo,
ButtonInput<T>, 2 Axis<T>.
2. ButtonInput/Axis generic methods become really inconvenient to use
e.g. any_pressed()
3. GamepadButton/Axis structs are unnecessary boilerplate:
```rust
for gamepad in gamepads.iter() {
if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::South)) {
info!("{:?} just pressed South", gamepad);
} else if button_inputs.just_released(GamepadButton::new(gamepad, GamepadButtonType::South))
{
info!("{:?} just released South", gamepad);
}
}
```
4. Projects often need to create resources to store the selected gamepad
and have to manually check if their gamepad is still valid anyways.
- Previously attempted by #3419 and #12674
## Solution
- Implement gamepads as entities.
Using entities solves all the problems above and opens new
possibilities.
1. Reduce boilerplate and allows iteration
```rust
let is_pressed = gamepads_buttons.iter().any(|buttons| buttons.pressed(GamepadButtonType::South))
```
2. ButtonInput/Axis generic methods become ergonomic again
```rust
gamepad_buttons.any_just_pressed([GamepadButtonType::Start, GamepadButtonType::Select])
```
3. Reduces the number of public components significantly (Gamepad,
GamepadSettings, GamepadButtons, GamepadAxes)
4. Components are highly convenient. Gamepad optional features could now
be expressed naturally (`Option<Rumble> or Option<Gyro>`), allows devs
to attach their own components and filter them, so code like this
becomes possible:
```rust
fn move_player<const T: usize>(
player: Query<&Transform, With<Player<T>>>,
gamepads_buttons: Query<&GamepadButtons, With<Player<T>>>,
) {
if let Ok(gamepad_buttons) = gamepads_buttons.get_single() {
if gamepad_buttons.pressed(GamepadButtonType::South) {
// move player
}
}
}
```
---
## Follow-up
- [ ] Run conditions?
- [ ] Rumble component
# Changelog
## Added
TODO
## Changed
TODO
## Removed
TODO
## Migration Guide
TODO
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
The next step in the migration to required components: Deprecate
`VisibilityBundle` and make `Visibility` require `InheritedVisibility`
and `ViewVisibility`, as per the [chosen
proposal](https://hackmd.io/@bevy/required_components/%2FcO7JPSAQR5G0J_j5wNwtOQ).
## Solution
Deprecate `VisibilityBundle` and make `Visibility` require
`InheritedVisibility` and `ViewVisibility`.
I chose not to deprecate `SpatialBundle` yet, as doing so would mean
that we need to manually add `Visibility` to a bunch of places. It will
be nicer once meshes, sprites, lights, fog, and cameras have been
migrated, since they will require `Transform` and `Visibility` and
therefore not need manually added defaults for them.
---
## Migration Guide
Replace all insertions of `VisibilityBundle` with the `Visibility`
component. The other components required by it will now be inserted
automatically.
The first step in the migration to required components! This PR removes
`GlobalTransform` from all user-facing code, since it's now added
automatically wherever `Transform` is used.
## Testing
- None of the examples I tested were broken, and I assume breaking
transforms in any way would be visible *everywhere*
---
## Changelog
- Make `Transform` require `GlobalTransform`
~~- Remove `GlobalTransform` from all engine bundles~~
- Remove in-engine insertions of GlobalTransform and TransformBundle
- Deprecate `TransformBundle`
- update docs to reflect changes
## Migration Guide
Replace all insertions of `GlobalTransform` and/or `TransformBundle`
with `Transform` alone.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Tim <JustTheCoolDude@gmail.com>
# Objective
- Fixes#6370
- Closes#6581
## Solution
- Added the following lints to the workspace:
- `std_instead_of_core`
- `std_instead_of_alloc`
- `alloc_instead_of_core`
- Used `cargo +nightly fmt` with [item level use
formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A)
to split all `use` statements into single items.
- Used `cargo clippy --workspace --all-targets --all-features --fix
--allow-dirty` to _attempt_ to resolve the new linting issues, and
intervened where the lint was unable to resolve the issue automatically
(usually due to needing an `extern crate alloc;` statement in a crate
root).
- Manually removed certain uses of `std` where negative feature gating
prevented `--all-features` from finding the offending uses.
- Used `cargo +nightly fmt` with [crate level use
formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A)
to re-merge all `use` statements matching Bevy's previous styling.
- Manually fixed cases where the `fmt` tool could not re-merge `use`
statements due to conditional compilation attributes.
## Testing
- Ran CI locally
## Migration Guide
The MSRV is now 1.81. Please update to this version or higher.
## Notes
- This is a _massive_ change to try and push through, which is why I've
outlined the semi-automatic steps I used to create this PR, in case this
fails and someone else tries again in the future.
- Making this change has no impact on user code, but does mean Bevy
contributors will be warned to use `core` and `alloc` instead of `std`
where possible.
- This lint is a critical first step towards investigating `no_std`
options for Bevy.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes#15142
## Solution
* Moved all the UI border geometry calculations that were scattered
through the UI extraction functions into `ui_layout_system`.
* Added a `border: BorderRect` field to `Node` to store the border size
computed by `ui_layout_system`.
* Use the border values returned from Taffy rather than calculate them
ourselves during extraction.
* Removed the `logical_rect` and `physical_rect` methods from `Node` the
descriptions and namings are deceptive, it's better to create the rects
manually instead.
* Added a method `outline_radius` to `Node` that calculates the border
radius of outlines.
* For border values `ExtractedUiNode` takes `BorderRect` and
`ResolvedBorderRadius` now instead of raw `[f32; 4]` values and converts
them in `prepare_uinodes`.
* Removed some unnecessary scaling and clamping of border values
(#15142).
* Added a `BorderRect::ZERO` constant.
* Added an `outlined_node_size` method to `Node`.
## Testing
Added some non-uniform borders to the border example. Everything seems
to be in order:
<img width="626" alt="nub"
src="https://github.com/user-attachments/assets/258ed8b5-1a9e-4ac5-99c2-6bf25c0ef31c">
## Migration Guide
The `logical_rect` and `physical_rect` methods have been removed from
`Node`. Use `Rect::from_center_size` with the translation and node size
instead.
The types of the fields border and border_radius of `ExtractedUiNode`
have been changed to `BorderRect` and `ResolvedBorderRadius`
respectively.
---------
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Co-authored-by: akimakinai <105044389+akimakinai@users.noreply.github.com>
# Objective
- Fixes#15451
## Migration Guide
- `World::init_component` has been renamed to `register_component`.
- `World::init_component_with_descriptor` has been renamed to
`register_component_with_descriptor`.
- `World::init_bundle` has been renamed to `register_bundle`.
- `Components::init_component` has been renamed to `register_component`.
- `Components::init_component_with_descriptor` has been renamed to
`register_component_with_descriptor`.
- `Components::init_resource` has been renamed to `register_resource`.
- `Components::init_non_send` had been renamed to `register_non_send`.
# Objective
Fixes the confusion that caused #5660
## Solution
Make it clear that it is the hardware which doesn't support the format
and not bevy's fault.
# Objective
Fixes#15401
## Solution
Changes the scroll inversion hotkey in the example from Shift to
Control.
Shift is idiomatic for this. Since we cannot use Shift per #15401, I
picked another modifier arbitrarily. A production app would handle this
in a platform specific way until the platform behaviors are unified
upstream, but no point here.
## Testing
I don't have a mac readily available for testing, if someone wouldn't
mind testing. I would also appreciate confirmation that trackpad is
working nicely.
# Objective
Adopted from #13563.
The goal is to implement the Bevy Remote Protocol over HTTP/JSON,
allowing the ECS to be interacted with remotely.
## Solution
At a high level, there are really two separate things that have been
undertaken here:
1. First, `RemotePlugin` has been created, which has the effect of
embedding a [JSON-RPC](https://www.jsonrpc.org/specification) endpoint
into a Bevy application.
2. Second, the [Bevy Remote Protocol
verbs](https://gist.github.com/coreh/1baf6f255d7e86e4be29874d00137d1d#file-bevy-remote-protocol-md)
(excluding `POLL`) have been implemented as remote methods for that
JSON-RPC endpoint under a Bevy-exclusive namespace (e.g. `bevy/get`,
`bevy/list`, etc.).
To avoid some repetition, here is the crate-level documentation, which
explains the request/response structure, built-in-methods, and custom
method configuration:
<details>
<summary>Click to view crate-level docs</summary>
```rust
//! An implementation of the Bevy Remote Protocol over HTTP and JSON, to allow
//! for remote control of a Bevy app.
//!
//! Adding the [`RemotePlugin`] to your [`App`] causes Bevy to accept
//! connections over HTTP (by default, on port 15702) while your app is running.
//! These *remote clients* can inspect and alter the state of the
//! entity-component system. Clients are expected to `POST` JSON requests to the
//! root URL; see the `client` example for a trivial example of use.
//!
//! The Bevy Remote Protocol is based on the JSON-RPC 2.0 protocol.
//!
//! ## Request objects
//!
//! A typical client request might look like this:
//!
//! ```json
//! {
//! "method": "bevy/get",
//! "id": 0,
//! "params": {
//! "entity": 4294967298,
//! "components": [
//! "bevy_transform::components::transform::Transform"
//! ]
//! }
//! }
//! ```
//!
//! The `id` and `method` fields are required. The `param` field may be omitted
//! for certain methods:
//!
//! * `id` is arbitrary JSON data. The server completely ignores its contents,
//! and the client may use it for any purpose. It will be copied via
//! serialization and deserialization (so object property order, etc. can't be
//! relied upon to be identical) and sent back to the client as part of the
//! response.
//!
//! * `method` is a string that specifies one of the possible [`BrpRequest`]
//! variants: `bevy/query`, `bevy/get`, `bevy/insert`, etc. It's case-sensitive.
//!
//! * `params` is parameter data specific to the request.
//!
//! For more information, see the documentation for [`BrpRequest`].
//! [`BrpRequest`] is serialized to JSON via `serde`, so [the `serde`
//! documentation] may be useful to clarify the correspondence between the Rust
//! structure and the JSON format.
//!
//! ## Response objects
//!
//! A response from the server to the client might look like this:
//!
//! ```json
//! {
//! "jsonrpc": "2.0",
//! "id": 0,
//! "result": {
//! "bevy_transform::components::transform::Transform": {
//! "rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
//! "scale": { "x": 1.0, "y": 1.0, "z": 1.0 },
//! "translation": { "x": 0.0, "y": 0.5, "z": 0.0 }
//! }
//! }
//! }
//! ```
//!
//! The `id` field will always be present. The `result` field will be present if the
//! request was successful. Otherwise, an `error` field will replace it.
//!
//! * `id` is the arbitrary JSON data that was sent as part of the request. It
//! will be identical to the `id` data sent during the request, modulo
//! serialization and deserialization. If there's an error reading the `id` field,
//! it will be `null`.
//!
//! * `result` will be present if the request succeeded and will contain the response
//! specific to the request.
//!
//! * `error` will be present if the request failed and will contain an error object
//! with more information about the cause of failure.
//!
//! ## Error objects
//!
//! An error object might look like this:
//!
//! ```json
//! {
//! "code": -32602,
//! "message": "Missing \"entity\" field"
//! }
//! ```
//!
//! The `code` and `message` fields will always be present. There may also be a `data` field.
//!
//! * `code` is an integer representing the kind of an error that happened. Error codes documented
//! in the [`error_codes`] module.
//!
//! * `message` is a short, one-sentence human-readable description of the error.
//!
//! * `data` is an optional field of arbitrary type containing additional information about the error.
//!
//! ## Built-in methods
//!
//! The Bevy Remote Protocol includes a number of built-in methods for accessing and modifying data
//! in the ECS. Each of these methods uses the `bevy/` prefix, which is a namespace reserved for
//! BRP built-in methods.
//!
//! ### bevy/get
//!
//! Retrieve the values of one or more components from an entity.
//!
//! `params`:
//! - `entity`: The ID of the entity whose components will be fetched.
//! - `components`: An array of fully-qualified type names of components to fetch.
//!
//! `result`: A map associating each type name to its value on the requested entity.
//!
//! ### bevy/query
//!
//! Perform a query over components in the ECS, returning all matching entities and their associated
//! component values.
//!
//! All of the arrays that comprise this request are optional, and when they are not provided, they
//! will be treated as if they were empty.
//!
//! `params`:
//! `params`:
//! - `data`:
//! - `components` (optional): An array of fully-qualified type names of components to fetch.
//! - `option` (optional): An array of fully-qualified type names of components to fetch optionally.
//! - `has` (optional): An array of fully-qualified type names of components whose presence will be
//! reported as boolean values.
//! - `filter` (optional):
//! - `with` (optional): An array of fully-qualified type names of components that must be present
//! on entities in order for them to be included in results.
//! - `without` (optional): An array of fully-qualified type names of components that must *not* be
//! present on entities in order for them to be included in results.
//!
//! `result`: An array, each of which is an object containing:
//! - `entity`: The ID of a query-matching entity.
//! - `components`: A map associating each type name from `components`/`option` to its value on the matching
//! entity if the component is present.
//! - `has`: A map associating each type name from `has` to a boolean value indicating whether or not the
//! entity has that component. If `has` was empty or omitted, this key will be omitted in the response.
//!
//! ### bevy/spawn
//!
//! Create a new entity with the provided components and return the resulting entity ID.
//!
//! `params`:
//! - `components`: A map associating each component's fully-qualified type name with its value.
//!
//! `result`:
//! - `entity`: The ID of the newly spawned entity.
//!
//! ### bevy/destroy
//!
//! Despawn the entity with the given ID.
//!
//! `params`:
//! - `entity`: The ID of the entity to be despawned.
//!
//! `result`: null.
//!
//! ### bevy/remove
//!
//! Delete one or more components from an entity.
//!
//! `params`:
//! - `entity`: The ID of the entity whose components should be removed.
//! - `components`: An array of fully-qualified type names of components to be removed.
//!
//! `result`: null.
//!
//! ### bevy/insert
//!
//! Insert one or more components into an entity.
//!
//! `params`:
//! - `entity`: The ID of the entity to insert components into.
//! - `components`: A map associating each component's fully-qualified type name with its value.
//!
//! `result`: null.
//!
//! ### bevy/reparent
//!
//! Assign a new parent to one or more entities.
//!
//! `params`:
//! - `entities`: An array of entity IDs of entities that will be made children of the `parent`.
//! - `parent` (optional): The entity ID of the parent to which the child entities will be assigned.
//! If excluded, the given entities will be removed from their parents.
//!
//! `result`: null.
//!
//! ### bevy/list
//!
//! List all registered components or all components present on an entity.
//!
//! When `params` is not provided, this lists all registered components. If `params` is provided,
//! this lists only those components present on the provided entity.
//!
//! `params` (optional):
//! - `entity`: The ID of the entity whose components will be listed.
//!
//! `result`: An array of fully-qualified type names of components.
//!
//! ## Custom methods
//!
//! In addition to the provided methods, the Bevy Remote Protocol can be extended to include custom
//! methods. This is primarily done during the initialization of [`RemotePlugin`], although the
//! methods may also be extended at runtime using the [`RemoteMethods`] resource.
//!
//! ### Example
//! ```ignore
//! fn main() {
//! App::new()
//! .add_plugins(DefaultPlugins)
//! .add_plugins(
//! // `default` adds all of the built-in methods, while `with_method` extends them
//! RemotePlugin::default()
//! .with_method("super_user/cool_method".to_owned(), path::to::my:🆒:handler)
//! // ... more methods can be added by chaining `with_method`
//! )
//! .add_systems(
//! // ... standard application setup
//! )
//! .run();
//! }
//! ```
//!
//! The handler is expected to be a system-convertible function which takes optional JSON parameters
//! as input and returns a [`BrpResult`]. This means that it should have a type signature which looks
//! something like this:
//! ```
//! # use serde_json::Value;
//! # use bevy_ecs::prelude::{In, World};
//! # use bevy_remote::BrpResult;
//! fn handler(In(params): In<Option<Value>>, world: &mut World) -> BrpResult {
//! todo!()
//! }
//! ```
//!
//! Arbitrary system parameters can be used in conjunction with the optional `Value` input. The
//! handler system will always run with exclusive `World` access.
//!
//! [the `serde` documentation]: https://serde.rs/
```
</details>
### Message lifecycle
At a high level, the lifecycle of client-server interactions is
something like this:
1. The client sends one or more `BrpRequest`s. The deserialized version
of that is just the Rust representation of a JSON-RPC request, and it
looks like this:
```rust
pub struct BrpRequest {
/// The action to be performed. Parsing is deferred for the sake of error reporting.
pub method: Option<Value>,
/// Arbitrary data that will be returned verbatim to the client as part of
/// the response.
pub id: Option<Value>,
/// The parameters, specific to each method.
///
/// These are passed as the first argument to the method handler.
/// Sometimes params can be omitted.
pub params: Option<Value>,
}
```
2. These requests are accumulated in a mailbox resource (small lie but
close enough).
3. Each update, the mailbox is drained by a system
`process_remote_requests`, where each request is processed according to
its `method`, which has an associated handler. Each handler is a Bevy
system that runs with exclusive world access and returns a result; e.g.:
```rust
pub fn process_remote_get_request(In(params): In<Option<Value>>, world: &World) -> BrpResult { // ... }
```
4. The result (or an error) is reported back to the client.
## Testing
This can be tested by using the `server` and `client` examples. The
`client` example is not particularly exhaustive at the moment (it only
creates barebones `bevy/query` requests) but is still informative. Other
queries can be made using `curl` with the `server` example running.
For example, to make a `bevy/list` request and list all registered
components:
```bash
curl -X POST -d '{ "jsonrpc": "2.0", "id": 1, "method": "bevy/list" }' 127.0.0.1:15702 | jq .
```
---
## Future direction
There were a couple comments on BRP versioning while this was in draft.
I agree that BRP versioning is a good idea, but I think that it requires
some consensus on a couple fronts:
- First of all, what does the version actually mean? Is it a version for
the protocol itself or for the `bevy/*` methods implemented using it?
Both?
- Where does the version actually live? The most natural place is just
where we have `"jsonrpc"` right now (at least if it's versioning the
protocol itself), but this means we're not actually conforming to
JSON-RPC any more (so, for example, any client library used to construct
JSON-RPC requests would stop working). I'm not really against that, but
it's at least a real decision.
- What do we actually do when we encounter mismatched versions? Adding
handling for this would be actual scope creep instead of just a little
add-on in my opinion.
Another thing that would be nice is making the internal structure of the
implementation less JSON-specific. Right now, for example, component
values that will appear in server responses are quite eagerly converted
to JSON `Value`s, which prevents disentangling the handler logic from
the communication medium, but it can probably be done in principle and I
imagine it would enable more code reuse (e.g. for custom method
handlers) in addition to making the internals more readily usable for
other formats.
---------
Co-authored-by: Patrick Walton <pcwalton@mimiga.net>
Co-authored-by: DragonGamesStudios <margos.michal@gmail.com>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective
Fixes#14331
## Solution
- Make `Traversal` a subtrait of `ReadOnlyQueryData`
- Update implementations and usages
## Testing
- Updated unit tests
## Migration Guide
Update implementations of `Traversal`.
---------
Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
# Objective
Currently, the term "value" in the context of reflection is a bit
overloaded.
For one, it can be used synonymously with "data" or "variable". An
example sentence would be "this function takes a reflected value".
However, it is also used to refer to reflected types which are
`ReflectKind::Value`. These types are usually either primitives, opaque
types, or types that don't fall into any other `ReflectKind` (or perhaps
could, but don't due to some limitation/difficulty). An example sentence
would be "this function takes a reflected value type".
This makes it difficult to write good documentation or other learning
material without causing some amount of confusion to readers. Ideally,
we'd be able to move away from the `ReflectKind::Value` usage and come
up with a better term.
## Solution
This PR replaces the terminology of "value" with "opaque" across
`bevy_reflect`. This includes in documentation, type names, variant
names, and macros.
The term "opaque" was chosen because that's essentially how the type is
treated within the reflection API. In other words, its internal
structure is hidden. All we can do is work with the type itself.
### Primitives
While primitives are not technically opaque types, I think it's still
clearer to refer to them as "opaque" rather than keep the confusing
"value" terminology.
We could consider adding another concept for primitives (e.g.
`ReflectKind::Primitive`), but I'm not sure that provides a lot of
benefit right now. In most circumstances, they'll be treated just like
an opaque type. They would also likely use the same macro (or two copies
of the same macro but with different names).
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect --all-features
```
---
## Migration Guide
The reflection concept of "value type" has been replaced with a clearer
"opaque type". The following renames have been made to account for this:
- `ReflectKind::Value` → `ReflectKind::Opaque`
- `ReflectRef::Value` → `ReflectRef::Opaque`
- `ReflectMut::Value` → `ReflectMut::Opaque`
- `ReflectOwned::Value` → `ReflectOwned::Opaque`
- `TypeInfo::Value` → `TypeInfo::Opaque`
- `ValueInfo` → `OpaqueInfo`
- `impl_reflect_value!` → `impl_reflect_opaque!`
- `impl_from_reflect_value!` → `impl_from_reflect_opaque!`
Additionally, declaring your own opaque types no longer uses
`#[reflect_value]`. This attribute has been replaced by
`#[reflect(opaque)]`:
```rust
// BEFORE
#[derive(Reflect)]
#[reflect_value(Default)]
struct MyOpaqueType(u32);
// AFTER
#[derive(Reflect)]
#[reflect(opaque)]
#[reflect(Default)]
struct MyOpaqueType(u32);
```
Note that the order in which `#[reflect(opaque)]` appears does not
matter.
# Objective
- Fixes#10720
- Adds the ability to control font smoothing of rendered text
## Solution
- Introduce the `FontSmoothing` enum, with two possible variants
(`FontSmoothing::None` and `FontSmoothing::AntiAliased`):
- This is based on `-webkit-font-smoothing`, in line with our practice
of adopting CSS-like properties/names for UI;
- I could have gone instead for the [`font-smooth`
property](https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)
that's also supported by browsers, but didn't since it's also
non-standard, has an uglier name, and doesn't allow controlling the type
of antialias applied.
- Having an enum instead of e.g. a boolean, leaves the path open for
adding `FontSmoothing::SubpixelAntiAliased` in the future, without a
breaking change;
- Add all the necessary plumbing to get the `FontSmoothing` information
to where we rasterize the glyphs and store them in the atlas;
- Change the font atlas key to also take into account the smoothing
setting, not only font and font size;
- Since COSMIC Text [doesn't support controlling font
smoothing](https://github.com/pop-os/cosmic-text/issues/279), we roll
out our own threshold-based “implementation”:
- This has the downside of **looking ugly for “regular” vector fonts**
⚠️, since it doesn't properly take the hinting information into account
like a proper implementation on the rasterizer side would.
- However, **for fonts that have been specifically authored to be pixel
fonts, (a common use case in games!) this is not as big of a problem**,
since all lines are vertical/horizontal, and close to the final pixel
boundaries (as long as the font is used at a multiple of the size
originally intended by the author)
- Once COSMIC exposes this functionality, we can switch to using it
directly, and get better results;
- Use a nearest neighbor sampler for atlases with font smoothing
disabled, so that you can scale the text via transform and still get the
pixelated look;
- Add a convenience method to `Text` for setting the font smoothing;
- Add a demonstration of using the `FontSmoothing` property to the
`text2d` example.
## Testing
- Did you test these changes? If so, how?
- Yes. Via the `text2d`example, and also in my game.
- Are there any parts that need more testing?
- I'd like help from someone for testing this on devices/OSs with
fractional scaling (Android/Windows)
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Both via the `text2d` example and also by using it directly on your
projects.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- macOS
---
## Showcase
```rust
commands.spawn(Text2dBundle {
text: Text::from_section("Hello, World!", default())
.with_font_smoothing(FontSmoothing::None),
..default()
});
```
![Screenshot 2024-09-22 at 12 33
39](https://github.com/user-attachments/assets/93e19672-b8c0-4cba-a8a3-4525fe2ae1cb)
<img width="740" alt="image"
src="https://github.com/user-attachments/assets/b881b02c-4e43-410b-902f-6985c25140fc">
## Migration Guide
- `Text` now contains a `font_smoothing: FontSmoothing` property, make
sure to include it or add `..default()` when using the struct directly;
- `FontSizeKey` has been renamed to `FontAtlasKey`, and now also
contains the `FontSmoothing` setting;
- The following methods now take an extra `font_smoothing:
FontSmoothing` argument:
- `FontAtlas::new()`
- `FontAtlasSet::add_glyph_to_atlas()`
- `FontAtlasSet::get_glyph_atlas_info()`
- `FontAtlasSet::get_outlined_glyph_texture()`
# Objective
- Fixes#8074
- Adopts / Supersedes #8104
## Solution
Adapted from #8104 and affords the same benefits.
**Additions**
- [x] Update scrolling on relayout (height of node or contents may have
changed)
- [x] Make ScrollPosition component optional for ui nodes to avoid
checking every node on scroll
- [x] Nested scrollviews
**Omissions**
- Removed input handling for scrolling from `bevy_ui`. Users should
update `ScrollPosition` directly.
### Implementation
Adds a new `ScrollPosition` component. Updating this component on a
`Node` with an overflow axis set to `OverflowAxis::Scroll` will
reposition its children by that amount when calculating node transforms.
As before, no impact on the underlying Taffy layout.
Calculating this correctly is trickier than it was in #8104 due to
`"Update scrolling on relayout"`.
**Background**
When `ScrollPosition` is updated directly by the user, it can be
trivially handled in-engine by adding the parent's scroll position to
the final location of each child node. However, _other layout actions_
may result in a situation where `ScrollPosition` needs to be updated.
Consider a 1000 pixel tall vertically scrolling list of 100 elements,
each 100 pixels tall. Scrolled to the bottom, the
`ScrollPosition.offset_y` is 9000, just enough to display the last
element in the list. When removing an element from that list, the new
desired `ScrollPosition.offset_y` is 8900, but, critically, that is not
known until after the sizes and positions of the children of the
scrollable node are resolved.
All user scrolling code today handles this by delaying the resolution by
one frame. One notable disadvantage of this is the inability to support
`WinitSettings::desktop_app()`, since there would need to be an input
AFTER the layout change that caused the scroll position to update for
the results of the scroll position update to render visually.
I propose the alternative in this PR, which allows for same-frame
resolution of scrolling layout.
**Resolution**
_Edit: Below resolution is outdated, and replaced with the simpler usage
of taffy's `Layout::content_size`._
When recursively iterating the children of a node, each child now
returns a `Vec2` representing the location of their own bottom right
corner. Then, `[[0,0, [x,y]]` represents a bounding box containing the
scrollable area filled by that child. Scrollable parents aggregate those
areas into the bounding box of _all_ children, then consider that result
against `ScrollPosition` to ensure its validity.
In the event that resolution of the layout of the children invalidates
the `ScrollPosition` (e.g. scrolled further than there were children to
scroll to), _all_ children of that node must be recursively
repositioned. The position of each child must change as a result of the
change in scroll position.
Therefore, this implementation takes care to only spend the cost of the
"second layout pass" when a specific node actually had a
`ScrollPosition` forcibly updated by the layout of its children.
## Testing
Examples in `ui/scroll.rs`. There may be more complex node/style
interactions that were unconsidered.
---
## Showcase
![scroll](https://github.com/user-attachments/assets/1331138f-93aa-4a8f-959c-6be18a04ff03)
## Alternatives
- `bevy_ui` doesn't support scrolling.
- `bevy_ui` implements scrolling with a one-frame delay on reactions to
layout changes.
Currently, Bevy restricts animation clips to animating
`Transform::translation`, `Transform::rotation`, `Transform::scale`, or
`MorphWeights`, which correspond to the properties that glTF can
animate. This is insufficient for many use cases such as animating UI,
as the UI layout systems expect to have exclusive control over UI
elements' `Transform`s and therefore the `Style` properties must be
animated instead.
This commit fixes this, allowing for `AnimationClip`s to animate
arbitrary properties. The `Keyframes` structure has been turned into a
low-level trait that can be implemented to achieve arbitrary animation
behavior. Along with `Keyframes`, this patch adds a higher-level trait,
`AnimatableProperty`, that simplifies the task of animating single
interpolable properties. Built-in `Keyframes` implementations exist for
translation, rotation, scale, and morph weights. For the most part, you
can migrate by simply changing your code from
`Keyframes::Translation(...)` to `TranslationKeyframes(...)`, and
likewise for rotation, scale, and morph weights.
An example `AnimatableProperty` implementation for the font size of a
text section follows:
#[derive(Reflect)]
struct FontSizeProperty;
impl AnimatableProperty for FontSizeProperty {
type Component = Text;
type Property = f32;
fn get_mut(component: &mut Self::Component) -> Option<&mut
Self::Property> {
Some(&mut component.sections.get_mut(0)?.style.font_size)
}
}
In order to keep this patch relatively small, this patch doesn't include
an implementation of `AnimatableProperty` on top of the reflection
system. That can be a follow-up.
This patch builds on top of the new `EntityMutExcept<>` type in order to
widen the `AnimationTarget` query to include write access to all
components. Because `EntityMutExcept<>` has some performance overhead
over an explicit query, we continue to explicitly query `Transform` in
order to avoid regressing the performance of skeletal animation, such as
the `many_foxes` benchmark. I've measured the performance of that
benchmark and have found no significant regressions.
A new example, `animated_ui`, has been added. This example shows how to
use Bevy's built-in animation infrastructure to animate font size and
color, which wasn't possible before this patch.
## Showcase
https://github.com/user-attachments/assets/1fa73492-a9ce-405a-a8f2-4aacd7f6dc97
## Migration Guide
* Animation keyframes are now an extensible trait, not an enum. Replace
`Keyframes::Translation(...)`, `Keyframes::Scale(...)`,
`Keyframes::Rotation(...)`, and `Keyframes::Weights(...)` with
`Box::new(TranslationKeyframes(...))`, `Box::new(ScaleKeyframes(...))`,
`Box::new(RotationKeyframes(...))`, and
`Box::new(MorphWeightsKeyframes(...))` respectively.
# Objective
Functions created into `DynamicFunction[Mut]` do not currently validate
the number of arguments they are given before calling the function.
I originally did this because I felt users would want to validate this
themselves in the function rather than have it be done
behind-the-scenes. I'm now realizing, however, that we could remove this
boilerplate and if users wanted to check again then they would still be
free to do so (it'd be more of a sanity check at that point).
## Solution
Automatically validate the number of arguments passed to
`DynamicFunction::call` and `DynamicFunctionMut::call[_once]`.
This is a pretty trivial change since we just need to compare the length
of the `ArgList` to the length of the `[ArgInfo]` in the function's
`FunctionInfo`.
I also ran the benchmarks just in case and saw no regression by doing
this.
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect --all-features
```
# Objective
#13320 added convenience methods for casting a `TypeInfo` into its
respective variant:
```rust
let info: &TypeInfo = <Vec<i32> as Typed>::type_info();
// We know `info` contains a `ListInfo`, so we can simply cast it:
let list_info: &ListInfo = info.as_list().unwrap();
```
This is especially helpful when you have already verified a type is a
certain kind via `ReflectRef`, `ReflectMut`, `ReflectOwned`, or
`ReflectKind`.
As mentioned in that PR, though, it would be useful to add similar
convenience methods to those types as well.
## Solution
Added convenience casting methods to `ReflectRef`, `ReflectMut`, and
`ReflectOwned`.
With these methods, I was able to reduce our nesting in certain places
throughout the crate.
Additionally, I took this opportunity to move these types (and
`ReflectKind`) to their own module to help clean up the `reflect`
module.
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect --all-features
```
---
## Showcase
Convenience methods for casting `ReflectRef`, `ReflectMut`, and
`ReflectOwned` into their respective variants has been added! This
allows you to write cleaner code if you already know the kind of your
reflected data:
```rust
// BEFORE
let ReflectRef::List(list) = list.reflect_ref() else {
panic!("expected list");
};
// AFTER
let list = list.reflect_ref().as_list().unwrap();
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
# Objective
While #13152 added function reflection, it didn't really make functions
reflectable. Instead, it made it so that they can be called with
reflected arguments and return reflected data. But functions themselves
cannot be reflected.
In other words, we can't go from `DynamicFunction` to `dyn
PartialReflect`.
## Solution
Allow `DynamicFunction` to actually be reflected.
This PR adds the `Function` reflection subtrait (and corresponding
`ReflectRef`, `ReflectKind`, etc.). With this new trait, we're able to
implement `PartialReflect` on `DynamicFunction`.
### Implementors
`Function` is currently only implemented for `DynamicFunction<'static>`.
This is because we can't implement it generically over all
functions—even those that implement `IntoFunction`.
What about `DynamicFunctionMut`? Well, this PR does **not** implement
`Function` for `DynamicFunctionMut`.
The reasons for this are a little complicated, but it boils down to
mutability. `DynamicFunctionMut` requires `&mut self` to be invoked
since it wraps a `FnMut`. However, we can't really model this well with
`Function`. And if we make `DynamicFunctionMut` wrap its internal
`FnMut` in a `Mutex` to allow for `&self` invocations, then we run into
either concurrency issues or recursion issues (or, in the worst case,
both).
So for the time-being, we won't implement `Function` for
`DynamicFunctionMut`. It will be better to evaluate it on its own. And
we may even consider the possibility of removing it altogether if it
adds too much complexity to the crate.
### Dynamic vs Concrete
One of the issues with `DynamicFunction` is the fact that it's both a
dynamic representation (like `DynamicStruct` or `DynamicList`) and the
only way to represent a function.
Because of this, it's in a weird middle ground where we can't easily
implement full-on `Reflect`. That would require `Typed`, but what static
`TypeInfo` could it provide? Just that it's a `DynamicFunction`? None of
the other dynamic types implement `Typed`.
However, by not implementing `Reflect`, we lose the ability to downcast
back to our `DynamicStruct`. Our only option is to call
`Function::clone_dynamic`, which clones the data rather than by simply
downcasting. This works in favor of the `PartialReflect::try_apply`
implementation since it would have to clone anyways, but is definitely
not ideal. This is also the reason I had to add `Debug` as a supertrait
on `Function`.
For now, this PR chooses not to implement `Reflect` for
`DynamicFunction`. We may want to explore this in a followup PR (or even
this one if people feel strongly that it's strictly required).
The same is true for `FromReflect`. We may decide to add an
implementation there as well, but it's likely out-of-scope of this PR.
## Testing
You can test locally by running:
```
cargo test --package bevy_reflect --all-features
```
---
## Showcase
You can now pass around a `DynamicFunction` as a `dyn PartialReflect`!
This also means you can use it as a field on a reflected type without
having to ignore it (though you do need to opt out of `FromReflect`).
```rust
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct ClickEvent {
callback: DynamicFunction<'static>,
}
let event: Box<dyn Struct> = Box::new(ClickEvent {
callback: (|| println!("Clicked!")).into_function(),
});
// We can access our `DynamicFunction` as a `dyn PartialReflect`
let callback: &dyn PartialReflect = event.field("callback").unwrap();
// And access function-related methods via the new `Function` trait
let ReflectRef::Function(callback) = callback.reflect_ref() else {
unreachable!()
};
// Including calling the function
callback.reflect_call(ArgList::new()).unwrap(); // Prints: Clicked!
```
# Objective
Add examples for manipulating sprites and meshes by either mutating the
handle or direct manipulation of the asset, as described in #15056.
Closes#3130.
(The previous PR suffered a Git-tastrophe, and was unceremoniously
closed, sry! 😅 )
---------
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
# Objective
- Fixes#15319
- Fixes#15317
## Solution
- Updated CI task to check for _any_ `bevy_*` crates, rather than just
`bevy_internal`
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>