mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-01 00:05:45 -04:00
create-pull-request/patch
104 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8ce450bd98 |
Another text measurement fix (#24669)
# Objective Text is wrapped to fit inside the node's content box but the node is sized as though the text isn't wrapped. Fixes #24664 ## Solution Subtract the total of the horizontal padding and border insets. ## Testing Added the replication from #24664 to `testbed_ui`'s `TextWrap` scene: ``` cargo run --example testbed_ui -- textwrap ``` <img width="500" alt="image" src="https://github.com/user-attachments/assets/e37884d1-7ae6-4aab-b16f-c5af004f5556" /> --- The other examples, particularly `text_debug`, shouldn't demonstrate any changes. --------- Co-authored-by: Dimitrios Loukadakis <dloukadakis@users.noreply.github.com> |
||
|
|
457b91808a |
More consistant Font asset resolution and change detection (#24362)
# Objective - When `FontSource` resolution fails because an asset isn't yet loaded, it doesn't attempt to reresolve the `FontSource` again the next frame. - When a `Font` asset is removed, its font data is not unloaded from Parley's font database - The results from font lookups can be inconsistant when using `Handle`s to identify a font. Fixes #24356 ## Solution * In `load_font_assets_into_font_collection`, register each font twice, once with an internal asset-specific alias for handle lookups and once using its embedded family name. * Use `set_changed` on `TextFont` to trigger chain detection, instead of the `needs_rerender` flag on `TextBlock`. Schedule `load_font_assets_into_collection` to run before `detect_text_needs_rerender`, otherwise this would delay updates for a frame. * Store the changed family ids and asset paths, and set any `TextFont`s that refer to them as changed. * Don't update the measure funcs in `update_editable_text_content_size` on font asset changes. Instead rely on the narrower `TextFont` change detection, which is sufficient now because `load_font_assets_into_font_collection` marks affected `TextFont` components as changed when newly loaded font assets can affect font resolution. * Removed the mutable deref of `EditableText` at the start of `update_editable_text_styles`. The editor is only mutable accessed if updates to the styles need to be made. * On unloading a font rebuild the whole font database, minus the unloaded fonts, remap any generic families and relayout all text. * Keep a copy of the registered generic families in `FontCx`, so they can be remapped after unloading a font. ## Testing ```rust use bevy::{ feathers::{controls::FeathersNumberInput, FeathersPlugins}, prelude::*, }; fn main() { App::new() .add_plugins((DefaultPlugins, FeathersPlugins)) .add_systems(Startup, setup) .add_systems(Update, spawn_number_input) .run(); } fn setup(mut commands: Commands) { // ui camera commands.spawn(Camera2d); } fn spawn_number_input(mut commands: Commands, mut is_spawned: Local<bool>) { if *is_spawned { return; } *is_spawned = true; commands.spawn_scene(bsn! { Node { margin: UiRect::all(auto()) } Outline Children [ :FeathersNumberInput Node { width: px(200) } ] }); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
50242076bf |
ImageNode measure func fixes and allow drawing inside border and padding (#24154)
# Objective Allowing drawing with `ImageNode` inside the border and padding boxes of a UI node. Fix a lot of image measurement and aspect ratio issues. Fixes #24150 ## Solution Renamed `OverflowClipMarginBox` to `VisualBox`. Added a `visual_box: VisualBox` field to `ImageNode`. Use the size of the selected node region in image rendering. Fixed the bugs with the `ImageNode` measure func. ## Testing Added many more cases to `testbed_ui`'s `image` and `slice` scenes: ``` cargo run --example testbed_ui -- --scene image cargo run --example testbed_ui -- --scene slice ``` ## Showcase <img width="902" height="530" alt="box" src="https://github.com/user-attachments/assets/74f873d4-f00e-47c3-ad3a-3640a39466e4" /> <img width="962" height="563" alt="borderbox" src="https://github.com/user-attachments/assets/3b27c8e1-3b27-4a82-b136-95fed19c28ce" /> <img width="766" height="541" alt="content" src="https://github.com/user-attachments/assets/a4543995-a835-45e1-8ffb-5d671fc7ec05" /> |
||
|
|
b00ff93c87 |
Remove new_with_ prefix from the TextLayout constuctor functions (#24049)
# Objective Remove the `new_with_` prefixes from the `TextLayout` constuctor functions. Generally, the "new" part is redundant and "with" is used by fluent APIs. ## Solution Just delete the prefixes, shorten the names (all on `TextLayout`). * `new_with_justify` -> `justify` * `new_with_linebreak` -> `linebreak` * `new_with_no_wrap` -> `no_wrap` |
||
|
|
0e49f63973 |
Add a rectangular light to the 3d testbed Light scene (#24025)
# Objective - Regression test for broken Rect Lights ## Solution - Add a rectangular light to testbed/3d ## Testing - `cargo run --example testbed_3d` - I can see the new light (well, after #24024 is merged hehe) Before (w/o rect light) <img width="1271" height="747" alt="Screenshot 2026-04-28 at 11 28 16 PM" src="https://github.com/user-attachments/assets/aa6b6258-e80d-4a98-861c-bcad6e8db541" /> After (w/ rect light - it’s behind the cube) <img width="1271" height="750" alt="Screenshot 2026-04-28 at 11 25 33 PM" src="https://github.com/user-attachments/assets/877013f0-843a-43f2-9dbd-96f2fa37d8e2" /> |
||
|
|
8b8a432ab4 |
EditableText change detection using parley::Generation (#23785)
# Objective Improved change detection for `EditableText`. Fixes #23793 ## Solution * Remove the `text_edited` field from `EditableText` that was used for manual change detection. * Use the `PlainEditor`'s `Generation` to track changes. * New component `EditableTextGeneration`. Newtypes `parley::Generation`. Stores the generation from the last `TextLayoutInfo` update. * If `EditableText::editor`'s and `EditableTextGeneration`'s generation values aren't equal, reupdate `TextLayoutInfo`. * Added support for `TextLayout::justify`. * Split up `editable_text_system` into two systems `update_editable_text_styles` and `update_editable_text_layout`. The text input's style values need to be updated before layout, so the measure func returns the correct size for the text layout. * New `EditableText` `testbed_ui` scene. * Added two numeric inputs to `multiline_text_input` that allow you to set the height of the multiline input and its fontsize. * Update selection rects on all changes, not just when a text input is focused. ## Testing ``` cargo run --example multiline_text_input ``` The cursor appears to be missing for the numeric inputs in the example but it isn't. The cursor gets clipped because it's at the end of the right aligned input value text. If you press left it comes into view. Cursor and scrolling behaviour needs some adjustments but that's out of the scope of this PR. |
||
|
|
61127f6d01 |
Replace all different load variants in AssetServer with a builder. (#23663)
# Objective
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
|
||
|
|
ec254ded7d |
Make Skybox’s image optional so that Skybox::default() is valid. (#23691)
# Objective - Partially addresses #23688. - Prevents use of `Skybox::default()` from causing errors. ## Solution `Skybox::default()` is problematic because it contains an `Image` that is not a valid skybox. ~~This change removes the `Default` implementation and instead provides a `new()` function which takes the image as a parameter (and also the brightness, which is practically required).~~ This change makes the `image` field optional so that the default `None` renders nothing. Things we could do instead of this: * Make `Skybox` not implement `Default`. I am informed this is a bad idea. * Create a default cubemap image for `default()` to use. ## Testing Ran the `skybox` and `irradiance_volumes` examples. |
||
|
|
535cf401cc |
Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper. |
||
|
|
36a97620df |
Add white furnace analog for 2D color consistency (#23589)
# Objective Create a regression test for https://github.com/bevyengine/bevy/issues/23577. ## Solution Copy the white furnace test idea by spawning a bunch of 2D things with exactly the same color. Split out from #23584. ## Testing `cargo run -example testbed_2d`. Press space to get to the example that's all grey. Locally, this is all one uniform grey for me: I could not reproduce #23577. Still, this is a useful property to test for! |
||
|
|
2589ed242f |
Add render_layers scene to testbed_3d (#23559)
# Objective - RenderLayers are easy to break and so is multiple viewport support ## Solution - Add a new scene to testbed_3d that shows multiple cameras at the same time all with different combinations of render layers ## Testing - I ran the example locally and toggled between all the scenes --- ## Showcase <img width="1282" height="752" alt="testbed_3d_KmHPJ8pweU" src="https://github.com/user-attachments/assets/dad376f3-fd72-424d-b68d-7048843da179" /> As you can see, this already highlights a bug with shadows not respecting render layers |
||
|
|
f62d53dbca |
Bordered and padded content (#23510)
# Objective Allow borders and padding on text and images. Fixes #17300, #14498, #14789, #11557, #6879 ## Solution Remove filter for border values on content sized nodes. Apply content offsets to content in rendering and picking. ## Testing ``` cargo run --example testbed_ui -- boxedcontent ``` ``` cargo run --example image_node ``` ``` cargo run --example editable_text ``` ``` cargo run --example text_background_colors ``` ## Showcase <img width="1924" height="1127" alt="Screenshot 2026-03-25 113700" src="https://github.com/user-attachments/assets/68be286b-ca0e-46c7-9295-973df77412e9" /> <img width="223" height="223" alt="birdd" src="https://github.com/user-attachments/assets/e9f6e393-7ee9-498b-85d0-318abd702448" /> |
||
|
|
5e6493b82d |
move white furnace to the 3d testbed (#23207)
# Objective - https://github.com/bevyengine/bevy/pull/22584 added a white furnace test, but it was not setup as a testbed - Fixes #23202 ## Solution - Add it to the 3d testbed |
||
|
|
5b953998ce |
Disable global ambient light in white furnace test (#23189)
# Objective The recently added white furnace test doesn't disable global ambient lighting, which makes the results appear brighter than they should be. ## Solution Disable global ambient lighting in the white furnace test. ## Testing - Tested by running `cargo run --example testbed_white_furnace` The PBR renderer still loses energy for partially metallic materials, but it appears to work fine for pure dielectrics or metals when using a solid color light. --- ## Showcase <img width="1280" height="720" alt="color_based" src="https://github.com/user-attachments/assets/3491336e-5244-4b57-8f1a-6879edc8b458" /> <img width="1280" height="720" alt="image_based" src="https://github.com/user-attachments/assets/bf59f8f2-2290-490d-beed-c64be44c2d22" /> |
||
|
|
df5cdd0faa |
Despawn atlas sprites on scene exit in testbed_2d (#23133)
# Objective Some cleanup is missing from the 2d testbed's `TextureAtlasBuilder` scene. ## Solution Add `DespawnOnExit(super::Scene::TextureAtlasBuilder)` to the atlas sprites. |
||
|
|
1aedbf36d1 |
Fix the gizmo colors in testbed_2d's texture_atlas_builder scene (#23146)
# Objective
Fix the gizmo colours in testbed_2d instead of basing them on entity
ids.
## Solution
```rust
ShowAabbGizmo {
color: Some(Color::WHITE),
}
```
|
||
|
|
a17dfe4611 |
Add white furnace test for testing PBR (#22584)
# Objective - Add a new testbed example demonstrating a white furnace test for PBR materials - Provide a tool for verifying that PBR materials correctly conserve energy under uniform white lighting ## Solution - Created a new testbed example `examples/testbed/white_furnace.rs` - Programmatically generate a pure white cubemap using `Rgba16Float` format with hardcoded f16 bytes - Use `GeneratedEnvironmentMapLight` to provide the white environment map for IBL - Use `Skybox` component to display the white environment as the background - Displays a grid of spheres - same as the PBR example - Enabled HDR rendering, intentionally disabled tonemapping with `Tonemapping::None` to represent the raw lighting values in the final output framebuffer - Added the example to `Cargo.toml` ## Testing - Tested by running `cargo run --example testbed_white_furnace` - Confirmed that the white cubemap is correctly generated and applied to both the skybox and environment lighting However the test reveals that Bevy's PBR renderer is not energy conserving under all conditions. --- ## Showcase <img width="1280" height="751" alt="Screenshot 2026-01-18 at 10 45 17 AM" src="https://github.com/user-attachments/assets/877ddf69-1c9c-48aa-978e-d7b4884a420e" /> this is even more apparent when exporting from the shader debugger before the display transform is applied. <img width="2560" height="1440" alt="pre-tonemap" src="https://github.com/user-attachments/assets/20a2db7f-856e-4648-9d1c-f7e5288ad8dd" /> ## Open Items - Add the furnace test to CI /automated test suite to catch regressions as suggested by Alice |
||
|
|
4732c21077 |
The testbed_ui text scene should use a column layout (#23077)
# Objective Remove unnecessary usage of absolute positioning for each text element Fixes https://github.com/bevyengine/bevy/issues/22976 ## Solution Uses display grid with two columns to display the `testbed_ui` text. Beyond that I removed `DespawnOnExit(super::Scene::Text)` from each text element and added that only to the parent component. ## Testing Run and hit space once: ```sh cargo run --example testbed_ui ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
b03922d484 |
testbed_2d texture atlas builder scene (#23074)
# Objective Add a scene displaying the output from `TextureAtlasBuilder`. ## Solution Add a scene showing the atlases textures and padding and how it affects the sub images when they are drawn. ## Testing ``` cargo run --example testbed_2d -- textureatlasbuilder ``` |
||
|
|
c43c8f4c32 |
Inverted UI node background color (#23043)
# Objective Fixes #22797 ## Solution New `OuterColor` marker component. Sets a fill color for the area outside the border created when a border radius is set. The Implementation is very simple. The shader now has an `INVERT` flag, if enabled the direction of the rounded corners SDF is flipped. # I'm not sure about the `OuterColor` name, but can't think of anything more descriptive that isn't also annoyingly long. ## Testing New `testbed_ui` scene: ``` cargo run --example testbed_ui -- outercolor ``` ## Showcase <img width="1924" height="1127" alt="inverted" src="https://github.com/user-attachments/assets/095f49d6-844a-4b0c-af2b-86b50d40952a" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
7c153070c0 |
Adds text gizmos to testbeds (#23064)
# Objective - Fixed both #23058 and #23059 ## Solution - Adds text gizmos to the 2D and 3D testbed examples ## Testing Tested on Windows 11/Nvidia/Vulkan --- ## Showcase <details> <summary>Click to view showcase</summary> ### testbed_2d <img width="1282" height="752" alt="image" src="https://github.com/user-attachments/assets/ce20cf8f-c6f0-49be-8290-9f912c144f65" /> ### testbed_3d <img width="1282" height="752" alt="image" src="https://github.com/user-attachments/assets/2de280bd-bf2f-4335-a7ba-c25fc9a53d25" /> </details> |
||
|
|
92a1255357 |
Add font hinting example to testbed_ui's text scene (#22970)
# Objective Add font hinting to the UI testbed text scene. ## Solution Draw all the cases again without hinting: <img width="1924" height="1127" alt="Screenshot 2026-02-15 235523" src="https://github.com/user-attachments/assets/bbd581b4-1249-4f23-939a-0d59e8cccf75" /> |
||
|
|
59e9ee3a1a |
Store Resources as components on singleton entities (#20934)
This is part of #19731. # Resources as Components ## Motivation More things should be entities. This simplifies the API, the lower-level implementation and the tools we have for entities and components can be used for other things in the engine. In particular, for resources, it is really handy to have observers, which we currently don't have. See #20821 under 1A, for a more specific use. ## Current Work This removes the `resources` field from the world storage and instead store the resources on singleton entities. For easy lookup, we add a `HashMap<ComponentId, Entity>` to `World`, in order to quickly find the singleton entity where the resource is stored. Because we store resources on entities, we derive `Component` alongside `Resource`, this means that ```rust #[derive(Resource)] struct Foo; ``` turns into ```rust #[derive(Resource, Component)] struct Foo; ``` This was also done for reflections, meaning that ```rust #[derive(Resource, Reflect)] #[refect(Resource)] struct Bar; ``` becomes ```rust #[derive(Resource, Component, Reflect)] #[refect(Resource, Component)] struct Bar; ``` In order to distinguish resource entities, they are tagged with the `IsResource` component. Additionally, to ensure that they aren't queried by accident, they are also tagged as being internal entities, which means that they don't show up in queries by default. ## Drawbacks - Currently you can't have a struct that is both a `Resource` and a `Component`, because `Resource` expands to also implement `Component`, this means that this throws a compiler error as it's implemented twice. - Because every reflected Resource must also implement `ReflectComponent` you need to import `bevy_ecs::reflect::ReflectComponent` every time you use `#[reflect(Resource)]`. This is kind of unintuitive. ## Future Work - Simplify `Access` in the ECS, to only deal with components (and not components *and* resources). - Newtype `Res<Resource>` to `Single<Ref<Resource>>` (or something similair). - Eliminate `ReflectResource`. - Take stabs at simplifying the public facing API. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Dimitrios Loukadakis <dloukadakis@users.noreply.github.com> |
||
|
|
ff57138b44 |
Adds tests for new UiDebugOverlay features into testbed_ui (#22673)
# Objective - Add some recently expanded UI Debug functionality to the testbed #21931 ## Solution To `testbed/ui.rs`, Adds a second row of entities demonstrating: 1. border, padding, content box outlines with a rounded border radius 2. vertical scrollbar outline 3. horizontal scrollbar outline 4. bidirectional scrollbar outline The scrollbars were taken from `scroll.rs` and I tried to strip them down to the barest code that shows them as outlines (so scrolling doesn’t work with them) ## Testing `cargo run --example testbed_ui --features="bevy_ui_debug” -- debugoutlines` does the trick ## Showcase If this screenshot / these new test cases expose(s) any bugs that are not intended behavior (i.e. why is the bottom left padding box’s corner also rounded when only the bottom right border radius was specified? Or why do the bidirectional scrollbars overlap the text?), please make an issue / let me know so that I can make an issue <details> <summary>Screenshot</summary> <img width="1282" height="747" alt="Screenshot 2026-01-23 at 6 50 18 PM" src="https://github.com/user-attachments/assets/a52fcec8-834c-46e9-b7a0-61c3e30cf730" /> </details> |
||
|
|
6ca4769128 |
Minimal responsive FontSize support (#22614)
# Objective
Add responsive font sizes supporting rem and viewport units to
`bevy_text` with minimal changes to the APIs and systems.
## Solution
Introduce a new `FontSize` enum:
```rust
pub enum FontSize {
/// Font Size in logical pixels.
Px(f32),
/// Font size as a percentage of the viewport width.
Vw(f32),
/// Font size as a percentage of the viewport height.
Vh(f32),
/// Font size as a percentage of the smaller of the viewport width and height.
VMin(f32),
/// Font size as a percentage of the larger of the viewport width and height.
VMax(f32),
/// Font Size relative to the value of the `RemSize` resource.
Rem(f32),
}
```
This replaces the `f32` value of `TextFont`'s `font_size` field.
The viewport variants work the same way as their respective `Val`
counterparts.
`Rem` values are multiplied by the value of the `RemSize` resource
(which newtypes an `f32`).
`FontSize` provides an `eval` method that takes a logical viewport size
and rem base size and returns an `f32` logical font size. The resolved
logical font size is then written into the `Attributes` passed to Cosmic
Text by `TextPipeline::update_buffer`.
Any text implementation using `bevy_text` must now provide viewport and
rem base values when calling `TextPipeline::update_buffer` or
`create_measure`.
`Text2d` uses the size of the primary window to resolve viewport values
(or `Vec2::splat(1000)` if no primary window is found). This is a
deliberate compromise, a single `Text2d` can be rendered to multiple
viewports using `RenderLayers`, so it's difficult to find a rule for
which viewport size should be chosen.
### Change detection
`ComputedTextBlock` has two new fields: `uses_viewport_sizes` and
`uses_rem_sizes`, which are set to true in `TextPipeline::update_buffer`
iff any text section in the block uses viewport or rem font sizes,
respectively.
The `ComputedTextBlock::needs_rerender` method has been modified to take
take two bool parameters:
```rust
pub fn needs_rerender(
&self,
is_viewport_size_changed: bool,
is_rem_size_changed: bool,
) -> bool {
self.needs_rerender
|| (is_viewport_size_changed && self.uses_viewport_sizes)
|| (is_rem_size_changed && self.uses_rem_sizes)
}
```
This ensures that text reupdates will also be scheduled if one of the text section's uses a viewport font size and the local viewport size changed, or if one of the text section's uses a rem font size and the rem size changed.
#### Limitations
There are some limitations because we don't have any sort of font style inheritance yet:
* "rem" units aren't proper rem units, and just based on the value of a resource.
* "em" units are resolved based on inherited font size, so can't be implemented without inheritance support.
#### Notes
* This PR is quite small and not very technical. Reviewers don't need to be especially familiar with `bevy_text`. Most of the changes are to the examples.
* We could consider using `Val` instead of `FontSize`, then we could use `Val`'s constructor functions which would be much nicer, but some variants might not have sensible interpretations in both UI and Text2d contexts. Also we'd have to make `Val` accessible to `bevy_text`.
## Testing
The changes to the text systems are relatively trivial and easy to understand. I already added a minor change to the `text` example to use `Vh` font size for the "hello bevy" text in the bottom right corner. If you change the size of the window, you should see the text change size in response. The text initially flickers before it updates because of some unrelated asset/image changes that mean that font textures aren't ready until the frame after the text update that changes the font size.
Most of the example migrations were automated using regular expressions, and there are bound to be mistakes in those changes. It's infeasible to check every single example thoroughly, but it's early enough in the release cycle that I don't think we should be too worried if a few bugs slip in.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
|
||
|
|
02c576215b |
FontSmoothing fixes (#22455)
# Objective 1. The `font_smoothing` field of `TextFont` is ignored in main currently. Instead it always uses `FontSmoothing::Antialiased`. 2. The `FontSmoothing` enum is missing from the `bevy_text` prelude. 3. The `FontSmoothing` bug wasn't caught by the screenshot CI. ## Solution 1. Store the antialiasing mode in `ComputedTextBlock`, for use in `TextPipeline::update_buffer`. 2. Add `FontSmoothing` to `bevy_text::prelude`. 3. Add a font smoothing example to `testbed_ui`'s `Text` scene. ## Testing Check `testbed_ui`'s text scene. Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
a88af65738 |
Contact Shadows (#22382)
# Objective - Implement contact shadows to add fine shadow detail where shadow cascades cannot. ## Solution - Extend our existing pbr implementation using our existing raymarching functions. --- ## Showcase <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/e93b79c5-c596-4a9e-b94d-20bdde1d863b" /> <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/0fd7dffa-60b8-4b92-8fad-7f993d4d89dd" /> https://github.com/user-attachments/assets/e74b190d-9ae3-4aaf-97f0-b520930a0667 https://github.com/user-attachments/assets/e80ccb26-bbaa-4d25-a823-8ea12354c5b9 https://github.com/user-attachments/assets/b04f4b00-92bd-4a2f-b7dd-5157d8fbe0ab <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/b7629908-dd32-48db-8ee7-a4d2dd8f66c2" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3de0258e-9191-4180-ac57-41b32e1205bd" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/951477f9-e9a9-426f-ae8d-18ae50cc7b85" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/2291453c-da57-4fcc-a6b0-f60f6eac6cbb" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/5820cdff-ea54-4294-b520-2a8d8dc24996" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3ea16481-7689-4e99-87e2-1589f1532e4c" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com> |
||
|
|
e5d4dfd7a6 |
Minimal Font Families, Font Queries, Collections, System Fonts, Stretch, and Slant support (#22156)
# Objective
Implement support for the remaining missing text features with minimal
changes.
## Solution
`TextFont` has been expanded to include new fields:
```rust
pub struct TextFont {
pub font: FontSource,
pub font_size: f32,
pub weight: FontWeight,
pub width: FontWidth,
pub style: FontStyle,
pub font_smoothing: FontSmoothing,
pub font_features: FontFeatures,
}
```
FontSource has two variants: Handle, which identifies a font by asset
handle, and Family, which selects a font by its family name.
`FontWidth` is a newtype struct representing OpenType font stretch
classifications ranging from ULTRA_CONDENSED (50%) to ULTRA_EXPANDED
(200%).
`FontStyle` is an enum used to set the slant style of a font, either
`Normal`, `Italic`, or `Oblique`.
The system font support is very barebones. You load them using the
`CosmicFontSystem` resource:
```rust
font_system.db_mut().load_system_fonts()
```
Then they are available to be selected by family name using
`FontSource::Family`.
### Other changes
* `TextPipelines`'s `glyph_info` field has been removed. There is no
need to collect the section infos or perform any querys during text
layout updates, so that code has been removed as well.
* `update_text_layout_info` used some `try_for_each` with some nested
closures which was unnecessarily complicated again. They've been
replaced with a regular for loop.
* After font assets are loaded there's a new system
`load_font_assets_into_fontdb_system` that automatically adds them to
cosmic text's font database. Then they are available to be looked up by
family name as well as by asset handle.
* There aren't are performance motivated changes but layout updates seem
to be overall significantly more efficient now, with a slight regression
for very large numbers of short, single section text entities.
* Font texture atlases are no longer automatically cleared when the font
asset they were generated from is removed. There is no way to remove
individual fonts from cosmic text's `FontSystem`, so the font is still
accessible using the family name with `FontSource::family` and removing
the text atlases naively could cause a panic since rendering expects
them to be present.
## Testing
```
cargo run --example font_query
```
---
## Showcase
<img width="1229" height="591" alt="font-query"
src="https://github.com/user-attachments/assets/23f5aaa2-fdb8-4448-9b4e-9d65d6431107"
/>
---------
Co-authored-by: Thierry Berger <contact@thierryberger.com>
|
||
|
|
730b002260 |
fix testbeds with argh in wasm (#22339)
# Objective - #22223 used argh in the testbed examples - this compile in wasm but crashes when running them ## Solution - Fix it like the other examples using argh |
||
|
|
9f02248af4 |
Add command line option to choose a starting scene in the `testbed_*' examples (#22223)
# Objective - Fixes #22218 ## Solution - Use `argh` to check if you passed a scene name, and if you did, start on that one instead of the default ## Testing - Tested on my local machine, I don't see a reason why it would change between different machines and/or platforms --------- Co-authored-by: Bayley Foster <43776524+apekros@users.noreply.github.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
64c7bec406 |
Extra testbed_ui font cases (#22198)
# Objective Modify the `testbed_ui` text scenes so it will catch bugs like #22191 ## Solution Add new cases to `testbed_ui`'s `text` scene displaying multiple text sections with different fonts and weights with spaces and without spaces. ## Showcase <img width="1924" height="1127" alt="Screenshot 2025-12-19 142112" src="https://github.com/user-attachments/assets/3247c0d7-cfca-45ac-88dd-b8a8a4266dff" /> |
||
|
|
ed85f770c9 |
Adds ViewportCoords Scene to examples/testbed/ui, Removes viewport_debug example (#22050)
# Objective - Closes #21806 ## Solution - Copies the setup contained in `spawn_with_viewport_coords` from `viewport_debug` into its own scene in the ui testbed. The testbed spawns a 1280 x 720 resolution window by default, so it looks the same. I explicitly set the size anyway and set resizeable to false to accommodate this new scene - Removes the `viewport_debug` example (Although, tbh, I’m wondering if it’s actually worth keeping around since it might be useful for debugging for whatever reason. **Second opinions welcome** on the deletion of `viewport_debug`) ## Testing Ran `cargo run --example testbed_ui` and compared the new `ViewportCoords` scene via screenshot to the `viewport_debug` example (`cargo run --example viewport_debug`). They look the same. --- ## Showcase To see the scene, checkout the branch and run `cargo run --example testbed_ui`. Hit the Spacebar 12x to see the new scene. <img width="1284" height="748" alt="Screenshot 2025-12-06 at 5 03 42 PM" src="https://github.com/user-attachments/assets/265caf93-57d2-475e-b532-6c7f36074085" /> |
||
|
|
a3ad40d412 |
Alternative glTF coordinate conversion (#20394)
## Objective Change glTF coordinate conversion to satisfy some common use cases while dodging the more controversial aspects. This fixes #20621, but at the cost of removing one feature. ## Summary The Bevy glTF loader can optionally convert nodes and meshes from glTF's "+Z forward" semantics to Bevy's "-Z forward". But the current implementation [has issues](https://github.com/bevyengine/bevy/issues/20621), particularly with cameras and lights. It might also cause problems for users who want to re-orient the scene as a whole while preserving the original node semantics. This PR replaces node conversion with a simpler correction to the scene root and mesh entities. The new approach satisfies many use cases and fixes the issues with cameras and lights. But it could be a regression for some users. ## Background There's been confusion over how glTF behaves and what users might want from coordinate conversion. This section recaps the basic concepts, glTF's semantics, the current loader behaviour, and some potential user stories. Or you can skip to the next section if you want to get straight to the changes. <details> <summary>Click to expand</summary> ### Coordinate Systems and Semantics 3D coordinate systems can have semantics assigned to their axes. These semantics are often defined as a forward axis, an up axis, and a [handedness](https://en.wikipedia.org/wiki/Right-hand_rule) - the side axis is implicit in the other choices. Bevy's standard semantics are "-Z = forward, +Y = up, right handed". This standard is codified by the `forward` and `up` methods of `Transform` and `GlobalTransform`, and by the renderer's interpretation of camera and light transforms. There are debates about the standard and whether users should be able to choose different semantics. This PR does not account for those debates, and assumes that users want to follow the current standard. Other engines, DCCs, and file formats can have [different semantics](https://mastodon.social/@acegikmo/113313928426095165). Unlike Bevy, some vary their semantics by object type - a camera's forward axis may not be the same as a light's. Some only specify an up axis, leaving the forward and side axes unspecified. Assets might not follow the standard semantics of their file format. Static mesh hierarchies and skeletal animation rigs may even have per-node or per-joint semantics - a character rig could be +Y forward in the scene, while the head joint is +Z forward. One character rig might have both feet +X forward, while another rig might have the left foot +X forward and the right foot -X forward. This creates complexity, but also creates jobs, so no-one can say if it's good or bad. ### Asset Loaders And Coordinate Conversion Bevy currently has a glTF loader, and I'm assuming it will get in-repo FBX and USD loaders at some point. These loaders are likely to follow a common pattern: - The files contain meshes, which correspond to Bevy `Mesh` assets and skinned meshes. - Bevy meshes can only have a single material, so what the file format considers a single mesh might be multiple Bevy meshes. - The files have a node hierarchy, where nodes roughly correspond to Bevy entities with a `Transform`. - Nodes can optionally be mesh instances, cameras, lights or skinned mesh joints. - The loader outputs the assets and a `Scene` with an entity hierarchy that tries to match the file's node hierarchy. - Some aspects of nodes (e.g. pivot transforms) can't be represented in Bevy within a single entity. - So a 1:1 mapping might not be possible - instead nodes become multiple entities, or some data is lost (e.g. baking down pivot transforms). - Users can choose to spawn the scene, or they can ignore it and use the assets directly. Users may want asset loaders that convert assets to Bevy's standard semantics, so `Transform::forward` matches the asset. But the details of conversion can be contentious - users may want some parts of the scene to be converted differently from other parts, and assets may have ambiguities than can only be resolved by the user. There will never be a simple "it just works" option, although there could be a least worst default that satisfies the biggest group of users. Converting in the loader is not the only option. The user could edit the assets themselves or run a conversion script in DCC. But that's a pain - particularly for users who rely on asset packs and don't have DCC experience. Another option is to implement an asset transform that does coordinate conversion. But having the options right there in the loader is convenient. ### User Stories For coordinate conversion in the loader, some user stories might be: - "I want to spawn a scene on an entity with Bevy semantics and have it look right." - This is probably the most common case - the user wants to do `SceneRoot(load("my.gltf"))` and have it visually match the entity's `Transform::forward()`, and cameras and lights should do the right thing. - The user might not care about the semantics of mesh assets and nodes in the scene - they just want the scene as a whole to look right. - "I want to spawn a scene, and convert some or all of the nodes to Bevy semantics." - The user might have nodes in their scene that they want to animate manually or hook up to other systems that assume Bevy semantics. - That becomes easier if the loader can convert the node's forward to match `Transform::forward()`. - Conversely, some users might want nodes to stay as they are (particularly skeletal animation rigs). - "I want a mesh asset that's converted to Bevy semantics. I'm not using a scene." - Maybe the user is doing `Mesh3d(load("mesh.gltf#Mesh0"))` and wants it to match the entity's forward. - Or this is the first stage of an asset pipeline and the remaining stages expect Bevy semantics. - "I don't want the loader to touch anything." - Maybe they've already converted the file, or want to convert it post-load, or don't want to use Bevy semantics at all. - "I want one of the other conversion stories, but the loader should convert to my chosen semantics rather than Bevy's". - Z-up is not a crime. ### glTF Semantics glTF [scene semantics](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#coordinate-system-and-units) are "+Z = forward, +Y = up, right handed". This is almost the same as Bevy, except that scene forward is +Z instead of Bevy's -Z. Some glTF assets do not follow the spec's scene semantics. The Kenney asset packs use a mix of +Z and -Z forward. At least [one of the Khronos sample assets](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/Duck) uses +X forward. That said, the majority of Kenney assets and almost all the Khronos sample assets I tested do follow the spec. glTF [camera node](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#view-matrix) and [light node](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/README.md#adding-light-instances-to-nodes) semantics are different from glTF scene semantics - they're -Z forward, same as Bevy. The glTF spec doesn't explicitly say if non-camera/light nodes and mesh buffers have semantics. I'm guessing that some users will have nodes and meshes that follow the spec's scene semantics, and might want them converted to Bevy semantics. But as noted in the user stories, it's likely that other users will have different needs. glTF and Bevy allow a single node/entity to be both a mesh and a camera or a light. This only makes sense if the user intends the mesh to have the same semantics as cameras and lights. I think it's very unlikely that significant numbers of users will want support for this combination - many other DCCs, file formats and engines don't support it at all. ### How The Bevy glTF Loader Works The loader maps glTF nodes to Bevy entities. It also adds entities for two cases: 1. A single "scene root" entity is added as a parent of the glTF root nodes. - Note that this is not the user's entity with the `SceneRoot` component - the scene root entity is a child of that entity. 2. Mesh primitive entities are added as a child of each glTF mesh node. - In glTF, a single mesh node can contain multiple primitives. - But in Bevy a mesh component can only contain a single primitive, so one entity can't contain multiple primitives. - So, for each primitive, Bevy adds a child entity with a mesh component. A single branch of the resulting scene hierarchy might look like this: - User entity with `SceneRoot` component. - Scene root entity. - glTF root node entity. - glTF intermediate node entities. - glTF mesh node entity (does not contain `Mesh3d` component) - Mesh primitive entities (does contain `Mesh3d` component). ### glTF Loader Changes In 0.17 In Bevy 0.16, the only user story supported by the glTF loader was "no conversion". During the 0.17 cycle, #19633 and some follow up PRs implemented an option that converts nodes, meshes and animation tracks. The changes do satisfy some user stories, including the common "convert scene semantics" (mostly) and "convert mesh semantics". But there's some problems (#20621): - The conversion depends on converting both nodes and meshes. - Some users might want to convert the scene without converting nodes and/or meshes. - Light and camera nodes get complicated. - glTF camera/light nodes already match Bevy semantics, so they need a counter-conversion (since their parent might have been converted). - Animation tracks for lights and cameras are not correctly converted. - (Counterpoint: This is fixable at the cost of some complexity) - Child nodes of lights and cameras are not correctly converted. - (Counterpoint: Also fixable, and probably a niche case?) - The conversion can't support a node that's a mesh instance and also a light and/or a camera. - (Counterpoint: As mentioned earlier, this is probably a very niche or non-existent use case.) </details> ## Solution The big change in this PR is the removal of node conversion. Instead, corrective transforms are applied to the scene root entity and mesh primitive entities. Before this PR: - Scene root entity. - glTF root node entity. <-- CONVERTED - glTF intermediate node entities. <-- CONVERTED - glTF mesh node entity. <-- CONVERTED - Mesh primitive entities. After this PR: - Scene root entity. <-- CORRECTIVE (if scene conversion enabled) - glTF root node entity. - glTF intermediate node entities. - glTF mesh node entity. - Mesh primitive entities. <-- CORRECTIVE (if mesh conversion enabled) The result is visually the same even though the scene internals are different. Cameras and lights now work correctly, including when animated. The new conversion is also simpler. There's no need to convert animations, and the scene part of the conversion only changes a single entity: ```diff +let world_root_transform = convert_coordinates.scene_conversion_transform(); let world_root_id = world - .spawn((Transform::default(), Visibility::default())) + .spawn((world_root_transform, Visibility::default())) .with_children(|parent| { for node in scene.nodes() { ``` Removing node conversion might be a regression for some users. My guess is that most users just want to spawn a scene with the correct orientation and don't worry about individual node transforms, so on balance this PR will be win. But I don't have much evidence to back that up. There might also be a path to adding node conversion back in as an option - see the "Future" section below. The previous conversion option - `GltfPlugin::use_model_forward_direction` - has been split into two separate options for scene and mesh conversion. ```diff struct GltfPlugin { ... - use_model_forward_direction: bool, + convert_coordinates: GltfConvertCoordinates, } ``` ```rust struct GltfConvertCoordinates { scenes: bool, meshes: bool, } ``` This might be turn out to be unnecessary flexibility, but I think it's the safer option for now in case users have unexpected needs. Both options are disabled by default. ### Testing I've tested various examples and glTFs with each combination of options, including glTFs with animated cameras and lights. ```sh # Visually the same as current Bevy *without* conversion. cargo run --example scene_viewer "assets/models/faces/faces.glb" cargo run --example scene_viewer "assets/models/faces/faces.glb" --convert-mesh-coordinates # Visually the same as current Bevy *with* conversion. cargo run --example scene_viewer "assets/models/faces/faces.glb" --convert-scene-coordinates cargo run --example scene_viewer "assets/models/faces/faces.glb" --convert-scene-coordinates --convert-mesh-coordinates cargo run --example animated_mesh ``` ## Future <details> <summary>Click to expand</summary> This PR removes node conversion, which is a desirable feature for some users. There are a couple of ways it could be added back as an option. The difficult part of node conversion is how to support camera and light nodes. glTF's camera/light semantics already match Bevy's -Z forward, so simply converting every node from +Z to -Z forward will leave camera and light nodes facing the wrong direction. The obvious solution is to special case camera/light node transforms - this is what the 0.17 conversion tries to do. But it's surprisingly complex to get right due to animation, child nodes, and nodes that can be meshes and cameras and lights. E.g. children of cameras and lights need a counter-conversion applied to their transform and animation tracks. For cameras, an alternative would be to split them multiple entities. The existing entity would correspond to the glTF node and be converted like every other node. But the Bevy `Camera` component would be on a new child entity and have a corrective transform. Before: - Parent glTF node entity. - Camera glTF node entity with `Camera` component and animated transform. - glTF node parented to camera node. After: - Parent glTF node entity. - Camera glTF node entity with animated transform. - New child entity with `Camera` component and corrective transform. - glTF node parented to camera node. Lights are already set up this way, so they only need the corrective transform. This approach is simpler since nodes are treated uniformly. And it's arguably a better reflection of the glTF format - glTF cameras are kind of a separate thing from nodes, and can be given a name that's different to their node's name. So it could be better for some users. The downside is that the glTF node entity might have the wrong semantics from the perspective of some users (although not all). And it will be annoying for users who currently assume the `Camera` component is on the node entity. </details> ## Alternatives <details> <summary>Click to expand</summary> ### What About The Forward Flag Proposal? There's a [proposal](https://github.com/bevyengine/bevy/pull/20135) to allow per-transform semantics, aka the "forward flag". This means the axis of `Transform::forward()` and others would depend on a variable in the `Transform`. In theory the forward flag might avoid the need for coordinate conversion in the loader. But whether that works in practice is unclear, and the proposal appears to be stalled. ### What Do Other Engines Do? [Godot's semantics](https://docs.godotengine.org/en/4.4/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions) are the same as the glTF standard. Godot doesn't offer any conversion options. Unreal's default semantics are "+X forward, +Z up, left handed", except meshes are typically "+Y forward, +Z up". Their glTF importer converts nodes and meshes to Unreal's mesh semantics - this is done by swapping the Y and Z axes, which implicitly flips the X for handedness. So Unreal's approach is actually closer to the current main approach of node + mesh conversion, versus this PR's scene + mesh conversion. The Unreal importer also supports a custom scene/mesh rotation and translation that's applied after normal conversion. There's no option to disable conversion. </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: Alice I Cecile <alice.i.cecile@gmail.com> |
||
|
|
b58ecf2ac3 |
Add sprite slicing scene to testbed_2d example (#22089)
# Objective - Sprite slicing doesn't have any example with the screenshot CI. - Fixes #22083 ## Solution - Added a new `sprite_slicing` module to `examples/testbed/2d.rs` ## Testing - Ran the testbed with `cargo run --example testbed_2d` - Cycled through all scenes using spacebar - Verified the sprite slicing scene displays correctly with both original and sliced sprites - Confirmed labels and positioning are correct --- ## Showcase <img width="1372" height="755" alt="Screenshot From 2025-12-11 15-41-23" src="https://github.com/user-attachments/assets/8f5a6223-78bb-4670-b23e-280e003df324" /> |
||
|
|
d44b9f8694 |
Adds UIDebug Overlay centered scene to examples/testbed/ui (#22052)
# Objective - Fixes #22019 ## Solution - Adds a new scene in the ui testbed that tests the generation of the Ui Debug Overlay. It only is viewable if you run the example with `--features bevy_ui_debug`. - Added a way to override the color used by the overlay to ensure that screenshots are consistent between CI runs (the line colors are generated based on the `Entity`, which changes every time you change the scene.) - Added the `--features bevy_ui_debug` flag to `example-run.yml`, ensuring that screenshots and their validations take this new scene into account. (I assumed I’m not supposed to add this to `validation-jobs.yml` under `run-examples-on-wasm`, but if I’m supposed to, let me know and I can add it. I figured a debug feature does not need that specific validation). ## Testing - I ran the command `cargo run --example testbed_ui --features bevy_ui_debug` and verified that debug lines showed up for regular outlines, for a node with `Visibility::Hidden` with `show_hidden = true`, and that clipped areas have outlines with `show_clipped = true`. I carouseled through all the scenes a couple of times - I also ran `cargo run --example testbed_ui ` and made sure nothing was ruined after multiple carousels through all the scenes. --- ## Showcase `cargo run --example testbed_ui --features bevy_ui_debug` , press spacebar 11x to see the scene. <img width="1282" height="750" alt="Screenshot 2025-12-06 at 10 03 25 PM" src="https://github.com/user-attachments/assets/f76ef3ac-b762-4eef-9843-573f362fe45a" /> |
||
|
|
c4b68363d8 |
Add a UiTransform centered scene to examples/testbed/ui (#21974)
# Objective Fixes #21815 ## Solution - Adds a new scene to `examples/testbed/ui` called `Transformations` that calls some basic `UiTransform`s on Nodes. This is my first PR for Bevy! ## Testing I ran the example via console to test out the scene. Tested on Desktop MacOS 15.7.1 ## Showcase <img width="902" height="780" alt="Screenshot 2025-11-29 at 11 29 52 AM" src="https://github.com/user-attachments/assets/602ab485-61dd-4887-a0ad-0624b3d79db8" /> To view, run in console: `cargo run --package bevy --example testbed_ui` Press spacebar 11x until you see the new scene. |
||
|
|
a37c14ce95 |
Add BorderRadius to Node (#21781)
# Objective There was discussion in the `Next Generation Scene/UI` about adding `BackgroundColor` to `Node`. I don't know about that, but it reminded me that border radius isn't part of `Node`. All the other properties that affect a `Node`'s shape are there, border radius should be there too. ## Solution * Add a `border_radius: BorderRadius` field. * Remove the `Component` derive from `BorderRadius`. ## Testing The internal changes are relatively trivial. There could be mistakes in the examples though, because of the large number of changes. |
||
|
|
6b34012ffa |
Rename cuboid to cube (#21393)
# Objective - Gizmos::cuboid is for drawing cubes, even the docs say so. A cuboid drawing function should take Cuboid as input. ## Solution - Rename ::cuboid to ::cube, making way for an actual cuboid drawing function. ## Testing - ci |
||
|
|
f07c12570a |
Renamed bevy_core_widgets to bevy_ui_widgets. (#20944)
Renamed `CoreXXX` components to `XXXBehavior`. Fixes: #20664 @alice-i-cecile @cart |
||
|
|
4d74baf1ae |
BufferedEvent -> Message Rename (#20953)
This renames the concept of `BufferedEvent` to `Message`, and updates our APIs, comments, and documentation to refer to these types as "messages" instead of "events". It also removes/updates anything that considers messages to be "observable", "listenable", or "triggerable". This is a followup to https://github.com/bevyengine/bevy/pull/20731, which omitted the `BufferedEvent -> Message` rename for brevity. See that post for rationale. |
||
|
|
eda118d033 |
Event Rearchitecture (#20731)
There is general consensus that our terminology for Events, "entity events", Observers, and BufferedEvents needs clarity. Additionally, many of us also agree that the current Observer system would benefit from additional static-ness: currently it is assumed that you can use events in pretty much any context, and they all go through the exact same code path. Alice put forth a proposal to [Overhaul Observers](https://hackmd.io/@bevy/rk4S92hmlg), and we have already partially implemented it for 0.17. I think it does a great job of outlining many of the issues at play, and it solves them reasonably well. But I _also_ think the proposed solution isn't yet ideal. Given that it is already partially implemented for 0.17, it is a breaking change, _and_ given that we have already broken the Observer API a number of times, I think we need to sort this out before the next release. This is a big changeset, but it is _largely_ just a reframing of what is already there. I haven't fundamentally changed the behaviors. I've just refined and constrained in a way that allows us to do what we are currently doing in a clearer, simpler, and more performant way. First, I'll give some quick notes on Alice's proposal (which you all should read if you haven't yet!): ### Notes on Alice's Proposal - I like the move toward a more static API - I think we've gone too far down the "separate terminology" path. The proposal introduces a zoo of apis, terms, and "subterms". I think we need to simplify our concepts and names to make this all easier to talk about and use in practice. - BroadcastEvent feels like the wrong name. EntityEvent is also "broadcast" in the exact same way - BufferedEvent is a completely different system than EntityEvent and BroadcastEvent. This muddles concepts too much. It needs its own standalone, single-word concept name. - "Universal observers": I think this should be fully context driven, rather than needing encoding in the API. - I agree we can't get rid of buffered events, and that merging them with "broadcast events" isn't helpful - I'm not quite sure how we'd make the proposed PropagateEvent subtrait work transparently. This can't be "layered on top" as a trait. It needs to be baked in at more fundamental level. * I don't like `app.add_broadcast_observers()`, `app.add_universal_observers()`, `Observer::entity_observer`, `Observer::broadcast`, etc. The `On` event should statically determine whether an observer is an "entity observer" or a "broadcast" Observer. This would already be encoded in the type system and is therefore something we can do on the developer's behalf. Likewise, any observer being registered at a top level is inherently _not_ a specific entity observer. All of these variants serve to make users guess and poke around in a way that is unnecessary. I want simple one word concept names, single constructors, etc. ### Proposed Principals - Static-ness: - Events should only be usable in the context they were defined to be used. - When triggered, Observers should *only* have access to fields and behaviors that are relevant: - Dont return Option or PLACEHOLDER: the field or function shouldn't exist - Entity events that don't support propagation shouldn't expose that functionality - Don't do unnecessary work at runtime - Event triggers shouldn't branch through every potential event code path - Don't clone potentially large lists of event context unnecessarily (Ex: we currently clone the component list for every observer invocation) - Minimize codegen - Don't recompile things redundantly. - Don't compile unnecessary code paths. - Clear and Simple - Minimize the number of concept names floating around, and lock each concept down heavily to a specific context - I'm convinced at this point that "buffered events" and "observer events" sharing concept names is wrong. We need two clean and clear terms, and I'm willing to give "buffered events" a slightly worse name if it means "observer events" can be nicer. - Don't throw the concept name "Event" out ... it is a very good name. Instead, constrain it to one specific thing. - Minimize our API surface - Events contain all context, including what used to be the "target". This lets people define the "target" name that makes the most sense for the context, and lets the documentation fully describe the context of that "target". ### Concepts - **Event** (the thing you "observe") - Rationale: "Event" is the clear choice for this concept. An "event" feels like something that happens in real time. "Event observers" are things that observe events when they occur (are triggered). Additionally, this is the concept that "propagates", and "event propagation" is a term people understand. - **Trigger**: (the verb that "causes" events to happen for targets). Events are Triggered. This can include additional context/ data that is passed to observers / informs the trigger behavior. Events have _exactly_ one Trigger. If you want a different trigger behavior, define a new event. This makes the system more static, more predictable, and easier to understand and document. `world.trigger_ref_with` makes it possible to pass in mutable reference to your own Trigger data, making it possible to customize the input trigger data and read out the final trigger data. - **Observer** (the thing that "observes" events): An event's `Trigger` determines which observers will run. - **Event Types**: You can build any "type" of event. The concept of a "target" has been removed. Instead, define a `Trigger` that expects a specific kind of event (ex: `E: EntityEvent`). - **EntityEvent** We add a new `EntityEvent` trait, which defines an `event.entity()` accessor. This is used by the `Trigger` impls : `EntityTrigger`, `PropagateEntityTrigger`, and `EntityComponentsTrigger`. - **Message** (the buffered thing you "read" and "write") - `Message` is a solid metaphor for what this is ... it is data that is written and then at some later point read by someone / something else. I expect existing consumers of "buffered events" to lament this name change, as "event" feels nicer. But having a separate name is within everyone's best interest. - **MessageReader** (the thing that reads messages) - **MessageWriter** (the thing that writes messages) ### The Changes - `Event` trait changes - Event is now used exclusively by Observers - Added `Event::Trigger`, which defines what trigger implementation this event will use - Added the `Trigger` trait - All of the shared / hard-coded observer trigger logic has been broken out into individual context-specific Trigger traits. - "Trigger Targets" have been removed. - Instead, Events, in combination with their Trigger impl, decide how they will be triggered. In general, this means that Events now include their "targets" as fields on the event. - APIs like `trigger_targets` have been replaced by `trigger`, which can now be used for any `Event` - `EntityEvent` trait changes - Propagation config has been removed from the `EntityEvent` trait. It now lives on the `Trigger` trait (specifically the `PropagateEntityTrigger` trait). - `EntityEvent` now provides `entity / entity_mut` accessors for the Event it is implemented for - `EntityEvent` defaults to having no propagation (uses the simpler `EntityTrigger`) - `#[entity_event(propagate)]` enables the "default" propagation logic (uses ChildOf). The existing `#[entity_event(traversal = X)]` has been renamed to `#[entity_event(propagate = X)` - Deriving `EntityEvent` requires either a single `MyEvent(Entity)`, the `entity` field name (`MyEvent { entity: Entity}`), or `MyEvent { #[event_entity] custom: Entity }` - Animation event changes - Animation events now have their own `AnimationEvent` trait, which sets the `AnimationEventTrigger`. This allows developers to pass in events that _dont_ include the Entity field (as this is set by the system). The custom trigger also opens the doors to cheaply passing in additional animation system context, accessible through `On` - `EntityComponentsTrigger` - The built in Add/Remove/etc lifecycle events now use the `EntityComponentsTrigger`, which passes in the components as additional state. This _significantly_ cuts down on clones, as it does a borrow rather than cloning the list into _each_ observer execution. - Each event now has an `entity` field. - Style changes - Prefer the event name for variables: `explode: On<Explode>` not `event: On<Explode>` - Prefer using the direct field name for the entity on entity events, rather than `event.entity()`. This allows us to use more specific names where appropriate, provides better / more contextual docs, and coaches developers to think of `On<MyEvent>` _as_ the event itself. Take a look at the changes to the examples and the built-in events to see what this looks like in practice. ### Downsides - Moving the "target" into the event adds some new constraints: - Triggering the same event for multiple entities requires multiple trigger calls. For "expensive" events (ex: lots of data attached to the event), this will be more awkward. Your options become: - Create multiple instances of the event, cloning the expensive data - Use `trigger_ref`, and mutate the event on each call to change the target. - Move the "expensive" shared data into the Trigger, and use `trigger_ref_with`` - We could build a new EntityEvent method that abstracts over the "event mutation" behavior and provides something like the old `trigger_target` behavior. - Use a different `EntityTargetTrigger` (not currently provided by bevy, but we could), which brings back the old behavior. This would be used with `trigger_with` to replicate the old pattern: `world.trigger_with(MyEvent, [e1, e2].into())` (or we could make the `into()` implicit) - Bubbling the event involves mutating the event to set the entity. This means that `trigger_ref` will result in the event's `EntityEvent::entity()` being the final bubbled entity instead of the initial entity. - Some APIs (trivially) benefit from the "target entity" being separate from the event. Specifically, this new API requires changes to the "Animation Event" system in AnimationPlayer. I think this is actually a good change set, as it allows us to: - Cheaply expose more animation state as part of a new AnimationEventTrigger impl - Move that "implict" entity target provided by the AnimationPlayer into the AnimationEventTrigger - Encode the "animation event trigger-ness" of the event into the type itself (by requiring `#[event(trigger = AnimationEventTrigger)]`) - By not implementing Default for AnimationEventTrigger, we can block animation events from being fired manually by the user. ### Draft TODO - [x] Fill in documentation and update existing docs - [ ] Benchmark: I expect this impl to be significantly faster. There might also be tangible binary size improvements, as I've removed a lot of redundant codegen. - [x] Update release notes and migration guides ### Next Steps - The `BufferedEvent -> Message` rename was not included to keep the size down. Fixes #19648 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |
||
|
|
f6b77e4e49 |
bevy_post_process (#20778)
# Objective Split out post process effects from bevy_core_pipeline because they are not core pipelines. ## Solution @IceSentry proposed something like this, not sure if the split is exactly as he envisioned but this seems reasonable to me. The goal is to move absolutely everything possible out of bevy_core_pipelines to unblock bevy_pbr/bevy_sprite_render compilation. Future PRs may attempt to move more little bits out. ## Testing |
||
|
|
b43a73df3f |
Rename DespawnOnExitState to DespawnOnExit (#20872)
# Objective - `StateScoped` was renamed to `DespawnOnExitState`, and we introduced `DespawnOnEnterState` - This is redundant: the type it wraps is already a state - Often, state enums have `State` in their names, leading to stutter: `DespawnOnExitState(GameState::Gameplay)` - This component is *very* common in games. The longer name makes it clunkier to use, so we should be careful when expanding it. I think the added clarity is good, but we can do better. ## Solution - Compromise to `DespawnOnExit` - Do the same for `DespawnOnEnter` ## Testing - CI |
||
|
|
6eb011c30c |
Replace from_ constructors on TextFont with From impls (#20450)
# Objective - Closes #20353 ## Solution - Removed `TextFont::from_font` and `TextFont::from_line_height`. - Updated `examples/testbed/2d.rs` to use the `Form` impl. - Added migration guide. |
||
|
|
c365f97cb9 |
another text color fix (#20856)
# Objective Fixes #20854 ## Solution Query for the next color on span changes before queuing the next glyph. ## Testing Added two more cases to `testbed_ui`'s text example, the three lines of coloured text should all look identical: ```cargo run --example testbed_ui``` <img width="487" height="234" alt="text-color" src="https://github.com/user-attachments/assets/78d10141-9b2f-4a57-9b64-eda62da46db5" /> |
||
|
|
e46fd71d80 |
Add coloured text to the ui testbed example's text module (#20836)
# Objective Add some coloured text to the UI testbed example. |
||
|
|
13877fa84d |
Add a new trait to accept more types in the Val-helper functions (#20551)
# Objective - Allow the `Val`-helper functions to accept more types besides just `f32` Fixes #20549 ## Solution - Adds a new trait that can be implemented for numbers - That trait has a method that converts `self` to `f32` ## Testing - I tested it using Rust's testing framework (although I didn't leave the tests in, as I don't deem them important enough) <details> <summary>Rust test</summary> ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_val_helpers_work() { let p = px(10_u8); assert_eq!(p, Val::Px(10.0)); let p = px(10_u16); assert_eq!(p, Val::Px(10.0)); let p = px(10_u32); assert_eq!(p, Val::Px(10.0)); let p = px(10_u64); assert_eq!(p, Val::Px(10.0)); let p = px(10_u128); assert_eq!(p, Val::Px(10.0)); let p = px(10_i8); assert_eq!(p, Val::Px(10.0)); let p = px(10_i16); assert_eq!(p, Val::Px(10.0)); let p = px(10_i32); assert_eq!(p, Val::Px(10.0)); let p = px(10_i64); assert_eq!(p, Val::Px(10.0)); let p = px(10_i128); assert_eq!(p, Val::Px(10.0)); let p = px(10.3_f32); assert_eq!(p, Val::Px(10.3)); let p = px(10.6_f64); assert_eq!(p, Val::Px(10.6)); } } ``` </details> --- ## Showcase ```rust // Same as Val::Px(10.) px(10); px(10_u8); px(10.0); ``` |
||
|
|
5058f8a9e6 |
Improve On Terminology (#20648)
# Objective Fixes #19263 (and expands on it) Within `Observers`, we have started to distance ourselves from the "trigger" terminology. Specifically, we have renamed `Trigger` to `On`. I think this was a very good move, but we're currently in an awkward middle ground state. Users interact with `On` as if it were an event: `On<E>` exposes the event and derefs directly to it. I think we should embrace this mindset fully, in the interest of clarity. ## Solution - Rename all `trigger: On<SomeEvent>` cases to `event: On<SomeEvent>`. - Rename `On::target` to `On::entity`. This reads _much_ better when writing `query.get(event.entity())` and pairs more effectively with the `EntityEvent` terminology. - Rename `On::original_target` to `On::original_entity`, for the same reasons. - Take advantage of the `Deref` behavior where appropriate ```rust // Before entity.observe(|trigger: On<Explode>| { println!("{} exploded!", trigger.target()); }) // After entity.observe(|event: On<Explode>| { println!("{} exploded!", event.entity()); }) ``` |
||
|
|
590da83c24 |
Fix glTF coordinate conversion not converting mesh bounds (#20608)
glTF coordinate conversion is applied to mesh assets, but not their bounds: <img width="1602" height="939" alt="image" src="https://github.com/user-attachments/assets/b2bd0c4b-c9aa-4d53-b33d-c3463af7480e" /> After the fix: <img width="1602" height="939" alt="image" src="https://github.com/user-attachments/assets/ffb40e11-14e4-427f-adfb-5007e6b2cb0a" /> The PR also adds the above scene to `testbed_3d`. ## Testing ```sh cargo run --example testbed_3d ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
03dd839b82 |
Use bevy::camera in examples instead of bevy::render::camera re-export (#20477)
# Objective - Prepare for removing re-exports ## Solution - title ## Testing - cargo check --examples |