custom
355 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
d9190e4ff6
|
Add Support for Triggering Events via AnimationEvent s (#15538)
# 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.  --------- 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> |
||
![]() |
d0edbdac78
|
Fix cargo-ndk build command (#15648)
# Objective - Fix cargo-ndk build command documentation in readme. ```sh ❯ cargo ndk -t arm64-v8a build -o android_example/app/src/main/jniLibs Building arm64-v8a (aarch64-linux-android) error: unexpected argument '-o' found ``` ## Solution - Move "build" to the end of the command. ## Testing - With the new command order building works. ```sh ❯ cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build Building arm64-v8a (aarch64-linux-android) Compiling bevy_ptr v0.15.0-dev (/home/eero/repos/bevy/crates/bevy_ptr) Compiling bevy_macro_utils v0.15.0-dev (/home/eero/repos/bevy/crates/bevy_macro_utils) Compiling event-listener v5.3.1 ... rest of compilation ... ``` |
||
![]() |
f86ee32576
|
Add UI GhostNode (#15341)
# 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> |
||
![]() |
e924df0e1a
|
Add features to switch NativeActivity and GameActivity usage (#12095)
# 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> |
||
![]() |
c323db02e0
|
Add sub_camera_view , enabling sheared projection (#15537)
# 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  - 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> |
||
![]() |
120d66482e
|
Clarify purpose of shader_instancing example (#15456)
# 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> |
||
![]() |
78a3aae81b
|
feat(gltf): add name component to gltf mesh primitive (#13912)
# 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> |
||
![]() |
c1486654d7
|
QuerySingle family of system params (#15476)
# 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. |
||
![]() |
89e98b208f
|
Initial implementation of the Bevy Remote Protocol (Adopted) (#14880)
# 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> |
||
![]() |
55dddaf72e
|
UI Scrolling (#15291)
# 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  ## Alternatives - `bevy_ui` doesn't support scrolling. - `bevy_ui` implements scrolling with a one-frame delay on reactions to layout changes. |
||
![]() |
8154164f1b
|
Allow animation clips to animate arbitrary properties. (#15282)
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. |
||
![]() |
e3b6b125a0
|
Add sprite and mesh alteration examples (#15298)
# 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> |
||
![]() |
2ae5a21009
|
Implement percentage-closer soft shadows (PCSS). (#13497)
[*Percentage-closer soft shadows*] are a technique from 2004 that allow shadows to become blurrier farther from the objects that cast them. It works by introducing a *blocker search* step that runs before the normal shadow map sampling. The blocker search step detects the difference between the depth of the fragment being rasterized and the depth of the nearby samples in the depth buffer. Larger depth differences result in a larger penumbra and therefore a blurrier shadow. To enable PCSS, fill in the `soft_shadow_size` value in `DirectionalLight`, `PointLight`, or `SpotLight`, as appropriate. This shadow size value represents the size of the light and should be tuned as appropriate for your scene. Higher values result in a wider penumbra (i.e. blurrier shadows). When using PCSS, temporal shadow maps (`ShadowFilteringMethod::Temporal`) are recommended. If you don't use `ShadowFilteringMethod::Temporal` and instead use `ShadowFilteringMethod::Gaussian`, Bevy will use the same technique as `Temporal`, but the result won't vary over time. This produces a rather noisy result. Doing better would likely require downsampling the shadow map, which would be complex and slower (and would require PR #13003 to land first). In addition to PCSS, this commit makes the near Z plane for the shadow map configurable on a per-light basis. Previously, it had been hardcoded to 0.1 meters. This change was necessary to make the point light shadow map in the example look reasonable, as otherwise the shadows appeared far too aliased. A new example, `pcss`, has been added. It demonstrates the percentage-closer soft shadow technique with directional lights, point lights, spot lights, non-temporal operation, and temporal operation. The assets are my original work. Both temporal and non-temporal shadows are rather noisy in the example, and, as mentioned before, this is unavoidable without downsampling the depth buffer, which we can't do yet. Note also that the shadows don't look particularly great for point lights; the example simply isn't an ideal scene for them. Nevertheless, I felt that the benefits of the ability to do a side-by-side comparison of directional and point lights outweighed the unsightliness of the point light shadows in that example, so I kept the point light feature in. Fixes #3631. [*Percentage-closer soft shadows*]: https://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf ## Changelog ### Added * Percentage-closer soft shadows (PCSS) are now supported, allowing shadows to become blurrier as they stretch away from objects. To use them, set the `soft_shadow_size` field in `DirectionalLight`, `PointLight`, or `SpotLight`, as applicable. * The near Z value for shadow maps is now customizable via the `shadow_map_near_z` field in `DirectionalLight`, `PointLight`, and `SpotLight`. ## Screenshots PCSS off:  PCSS on:  --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com> |
||
![]() |
8e7ef64bb1
|
Split zoom/orbit into separate examples (#15135)
# Objective As previously discussed, split camera zoom and orbiting examples to keep things less cluttered. See discussion on #15092 for context. |
||
![]() |
b9b43ad89c
|
Add examples for orthographic and perspective zoom (#15092)
# Objective Add examples for zooming (and orbiting) orthographic and perspective cameras. I'm pretty green with 3D, so please treat with suspicion! I note that if/when #15075 is merged, `.scale` will go away so this example uses `.scaling_mode`. Closes #2580 |
||
![]() |
8ac745ab10
|
UI texture slice texture flipping reimplementation (#15034)
# Objective Fixes #15032 ## Solution Reimplement support for the `flip_x` and `flip_y` fields. This doesn't flip the border geometry, I'm not really sure whether that is desirable or not. Also fixes a bug that was causing the side and center slices to tile incorrectly. ### Testing ``` cargo run --example ui_texture_slice_flip_and_tile ``` ## Showcase <img width="787" alt="nearest" src="https://github.com/user-attachments/assets/bc044bae-1748-42ba-92b5-0500c87264f6"> With tiling need to use nearest filtering to avoid bleeding between the slices. --------- Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
d2624765d0
|
Implement animation masks, allowing fine control of the targets that animations affect. (#15013)
This commit adds support for *masks* to the animation graph. A mask is a set of animation targets (bones) that neither a node nor its descendants are allowed to animate. Animation targets can be assigned one or more *mask group*s, which are specific to a single graph. If a node masks out any mask group that an animation target belongs to, animation curves for that target will be ignored during evaluation. The canonical use case for masks is to support characters holding objects. Typically, character animations will contain hand animations in the case that the character's hand is empty. (For example, running animations may close a character's fingers into a fist.) However, when the character is holding an object, the animation must be altered so that the hand grips the object. Bevy currently has no convenient way to handle this. The only workaround that I can see is to have entirely separate animation clips for characters' hands and bodies and keep them in sync, which is burdensome and doesn't match artists' expectations from other engines, which all effectively have support for masks. However, with mask group support, this task is simple. We assign each hand to a mask group and parent all character animations to a node. When a character grasps an object in hand, we position the fingers as appropriate and then enable the mask group for that hand in that node. This allows the character's animations to run normally, while the object remains correctly attached to the hand. Note that even with this PR, we won't have support for running separate animations for a character's hand and the rest of the character. This is because we're missing additive blending: there's no way to combine the two masked animations together properly. I intend that to be a follow-up PR. The major engines all have support for masks, though the workflow varies from engine to engine: * Unity has support for masks [essentially as implemented here], though with layers instead of a tree. However, when using the Mecanim ("Humanoid") feature, precise control over bones is lost in favor of predefined muscle groups. * Unreal has a feature named [*layered blend per bone*]. This allows for separate blend weights for different bones, effectively achieving masks. I believe that the combination of blend nodes and masks make Bevy's animation graph as expressible as that of Unreal, once we have support for additive blending, though you may have to use more nodes than you would in Unreal. Moreover, separating out the concepts of "blend weight" and "which bones this node applies to" seems like a cleaner design than what Unreal has. * Godot's `AnimationTree` has the notion of [*blend filters*], which are essentially the same as masks as implemented in this PR. Additionally, this patch fixes a bug with weight evaluation whereby weights weren't properly propagated down to grandchildren, because the weight evaluation for a node only checked its parent's weight, not its evaluated weight. I considered submitting this as a separate PR, but given that this PR refactors that code entirely to support masks and weights under a unified "evaluated node" concept, I simply included the fix here. A new example, `animation_masks`, has been added. It demonstrates how to toggle masks on and off for specific portions of a skin. This is part of #14395, but I'm going to defer closing that issue until we have additive blending. [essentially as implemented here]: https://docs.unity3d.com/560/Documentation/Manual/class-AvatarMask.html [*layered blend per bone*]: https://dev.epicgames.com/documentation/en-us/unreal-engine/using-layered-animations-in-unreal-engine [*blend filters*]: https://docs.godotengine.org/en/stable/tutorials/animation/animation_tree.html ## Migration Guide * The serialized format of animation graphs has changed with the addition of animation masks. To upgrade animation graph RON files, add `mask` and `mask_groups` fields as appropriate. (They can be safely set to zero.) |
||
![]() |
a4640046fc
|
Adds ShaderStorageBuffer asset (#14663)
Adds a new `Handle<Storage>` asset type that can be used as a render asset, particularly for use with `AsBindGroup`. Closes: #13658 # Objective Allow users to create storage buffers in the main world without having to access the `RenderDevice`. While this resource is technically available, it's bad form to use in the main world and requires mixing rendering details with main world code. Additionally, this makes storage buffers easier to use with `AsBindGroup`, particularly in the following scenarios: - Sharing the same buffers between a compute stage and material shader. We already have examples of this for storage textures (see game of life example) and these changes allow a similar pattern to be used with storage buffers. - Preventing repeated gpu upload (see the previous easier to use `Vec` `AsBindGroup` option). - Allow initializing custom materials using `Default`. Previously, the lack of a `Default` implement for the raw `wgpu::Buffer` type made implementing a `AsBindGroup + Default` bound difficult in the presence of buffers. ## Solution Adds a new `Handle<Storage>` asset type that is prepared into a `GpuStorageBuffer` render asset. This asset can either be initialized with a `Vec<u8>` of properly aligned data or with a size hint. Users can modify the underlying `wgpu::BufferDescriptor` to provide additional usage flags. ## Migration Guide The `AsBindGroup` `storage` attribute has been modified to reference the new `Handle<Storage>` asset instead. Usages of Vec` should be converted into assets instead. --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
![]() |
3540b87e17
|
Add bevy_picking sprite backend (#14757)
# Objective Add `bevy_picking` sprite backend as part of the `bevy_mod_picking` upstreamening (#12365). ## Solution More or less a copy/paste from `bevy_mod_picking`, with the changes [here](https://github.com/aevyrie/bevy_mod_picking/pull/354). I'm putting that link here since those changes haven't yet made it through review, so should probably be reviewed on their own. ## Testing I couldn't find any sprite-backend-specific tests in `bevy_mod_picking` and unfortunately I'm not familiar enough with Bevy's testing patterns to write tests for code that relies on windowing and input. I'm willing to break the pointer hit system into testable blocks and add some more modular tests if that's deemed important enough to block, otherwise I can open an issue for adding tests as follow-up. ## Follow-up work - More docs/tests - Ignore pick events on transparent sprite pixels with potential opt-out --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> |
||
![]() |
510fce9af3
|
Allow fog density texture to be scrolled over time with an offset (#14868)
# Objective - The goal of this PR is to make it possible to move the density texture of a `FogVolume` over time in order to create dynamic effects like fog moving in the wind. - You could theoretically move the `FogVolume` itself, but this is not ideal, because the `FogVolume` AABB would eventually leave the area. If you want an area to remain foggy while also creating the impression that the fog is moving in the wind, a scrolling density texture is a better solution. ## Solution - The PR adds a `density_texture_offset` field to the `FogVolume` component. This offset is in the UVW coordinates of the density texture, meaning that a value of `(0.5, 0.0, 0.0)` moves the 3d texture by half along the x-axis. - Values above 1.0 are wrapped, a 1.5 offset is the same as a 0.5 offset. This makes it so that the density texture wraps around on the other side, meaning that a repeating 3d noise texture can seamlessly scroll forever. It also makes it easy to move the density texture over time by simply increasing the offset every frame. ## Testing - A `scrolling_fog` example has been added to demonstrate the feature. It uses the offset to scroll a repeating 3d noise density texture to create the impression of fog moving in the wind. - The camera is looking at a pillar with the sun peaking behind it. This highlights the effect the changing density has on the volumetric lighting interactions. - Temporal anti-aliasing combined with the `jitter` option of `VolumetricFogSettings` is used to improve the quality of the effect. --- ## Showcase https://github.com/user-attachments/assets/3aa50ebd-771c-4c99-ab5d-255c0c3be1a8 |
||
![]() |
eec38004a8
|
Add example demonstrating how to enable / disable diagnostics (#14741)
# Objective fixes #14569 ## Solution added an example to the diagnostic examples and linked the code to the docs of the diagnostic library itself. ## Testing I tested locally on my laptop in a web browser. Looked fine. You are able to collapse the whole "intro" part of the doc to get to the links sooner (for those who may think that including the example code here is annoying to scroll through) I would like people to run ```cargo doc``` and go the bevy_diagnostic page to see if they have any issues or suggestions. --- ## Showcase <img width="1067" alt="Screenshot 2024-08-14 at 12 52 16" src="https://github.com/user-attachments/assets/70b6c18a-0bb9-4656-ba53-c416f62c6116"> --------- Co-authored-by: dpeke <dpekelis@funstage.com> |
||
![]() |
6adf31babf
|
hooking up observers and clicking for ui node (#14695)
Makes the newly merged picking usable for UI elements. currently it both triggers the events, as well as sends them as throught commands.trigger_targets. We should probably figure out if this is needed for them all. # Objective Hooks up obserers and picking for a very simple example ## Solution upstreamed the UI picking backend from bevy_mod_picking ## Testing tested with the new example picking/simple_picking.rs --- --------- Co-authored-by: Lixou <82600264+DasLixou@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com> |
||
![]() |
5abc32ceda
|
Add 2d opaque phase with depth buffer (#13069)
This PR is based on top of #12982 # Objective - Mesh2d currently only has an alpha blended phase. Most sprites don't need transparency though. - For some 2d games it can be useful to have a 2d depth buffer ## Solution - Add an opaque phase to render Mesh2d that don't need transparency - This phase currently uses the `SortedRenderPhase` to make it easier to implement based on the already existing transparent phase. A follow up PR will switch this to `BinnedRenderPhase`. - Add a 2d depth buffer - Use that depth buffer in the transparent phase to make sure that sprites and transparent mesh2d are displayed correctly ## Testing I added the mesh2d_transforms example that layers many opaque and transparent mesh2d to make sure they all get displayed correctly. I also confirmed it works with sprites by modifying that example locally. --- ## Changelog - Added `AlphaMode2d` - Added `Opaque2d` render phase - Camera2d now have a `ViewDepthTexture` component ## Migration Guide - `ColorMaterial` now contains `AlphaMode2d`. To keep previous behaviour, use `AlphaMode::BLEND`. If you know your sprite is opaque, use `AlphaMode::OPAQUE` ## Follow up PRs - See tracking issue: #13265 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |
||
![]() |
3360b45153
|
Expose winit's MonitorHandle (#13669)
# Objective Adds a new `Monitor` component representing a winit `MonitorHandle` that can be used to spawn new windows and check for system monitor information. Closes #12955. ## Solution For every winit event, check available monitors and spawn them into the world as components. ## Testing TODO: - [x] Test plugging in and unplugging monitor during app runtime - [x] Test spawning a window on a second monitor by entity id - [ ] Since this touches winit, test all platforms --- ## Changelog - Adds a new `Monitor` component that can be queried for information about available system monitors. ## Migration Guide - `WindowMode` variants now take a `MonitorSelection`, which can be set to `MonitorSelection::Primary` to mirror the old behavior. --------- Co-authored-by: Pascal Hertleif <pascal@technocreatives.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Pascal Hertleif <killercup@gmail.com> |
||
![]() |
6f7c554daa
|
Fix common capitalization errors in documentation (#14562)
WASM -> Wasm MacOS -> macOS Nothing important, just something that annoyed me for a while :) |
||
![]() |
bfcb19a871
|
Add example showing how to use SpecializedMeshPipeline (#14370)
# Objective - A lot of mid-level rendering apis are hard to figure out because they don't have any examples - SpecializedMeshPipeline can be really useful in some cases when you want more flexibility than a Material without having to go to low level apis. ## Solution - Add an example showing how to make a custom `SpecializedMeshPipeline`. ## Testing - Did you test these changes? If so, how? - Are there any parts that need more testing? - How can other people (reviewers) test your changes? Is there anything specific they need to know? - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? --- ## Showcase The examples just spawns 3 triangles in a triangle pattern.  --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
9575b20d31
|
Track source location in change detection (#14034)
# Objective - Make it possible to know *what* changed your component or resource. - Common need when debugging, when you want to know the last code location that mutated a value in the ECS. - This feature would be very useful for the editor alongside system stepping. ## Solution - Adds the caller location to column data. - Mutations now `track_caller` all the way up to the public API. - Commands that invoke these functions immediately call `Location::caller`, and pass this into the functions, instead of the functions themselves attempting to get the caller. This would not work for commands which are deferred, as the commands are executed by the scheduler, not the user's code. ## Testing - The `component_change_detection` example now shows where the component was mutated: ``` 2024-07-28T06:57:48.946022Z INFO component_change_detection: Entity { index: 1, generation: 1 }: New value: MyComponent(0.0) 2024-07-28T06:57:49.004371Z INFO component_change_detection: Entity { index: 1, generation: 1 }: New value: MyComponent(1.0) 2024-07-28T06:57:49.012738Z WARN component_change_detection: Change detected! -> value: Ref(MyComponent(1.0)) -> added: false -> changed: true -> changed by: examples/ecs/component_change_detection.rs:36:23 ``` - It's also possible to inspect change location from a debugger: <img width="608" alt="image" src="https://github.com/user-attachments/assets/c90ecc7a-0462-457a-80ae-42e7f5d346b4"> --- ## Changelog - Added source locations to ECS change detection behind the `track_change_detection` flag. ## Migration Guide - Added `changed_by` field to many internal ECS functions used with change detection when the `track_change_detection` feature flag is enabled. Use Location::caller() to provide the source of the function call. --------- Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
![]() |
9da18cce2a
|
Add support for environment map transformation (#14290)
# Objective - Fixes: https://github.com/bevyengine/bevy/issues/14036 ## Solution - Add a world space transformation for the environment sample direction. ## Testing - I have tested the newly added `transform` field using the newly added `rotate_environment_map` example. https://github.com/user-attachments/assets/2de77c65-14bc-48ee-b76a-fb4e9782dbdb ## Migration Guide - Since we have added a new filed to the `EnvironmentMapLight` struct, users will need to include `..default()` or some rotation value in their initialization code. |
||
![]() |
3484bd916f
|
Cyclic splines (#14106)
# Objective Fill a gap in the functionality of our curve constructions by allowing users to easily build cyclic curves from control data. ## Solution Here I opted for something lightweight and discoverable. There is a new `CyclicCubicGenerator` trait with a method `to_curve_cyclic` which uses splines' control data to create curves that are cyclic. For now, its signature is exactly like that of `CubicGenerator` — `to_curve_cyclic` just yields a `CubicCurve`: ```rust /// Implement this on cubic splines that can generate a cyclic cubic curve from their spline parameters. /// /// This makes sense only when the control data can be interpreted cyclically. pub trait CyclicCubicGenerator<P: VectorSpace> { /// Build a cyclic [`CubicCurve`] by computing the interpolation coefficients for each curve segment. fn to_curve_cyclic(&self) -> CubicCurve<P>; } ``` This trait has been implemented for `CubicHermite`, `CubicCardinalSpline`, `CubicBSpline`, and `LinearSpline`: <img width="753" alt="Screenshot 2024-07-01 at 8 58 27 PM" src="https://github.com/bevyengine/bevy/assets/2975848/69ae0802-3b78-4fb9-b73a-6f842cf3b33c"> <img width="628" alt="Screenshot 2024-07-01 at 9 00 14 PM" src="https://github.com/bevyengine/bevy/assets/2975848/2992175a-a96c-40fc-b1a1-5206c3572cde"> <img width="606" alt="Screenshot 2024-07-01 at 8 59 36 PM" src="https://github.com/bevyengine/bevy/assets/2975848/9e99eb3a-dbe6-42da-886c-3d3e00410d03"> <img width="603" alt="Screenshot 2024-07-01 at 8 59 01 PM" src="https://github.com/bevyengine/bevy/assets/2975848/d037bc0c-396a-43af-ab5c-fad9a29417ef"> (Each type pictured respectively with the control points rendered as green spheres; tangents not pictured in the case of the Hermite spline.) These curves are all parametrized so that the output of `to_curve` and the output of `to_curve_cyclic` are similar. For instance, in `CubicCardinalSpline`, the first output segment is a curve segment joining the first and second control points in each, although it is constructed differently. In the other cases, the segments from `to_curve` are a subset of those in `to_curve_cyclic`, with the new segments appearing at the end. ## Testing I rendered cyclic splines from control data and made sure they looked reasonable. Existing tests are intact for splines where previous code was modified. (Note that the coefficient computation for cyclic spline segments is almost verbatim identical to that of their non-cyclic counterparts.) The Bezier benchmarks also look fine. --- ## Changelog - Added `CyclicCubicGenerator` trait to `bevy_math::cubic_splines` for creating cyclic curves from control data. - Implemented `CyclicCubicGenerator` for `CubicHermite`, `CubicCardinalSpline`, `CubicBSpline`, and `LinearSpline`. - `bevy_math` now depends on `itertools`. --- ## Discussion ### Design decisions The biggest thing here is just the approach taken in the first place: namely, the cyclic constructions use new methods on the same old structs. This choice was made to reduce friction and increase discoverability but also because creating new ones just seemed unnecessary: the underlying data would have been the same, so creating something like "`CyclicCubicBSpline`" whose internally-held control data is regarded as cyclic in nature doesn't really accomplish much — the end result for the user is basically the same either way. Similarly, I don't presently see a pressing need for `to_curve_cyclic` to output something other than a `CubicCurve`, although changing this in the future may be useful. See below. A notable omission here is that `CyclicCubicGenerator` is not implemented for `CubicBezier`. This is not a gap waiting to be filled — `CubicBezier` just doesn't have enough data to join its start with its end without just making up the requisite control points wholesale. In all the cases where `CyclicCubicGenerator` has been implemented here, the fashion in which the ends are connected is quite natural and follows the semantics of the associated spline construction. ### Future direction There are two main things here: 1. We should investigate whether we should do something similar for NURBS. I just don't know that much about NURBS at the moment, so I regarded this as out of scope for the PR. 2. We may eventually want to change the output type of `CyclicCubicGenerator::to_curve_cyclic` to a type which reifies the cyclic nature of the curve output. This wasn't done in this PR because I'm unsure how much value a type-level guarantee of cyclicity actually has, but if some useful features make sense only in the case of cyclic curves, this might be worth pursuing. |
||
![]() |
20c6bcdba4
|
Allow volumetric fog to be localized to specific, optionally voxelized, regions. (#14099)
Currently, volumetric fog is global and affects the entire scene uniformly. This is inadequate for many use cases, such as local smoke effects. To address this problem, this commit introduces *fog volumes*, which are axis-aligned bounding boxes (AABBs) that specify fog parameters inside their boundaries. Such volumes can also specify a *density texture*, a 3D texture of voxels that specifies the density of the fog at each point. To create a fog volume, add a `FogVolume` component to an entity (which is included in the new `FogVolumeBundle` convenience bundle). Like light probes, a fog volume is conceptually a 1×1×1 cube centered on the origin; a transform can be used to position and resize this region. Many of the fields on the existing `VolumetricFogSettings` have migrated to the new `FogVolume` component. `VolumetricFogSettings` on a camera is still needed to enable volumetric fog. However, by itself `VolumetricFogSettings` is no longer sufficient to enable volumetric fog; a `FogVolume` must be present. Applications that wish to retain the old global fog behavior can simply surround the scene with a large fog volume. By way of implementation, this commit converts the volumetric fog shader from a full-screen shader to one applied to a mesh. The strategy is different depending on whether the camera is inside or outside the fog volume. If the camera is inside the fog volume, the mesh is simply a plane scaled to the viewport, effectively falling back to a full-screen pass. If the camera is outside the fog volume, the mesh is a cube transformed to coincide with the boundaries of the fog volume's AABB. Importantly, in the latter case, only the front faces of the cuboid are rendered. Instead of treating the boundaries of the fog as a sphere centered on the camera position, as we did prior to this patch, we raytrace the far planes of the AABB to determine the portion of each ray contained within the fog volume. We then raymarch in shadow map space as usual. If a density texture is present, we modulate the fixed density value with the trilinearly-interpolated value from that texture. Furthermore, this patch introduces optional jitter to fog volumes, intended for use with TAA. This modifies the position of the ray from frame to frame using interleaved gradient noise, in order to reduce aliasing artifacts. Many implementations of volumetric fog in games use this technique. Note that this patch makes no attempt to write a motion vector; this is because when a view ray intersects multiple voxels there's no single direction of motion. Consequently, fog volumes can have ghosting artifacts, but because fog is "ghostly" by its nature, these artifacts are less objectionable than they would be for opaque objects. A new example, `fog_volumes`, has been added. It demonstrates a single fog volume containing a voxelized representation of the Stanford bunny. The existing `volumetric_fog` example has been updated to use the new local volumetrics API. ## Changelog ### Added * Local `FogVolume`s are now supported, to localize fog to specific regions. They can optionally have 3D density voxel textures for precise control over the distribution of the fog. ### Changed * `VolumetricFogSettings` on a camera no longer enables volumetric fog; instead, it simply enables the processing of `FogVolume`s within the scene. ## Migration Guide * A `FogVolume` is now necessary in order to enable volumetric fog, in addition to `VolumetricFogSettings` on the camera. Existing uses of volumetric fog can be migrated by placing a large `FogVolume` surrounding the scene. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <mockersf@gmail.com> |
||
![]() |
73d7e89a18
|
remove rounded_borders and merge with borders example (#14317)
# Objective The borders example is separate from the rounded borders example. If you find the borders example, you may miss the rounded borders example. ## Solution Merge the examples in a basic way, since there is enough room to show all options at the same time. I also considered renaming the borders and rounded borders examples so that they would be located next to each other in repo and UI, but it felt like having a singular example was better. ## Testing ``` cargo run --example borders ``` --- ## Showcase The merged example looks like this:  |
||
![]() |
276815a9a0
|
examples: Add Type Data reflection example (#13903)
# Objective Type data is a **super** useful tool to know about when working with reflection. However, most users don't fully understand how it works or that you can use it for more than just object-safe traits. This is unfortunate because it can be surprisingly simple to manually create your own type data. We should have an example detailing how type works, how users can define their own, and how thy can be used. ## Solution Added a `type_data` example. This example goes through all the major points about type data: - Why we need them - How they can be defined - The two ways they can be registered - A list of common/important type data provided by Bevy I also thought it might be good to go over the `#[reflect_trait]` macro as part of this example since it has all the other context, including how to define type data in places where `#[reflect_trait]` won't work. Because of this, I removed the `trait_reflection` example. ## Testing You can run the example locally with the following command: ``` cargo run --example type_data ``` --- ## Changelog - Added the `type_data` example - Removed the `trait_reflection` example |
||
![]() |
fcda67e894
|
Start a built-in postprocessing stack, and implement chromatic aberration in it. (#13695)
This commit creates a new built-in postprocessing shader that's designed to hold miscellaneous postprocessing effects, and starts it off with chromatic aberration. Possible future effects include vignette, film grain, and lens distortion. [Chromatic aberration] is a common postprocessing effect that simulates lenses that fail to focus all colors of light to a single point. It's often used for impact effects and/or horror games. This patch uses the technique from *Inside* ([Gjøl & Svendsen 2016]), which allows the developer to customize the particular color pattern to achieve different effects. Unity HDRP uses the same technique, while Unreal has a hard-wired fixed color pattern. A new example, `post_processing`, has been added, in order to demonstrate the technique. The existing `post_processing` shader has been renamed to `custom_post_processing`, for clarity. [Chromatic aberration]: https://en.wikipedia.org/wiki/Chromatic_aberration [Gjøl & Svendsen 2016]: https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf   ## Changelog ### Added * Chromatic aberration is now available as a built-in postprocessing effect. To use it, add `ChromaticAberration` to your camera. |
||
![]() |
ed2b8e0f35
|
Minimal Bubbling Observers (#13991)
# Objective Add basic bubbling to observers, modeled off `bevy_eventlistener`. ## Solution - Introduce a new `Traversal` trait for components which point to other entities. - Provide a default `TraverseNone: Traversal` component which cannot be constructed. - Implement `Traversal` for `Parent`. - The `Event` trait now has an associated `Traversal` which defaults to `TraverseNone`. - Added a field `bubbling: &mut bool` to `Trigger` which can be used to instruct the runner to bubble the event to the entity specified by the event's traversal type. - Added an associated constant `SHOULD_BUBBLE` to `Event` which configures the default bubbling state. - Added logic to wire this all up correctly. Introducing the new associated information directly on `Event` (instead of a new `BubblingEvent` trait) lets us dispatch both bubbling and non-bubbling events through the same api. ## Testing I have added several unit tests to cover the common bugs I identified during development. Running the unit tests should be enough to validate correctness. The changes effect unsafe portions of the code, but should not change any of the safety assertions. ## Changelog Observers can now bubble up the entity hierarchy! To create a bubbling event, change your `Derive(Event)` to something like the following: ```rust #[derive(Component)] struct MyEvent; impl Event for MyEvent { type Traverse = Parent; // This event will propagate up from child to parent. const AUTO_PROPAGATE: bool = true; // This event will propagate by default. } ``` You can dispatch a bubbling event using the normal `world.trigger_targets(MyEvent, entity)`. Halting an event mid-bubble can be done using `trigger.propagate(false)`. Events with `AUTO_PROPAGATE = false` will not propagate by default, but you can enable it using `trigger.propagate(true)`. If there are multiple observers attached to a target, they will all be triggered by bubbling. They all share a bubbling state, which can be accessed mutably using `trigger.propagation_mut()` (`trigger.propagate` is just sugar for this). You can choose to implement `Traversal` for your own types, if you want to bubble along a different structure than provided by `bevy_hierarchy`. Implementers must be careful never to produce loops, because this will cause bevy to hang. ## Migration Guide + Manual implementations of `Event` should add associated type `Traverse = TraverseNone` and associated constant `AUTO_PROPAGATE = false`; + `Trigger::new` has new field `propagation: &mut Propagation` which provides the bubbling state. + `ObserverRunner` now takes the same `&mut Propagation` as a final parameter. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
5ffdc0c93f
|
Moves smooth_follow to movement dir (#14249)
# Objective - Moves the smooth_follow.rs into movement directory in examples - Fixes #14241 ## Solution - Move the smooth_follow.rs to movement dir in examples. |
||
![]() |
d0e606b87c
|
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_ # Objective This example shows how to properly handle player input, advance a physics simulation in a fixed timestep, and display the results. The classic source for how and why this is done is Glenn Fiedler's article [Fix Your Timestep!](https://gafferongames.com/post/fix_your_timestep/). ## Motivation The naive way of moving a player is to just update their position like so: ```rust transform.translation += velocity; ``` The issue here is that the player's movement speed will be tied to the frame rate. Faster machines will move the player faster, and slower machines will move the player slower. In fact, you can observe this today when running some old games that did it this way on modern hardware! The player will move at a breakneck pace. The more sophisticated way is to update the player's position based on the time that has passed: ```rust transform.translation += velocity * time.delta_seconds(); ``` This way, velocity represents a speed in units per second, and the player will move at the same speed regardless of the frame rate. However, this can still be problematic if the frame rate is very low or very high. If the frame rate is very low, the player will move in large jumps. This may lead to a player moving in such large jumps that they pass through walls or other obstacles. In general, you cannot expect a physics simulation to behave nicely with *any* delta time. Ideally, we want to have some stability in what kinds of delta times we feed into our physics simulation. The solution is using a fixed timestep. This means that we advance the physics simulation by a fixed amount at a time. If the real time that passed between two frames is less than the fixed timestep, we simply don't advance the physics simulation at all. If it is more, we advance the physics simulation multiple times until we catch up. You can read more about how Bevy implements this in the documentation for [`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html). This leaves us with a last problem, however. If our physics simulation may advance zero or multiple times per frame, there may be frames in which the player's position did not need to be updated at all, and some where it is updated by a large amount that resulted from running the physics simulation multiple times. This is physically correct, but visually jarring. Imagine a player moving in a straight line, but depending on the frame rate, they may sometimes advance by a large amount and sometimes not at all. Visually, we want the player to move smoothly. This is why we need to separate the player's position in the physics simulation from the player's position in the visual representation. The visual representation can then be interpolated smoothly based on the last and current actual player position in the physics simulation. This is a tradeoff: every visual frame is now slightly lagging behind the actual physical frame, but in return, the player's movement will appear smooth. There are other ways to compute the visual representation of the player, such as extrapolation. See the [documentation of the lightyear crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html) for a nice overview of the different methods and their tradeoffs. ## Implementation - The player's velocity is stored in a `Velocity` component. This is the speed in units per second. - The player's current position in the physics simulation is stored in a `PhysicalTranslation` component. - The player's previous position in the physics simulation is stored in a `PreviousPhysicalTranslation` component. - The player's visual representation is stored in Bevy's regular `Transform` component. - Every frame, we go through the following steps: - Advance the physics simulation by one fixed timestep in the `advance_physics` system. This is run in the `FixedUpdate` schedule, which runs before the `Update` schedule. - Update the player's visual representation in the `update_displayed_transform` system. This interpolates between the player's previous and current position in the physics simulation. - Update the player's velocity based on the player's input in the `handle_input` system. ## Relevant Issues Related to #1259. I'm also fairly sure I've seen an issue somewhere made by @alice-i-cecile about showing how to move a character correctly in a fixed timestep, but I cannot find it. |
||
![]() |
3452781bf7
|
Deduplicate Wasm optimization instructions (#14173)
See https://github.com/bevyengine/bevy-website/pull/1538 for context. |
||
![]() |
276dd04001
|
bevy_reflect: Function reflection (#13152)
# Objective
We're able to reflect types sooooooo... why not functions?
The goal of this PR is to make functions callable within a dynamic
context, where type information is not readily available at compile
time.
For example, if we have a function:
```rust
fn add(left: i32, right: i32) -> i32 {
left + right
}
```
And two `Reflect` values we've already validated are `i32` types:
```rust
let left: Box<dyn Reflect> = Box::new(2_i32);
let right: Box<dyn Reflect> = Box::new(2_i32);
```
We should be able to call `add` with these values:
```rust
// ?????
let result: Box<dyn Reflect> = add.call_dynamic(left, right);
```
And ideally this wouldn't just work for functions, but methods and
closures too!
Right now, users have two options:
1. Manually parse the reflected data and call the function themselves
2. Rely on registered type data to handle the conversions for them
For a small function like `add`, this isn't too bad. But what about for
more complex functions? What about for many functions?
At worst, this process is error-prone. At best, it's simply tedious.
And this is assuming we know the function at compile time. What if we
want to accept a function dynamically and call it with our own
arguments?
It would be much nicer if `bevy_reflect` could alleviate some of the
problems here.
## Solution
Added function reflection!
This adds a `DynamicFunction` type to wrap a function dynamically. This
can be called with an `ArgList`, which is a dynamic list of
`Reflect`-containing `Arg` arguments. It returns a `FunctionResult`
which indicates whether or not the function call succeeded, returning a
`Reflect`-containing `Return` type if it did succeed.
Many functions can be converted into this `DynamicFunction` type thanks
to the `IntoFunction` trait.
Taking our previous `add` example, this might look something like
(explicit types added for readability):
```rust
fn add(left: i32, right: i32) -> i32 {
left + right
}
let mut function: DynamicFunction = add.into_function();
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
let result: Return = function.call(args).unwrap();
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
And it also works on closures:
```rust
let add = |left: i32, right: i32| left + right;
let mut function: DynamicFunction = add.into_function();
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
let result: Return = function.call(args).unwrap();
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
As well as methods:
```rust
#[derive(Reflect)]
struct Foo(i32);
impl Foo {
fn add(&mut self, value: i32) {
self.0 += value;
}
}
let mut foo = Foo(2);
let mut function: DynamicFunction = Foo::add.into_function();
let args: ArgList = ArgList::new().push_mut(&mut foo).push_owned(2_i32);
function.call(args).unwrap();
assert_eq!(foo.0, 4);
```
### Limitations
While this does cover many functions, it is far from a perfect system
and has quite a few limitations. Here are a few of the limitations when
using `IntoFunction`:
1. The lifetime of the return value is only tied to the lifetime of the
first argument (useful for methods). This means you can't have a
function like `(a: i32, b: &i32) -> &i32` without creating the
`DynamicFunction` manually.
2. Only 15 arguments are currently supported. If the first argument is a
(mutable) reference, this number increases to 16.
3. Manual implementations of `Reflect` will need to implement the new
`FromArg`, `GetOwnership`, and `IntoReturn` traits in order to be used
as arguments/return types.
And some limitations of `DynamicFunction` itself:
1. All arguments share the same lifetime, or rather, they will shrink to
the shortest lifetime.
2. Closures that capture their environment may need to have their
`DynamicFunction` dropped before accessing those variables again (there
is a `DynamicFunction::call_once` to make this a bit easier)
3. All arguments and return types must implement `Reflect`. While not a
big surprise coming from `bevy_reflect`, this implementation could
actually still work by swapping `Reflect` out with `Any`. Of course,
that makes working with the arguments and return values a bit harder.
4. Generic functions are not supported (unless they have been manually
monomorphized)
And general, reflection gotchas:
1. `&str` does not implement `Reflect`. Rather, `&'static str`
implements `Reflect` (the same is true for `&Path` and similar types).
This means that `&'static str` is considered an "owned" value for the
sake of generating arguments. Additionally, arguments and return types
containing `&str` will assume it's `&'static str`, which is almost never
the desired behavior. In these cases, the only solution (I believe) is
to use `&String` instead.
### Followup Work
This PR is the first of two PRs I intend to work on. The second PR will
aim to integrate this new function reflection system into the existing
reflection traits and `TypeInfo`. The goal would be to register and call
a reflected type's methods dynamically.
I chose not to do that in this PR since the diff is already quite large.
I also want the discussion for both PRs to be focused on their own
implementation.
Another followup I'd like to do is investigate allowing common container
types as a return type, such as `Option<&[mut] T>` and `Result<&[mut] T,
E>`. This would allow even more functions to opt into this system. I
chose to not include it in this one, though, for the same reasoning as
previously mentioned.
### Alternatives
One alternative I had considered was adding a macro to convert any
function into a reflection-based counterpart. The idea would be that a
struct that wraps the function would be created and users could specify
which arguments and return values should be `Reflect`. It could then be
called via a new `Function` trait.
I think that could still work, but it will be a fair bit more involved,
requiring some slightly more complex parsing. And it of course is a bit
more work for the user, since they need to create the type via macro
invocation.
It also makes registering these functions onto a type a bit more
complicated (depending on how it's implemented).
For now, I think this is a fairly simple, yet powerful solution that
provides the least amount of friction for users.
---
## Showcase
Bevy now adds support for storing and calling functions dynamically
using reflection!
```rust
// 1. Take a standard Rust function
fn add(left: i32, right: i32) -> i32 {
left + right
}
// 2. Convert it into a type-erased `DynamicFunction` using the `IntoFunction` trait
let mut function: DynamicFunction = add.into_function();
// 3. Define your arguments from reflected values
let args: ArgList = ArgList::new().push_owned(2_i32).push_owned(2_i32);
// 4. Call the function with your arguments
let result: Return = function.call(args).unwrap();
// 5. Extract the return value
let value: Box<dyn Reflect> = result.unwrap_owned();
assert_eq!(value.take::<i32>().unwrap(), 4);
```
## Changelog
#### TL;DR
- Added support for function reflection
- Added a new `Function Reflection` example:
|
||
![]() |
44db8b7fac
|
Allow phase items not associated with meshes to be binned. (#14029)
As reported in #14004, many third-party plugins, such as Hanabi, enqueue entities that don't have meshes into render phases. However, the introduction of indirect mode added a dependency on mesh-specific data, breaking this workflow. This is because GPU preprocessing requires that the render phases manage indirect draw parameters, which don't apply to objects that aren't meshes. The existing code skips over binned entities that don't have indirect draw parameters, which causes the rendering to be skipped for such objects. To support this workflow, this commit adds a new field, `non_mesh_items`, to `BinnedRenderPhase`. This field contains a simple list of (bin key, entity) pairs. After drawing batchable and unbatchable objects, the non-mesh items are drawn one after another. Bevy itself doesn't enqueue any items into this list; it exists solely for the application and/or plugins to use. Additionally, this commit switches the asset ID in the standard bin keys to be an untyped asset ID rather than that of a mesh. This allows more flexibility, allowing bins to be keyed off any type of asset. This patch adds a new example, `custom_phase_item`, which simultaneously serves to demonstrate how to use this new feature and to act as a regression test so this doesn't break again. Fixes #14004. ## Changelog ### Added * `BinnedRenderPhase` now contains a `non_mesh_items` field for plugins to add custom items to. |
||
![]() |
48f70789f5
|
Add first person view model example (#13828)
# Objective A very common way to organize a first-person view is to split it into two kinds of models: - The *view model* is the model that represents the player's body. - The *world model* is everything else. The reason for this distinction is that these two models should be rendered with different FOVs. The view model is typically designed and animated with a very specific FOV in mind, so it is generally *fixed* and cannot be changed by a player. The world model, on the other hand, should be able to change its FOV to accommodate the player's preferences for the following reasons: - *Accessibility*: How prone is the player to motion sickness? A wider FOV can help. - *Tactical preference*: Does the player want to see more of the battlefield? Or have a more zoomed-in view for precision aiming? - *Physical considerations*: How well does the in-game FOV match the player's real-world FOV? Are they sitting in front of a monitor or playing on a TV in the living room? How big is the screen? ## Solution I've added an example implementing the described setup as follows. The `Player` is an entity holding two cameras, one for each model. The view model camera has a fixed FOV of 70 degrees, while the world model camera has a variable FOV that can be changed by the player. I use different `RenderLayers` to select what to render. - The world model camera has no explicit `RenderLayers` component, so it uses the layer 0. All static objects in the scene are also on layer 0 for the same reason. - The view model camera has a `RenderLayers` component with layer 1, so it only renders objects explicitly assigned to layer 1. The arm of the player is one such object. The order of the view model camera is additionally bumped to 1 to ensure it renders on top of the world model. - The light source in the scene must illuminate both the view model and the world model, so it is assigned to both layers 0 and 1. To better see the effect, the player can move the camera by dragging their mouse and change the world model's FOV with the arrow keys. The arrow up key maps to "decrease FOV" and the arrow down key maps to "increase FOV". This sounds backwards on paper, but is more intuitive when actually changing the FOV in-game since a decrease in FOV looks like a zoom-in. I intentionally do not allow changing the view model's FOV even though it would be illustrative because that would be an anti-pattern and bloat the code a bit. The example is called `first_person_view_model` and not just `first_person` because I want to highlight that this is not a simple flycam, but actually renders the player. ## Testing Default FOV: <img width="1392" alt="image" src="https://github.com/bevyengine/bevy/assets/9047632/8c2e804f-fac2-48c7-8a22-d85af999dfb2"> Decreased FOV: <img width="1392" alt="image" src="https://github.com/bevyengine/bevy/assets/9047632/1733b3e5-f583-4214-a454-3554e3cbd066"> Increased FOV: <img width="1392" alt="image" src="https://github.com/bevyengine/bevy/assets/9047632/0b0640e6-5743-46f6-a79a-7181ba9678e8"> Note that the white bar on the right represents the player's arm, which is more obvious in-game because you can move the camera around. The box on top is there to make sure that the view model is receiving shadows. I tested only on macOS. --- ## Changelog I don't think new examples go in here, do they? ## Caveat The solution used here was implemented with help by @robtfm on [Discord](https://discord.com/channels/691052431525675048/866787577687310356/1241019224491561000): > shadow maps are specific to lights, not to layers > if you want shadows from some meshes that are not visible, you could have light on layer 1+2, meshes on layer 2, camera on layer 1 (for example) > but this might change in future, it's not exactly an intended feature In other words, the example code as-is is not guaranteed to work in the future. I want to bring this up because the use-case presented here is extremely common in first-person games and important for accessibility. It would be good to have a blessed and easy way of how to achieve it. I'm also not happy about how I get the `perspective` variable in `change_fov`. Very open to suggestions :) ## Related issues - Addresses parts of #12658 - Addresses parts of #12588 --------- Co-authored-by: Pascal Hertleif <killercup@gmail.com> |
||
![]() |
eb3c81374a
|
Generalised ECS reactivity with Observers (#10839)
# Objective - Provide an expressive way to register dynamic behavior in response to ECS changes that is consistent with existing bevy types and traits as to provide a smooth user experience. - Provide a mechanism for immediate changes in response to events during command application in order to facilitate improved query caching on the path to relations. ## Solution - A new fundamental ECS construct, the `Observer`; inspired by flec's observers but adapted to better fit bevy's access patterns and rust's type system. --- ## Examples There are 3 main ways to register observers. The first is a "component observer" that looks like this: ```rust world.observe(|trigger: Trigger<OnAdd, Transform>, query: Query<&Transform>| { let transform = query.get(trigger.entity()).unwrap(); }); ``` The above code will spawn a new entity representing the observer that will run it's callback whenever the `Transform` component is added to an entity. This is a system-like function that supports dependency injection for all the standard bevy types: `Query`, `Res`, `Commands` etc. It also has a `Trigger` parameter that provides information about the trigger such as the target entity, and the event being triggered. Importantly these systems run during command application which is key for their future use to keep ECS internals up to date. There are similar events for `OnInsert` and `OnRemove`, and this will be expanded with things such as `ArchetypeCreated`, `TableEmpty` etc. in follow up PRs. Another way to register an observer is an "entity observer" that looks like this: ```rust world.entity_mut(entity).observe(|trigger: Trigger<Resize>| { // ... }); ``` Entity observers run whenever an event of their type is triggered targeting that specific entity. This type of observer will de-spawn itself if the entity (or entities) it is observing is ever de-spawned so as to not leave dangling observers. Entity observers can also be spawned from deferred contexts such as other observers, systems, or hooks using commands: ```rust commands.entity(entity).observe(|trigger: Trigger<Resize>| { // ... }); ``` Observers are not limited to in built event types, they can be used with any type that implements `Event` (which has been extended to implement Component). This means events can also carry data: ```rust #[derive(Event)] struct Resize { x: u32, y: u32 } commands.entity(entity).observe(|trigger: Trigger<Resize>, query: Query<&mut Size>| { let event = trigger.event(); // ... }); // Will trigger the observer when commands are applied. commands.trigger_targets(Resize { x: 10, y: 10 }, entity); ``` You can also trigger events that target more than one entity at a time: ```rust commands.trigger_targets(Resize { x: 10, y: 10 }, [e1, e2]); ``` Additionally, Observers don't _need_ entity targets: ```rust app.observe(|trigger: Trigger<Quit>| { }) commands.trigger(Quit); ``` In these cases, `trigger.entity()` will be a placeholder. Observers are actually just normal entities with an `ObserverState` and `Observer` component! The `observe()` functions above are just shorthand for: ```rust world.spawn(Observer::new(|trigger: Trigger<Resize>| {}); ``` This will spawn the `Observer` system and use an `on_add` hook to add the `ObserverState` component. Dynamic components and trigger types are also fully supported allowing for runtime defined trigger types. ## Possible Follow-ups 1. Deprecate `RemovedComponents`, observers should fulfill all use cases while being more flexible and performant. 2. Queries as entities: Swap queries to entities and begin using observers listening to archetype creation triggers to keep their caches in sync, this allows unification of `ObserverState` and `QueryState` as well as unlocking several API improvements for `Query` and the management of `QueryState`. 3. Trigger bubbling: For some UI use cases in particular users are likely to want some form of bubbling for entity observers, this is trivial to implement naively but ideally this includes an acceleration structure to cache hierarchy traversals. 4. All kinds of other in-built trigger types. 5. Optimization; in order to not bloat the complexity of the PR I have kept the implementation straightforward, there are several areas where performance can be improved. The focus for this PR is to get the behavior implemented and not incur a performance cost for users who don't use observers. I am leaving each of these to follow up PR's in order to keep each of them reviewable as this already includes significant changes. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
c172c3c4b5
|
Custom primitives example (#13795)
# Objective - Add a new example showcasing how to add custom primitives and what you can do with them. ## Solution - Added a new example `custom_primitives` with a 2D heart shape primitive highlighting - `Bounded2d` by implementing and visualising bounding shapes, - `Measured2d` by implementing it, - `Meshable` to show the shape on the screen - The example also includes an `Extrusion<Heart>` implementing - `Measured3d`, - `Bounded3d` using the `BoundedExtrusion` trait and - meshing using the `Extrudable` trait. ## Additional information Here are two images of the heart and its extrusion:   --------- Co-authored-by: Jakub Marcowski <37378746+Chubercik@users.noreply.github.com> |
||
![]() |
a569b35c18
|
Stable interpolation and smooth following (#13741)
# Objective Partially address #13408 Rework of #13613 Unify the very nice forms of interpolation specifically present in `bevy_math` under a shared trait upon which further behavior can be based. The ideas in this PR were prompted by [Lerp smoothing is broken by Freya Holmer](https://www.youtube.com/watch?v=LSNQuFEDOyQ). ## Solution There is a new trait `StableInterpolate` in `bevy_math::common_traits` which enshrines a quite-specific notion of interpolation with a lot of guarantees: ```rust /// A type with a natural interpolation that provides strong subdivision guarantees. /// /// Although the only required method is `interpolate_stable`, many things are expected of it: /// /// 1. The notion of interpolation should follow naturally from the semantics of the type, so /// that inferring the interpolation mode from the type alone is sensible. /// /// 2. The interpolation recovers something equivalent to the starting value at `t = 0.0` /// and likewise with the ending value at `t = 1.0`. /// /// 3. Importantly, the interpolation must be *subdivision-stable*: for any interpolation curve /// between two (unnamed) values and any parameter-value pairs `(t0, p)` and `(t1, q)`, the /// interpolation curve between `p` and `q` must be the *linear* reparametrization of the original /// interpolation curve restricted to the interval `[t0, t1]`. /// /// The last of these conditions is very strong and indicates something like constant speed. It /// is called "subdivision stability" because it guarantees that breaking up the interpolation /// into segments and joining them back together has no effect. /// /// Here is a diagram depicting it: /// ```text /// top curve = u.interpolate_stable(v, t) /// /// t0 => p t1 => q /// |-------------|---------|-------------| /// 0 => u / \ 1 => v /// / \ /// / \ /// / linear \ /// / reparametrization \ /// / t = t0 * (1 - s) + t1 * s \ /// / \ /// |-------------------------------------| /// 0 => p 1 => q /// /// bottom curve = p.interpolate_stable(q, s) /// ``` /// /// Note that some common forms of interpolation do not satisfy this criterion. For example, /// [`Quat::lerp`] and [`Rot2::nlerp`] are not subdivision-stable. /// /// Furthermore, this is not to be used as a general trait for abstract interpolation. /// Consumers rely on the strong guarantees in order for behavior based on this trait to be /// well-behaved. /// /// [`Quat::lerp`]: crate::Quat::lerp /// [`Rot2::nlerp`]: crate::Rot2::nlerp pub trait StableInterpolate: Clone { /// Interpolate between this value and the `other` given value using the parameter `t`. /// Note that the parameter `t` is not necessarily clamped to lie between `0` and `1`. /// When `t = 0.0`, `self` is recovered, while `other` is recovered at `t = 1.0`, /// with intermediate values lying between the two. fn interpolate_stable(&self, other: &Self, t: f32) -> Self; } ``` This trait has a blanket implementation over `NormedVectorSpace`, where `lerp` is used, along with implementations for `Rot2`, `Quat`, and the direction types using variants of `slerp`. Other areas may choose to implement this trait in order to hook into its functionality, but the stringent requirements must actually be met. This trait bears no direct relationship with `bevy_animation`'s `Animatable` trait, although they may choose to use `interpolate_stable` in their trait implementations if they wish, as both traits involve type-inferred interpolations of the same kind. `StableInterpolate` is not a supertrait of `Animatable` for a couple reasons: 1. Notions of interpolation in animation are generally going to be much more general than those allowed under these constraints. 2. Laying out these generalized interpolation notions is the domain of `bevy_animation` rather than of `bevy_math`. (Consider also that inferring interpolation from types is not universally desirable.) Similarly, this is not implemented on `bevy_color`'s color types, although their current mixing behavior does meet the conditions of the trait. As an aside, the subdivision-stability condition is of interest specifically for the [Curve RFC](https://github.com/bevyengine/rfcs/pull/80), where it also ensures a kind of stability for subsampling. Importantly, this trait ensures that the "smooth following" behavior defined in this PR behaves predictably: ```rust /// Smoothly nudge this value towards the `target` at a given decay rate. The `decay_rate` /// parameter controls how fast the distance between `self` and `target` decays relative to /// the units of `delta`; the intended usage is for `decay_rate` to generally remain fixed, /// while `delta` is something like `delta_time` from an updating system. This produces a /// smooth following of the target that is independent of framerate. /// /// More specifically, when this is called repeatedly, the result is that the distance between /// `self` and a fixed `target` attenuates exponentially, with the rate of this exponential /// decay given by `decay_rate`. /// /// For example, at `decay_rate = 0.0`, this has no effect. /// At `decay_rate = f32::INFINITY`, `self` immediately snaps to `target`. /// In general, higher rates mean that `self` moves more quickly towards `target`. /// /// # Example /// ``` /// # use bevy_math::{Vec3, StableInterpolate}; /// # let delta_time: f32 = 1.0 / 60.0; /// let mut object_position: Vec3 = Vec3::ZERO; /// let target_position: Vec3 = Vec3::new(2.0, 3.0, 5.0); /// // Decay rate of ln(10) => after 1 second, remaining distance is 1/10th /// let decay_rate = f32::ln(10.0); /// // Calling this repeatedly will move `object_position` towards `target_position`: /// object_position.smooth_nudge(&target_position, decay_rate, delta_time); /// ``` fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32) { self.interpolate_stable_assign(target, 1.0 - f32::exp(-decay_rate * delta)); } ``` As the documentation indicates, the intention is for this to be called in game update systems, and `delta` would be something like `Time::delta_seconds` in Bevy, allowing positions, orientations, and so on to smoothly follow a target. A new example, `smooth_follow`, demonstrates a basic implementation of this, with a sphere smoothly following a sharply moving target: https://github.com/bevyengine/bevy/assets/2975848/7124b28b-6361-47e3-acf7-d1578ebd0347 ## Testing Tested by running the example with various parameters. |
||
![]() |
33dff0d3f7
|
2D top-down camera example (#12720)
# Objective This PR addresses the 2D part of #12658. I plan to separate the examples and make one PR per camera example. ## Solution Added a new top-down example composed of: - [x] Player keyboard movements - [x] UI for keyboard instructions - [x] Colors and bloom effect to see the movement of the player - [x] Camera smooth movement towards the player (lerp) ## Testing ```bash cargo run --features="wayland,bevy/dynamic_linking" --example 2d_top_down_camera ``` https://github.com/bevyengine/bevy/assets/10638479/95db0587-e5e0-4f55-be11-97444b795793 |
||
![]() |
49338245ea
|
Generalize StateTransitionEvent<S> to allow identity transitions (#13579)
# Objective This PR addresses one of the issues from [discord state discussion](https://discord.com/channels/691052431525675048/1237949214017716356). Same-state transitions can be desirable, so there should exist a hook for them. Fixes https://github.com/bevyengine/bevy/issues/9130. ## Solution - Allow `StateTransitionEvent<S>` to contain identity transitions. - Ignore identity transitions at schedule running level (`OnExit`, `OnTransition`, `OnEnter`). - Propagate identity transitions through `SubStates` and `ComputedStates`. - Add example about registering custom transition schedules. ## Changelog - `StateTransitionEvent<S>` can be emitted with same `exited` and `entered` state. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
df8ccb8735
|
Implement PBR anisotropy per KHR_materials_anisotropy . (#13450)
This commit implements support for physically-based anisotropy in Bevy's `StandardMaterial`, following the specification for the [`KHR_materials_anisotropy`] glTF extension. [*Anisotropy*] (not to be confused with [anisotropic filtering]) is a PBR feature that allows roughness to vary along the tangent and bitangent directions of a mesh. In effect, this causes the specular light to stretch out into lines instead of a round lobe. This is useful for modeling brushed metal, hair, and similar surfaces. Support for anisotropy is a common feature in major game and graphics engines; Unity, Unreal, Godot, three.js, and Blender all support it to varying degrees. Two new parameters have been added to `StandardMaterial`: `anisotropy_strength` and `anisotropy_rotation`. Anisotropy strength, which ranges from 0 to 1, represents how much the roughness differs between the tangent and the bitangent of the mesh. In effect, it controls how stretched the specular highlight is. Anisotropy rotation allows the roughness direction to differ from the tangent of the model. In addition to these two fixed parameters, an *anisotropy texture* can be supplied. Such a texture should be a 3-channel RGB texture, where the red and green values specify a direction vector using the same conventions as a normal map ([0, 1] color values map to [-1, 1] vector values), and the the blue value represents the strength. This matches the format that the [`KHR_materials_anisotropy`] specification requires. Such textures should be loaded as linear and not sRGB. Note that this texture does consume one additional texture binding in the standard material shader. The glTF loader has been updated to properly parse the `KHR_materials_anisotropy` extension. A new example, `anisotropy`, has been added. This example loads and displays the barn lamp example from the [`glTF-Sample-Assets`] repository. Note that the textures were rather large, so I shrunk them down and converted them to a mixture of JPEG and KTX2 format, in the interests of saving space in the Bevy repository. [*Anisotropy*]: https://google.github.io/filament/Filament.md.html#materialsystem/anisotropicmodel [anisotropic filtering]: https://en.wikipedia.org/wiki/Anisotropic_filtering [`KHR_materials_anisotropy`]: https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_anisotropy/README.md [`glTF-Sample-Assets`]: https://github.com/KhronosGroup/glTF-Sample-Assets/ ## Changelog ### Added * Physically-based anisotropy is now available for materials, which enhances the look of surfaces such as brushed metal or hair. glTF scenes can use the new feature with the `KHR_materials_anisotropy` extension. ## Screenshots With anisotropy:  Without anisotropy:  |
||
![]() |
d26900a9ea
|
add handling of all missing gltf extras: scene, mesh & materials (#13453)
# Objective - fixes #4823 ## Solution As outlined in the discussion in the linked issue as the best current solution, this PR adds specific GltfExtras for - scenes - meshes - materials - As it is , it is not a breaking change, I hesitated to rename the current "GltfExtras" component to "PrimitiveGltfExtras", but that would result in a breaking change and might be a bit confusing as to what "primitive" that refers to. ## Testing - I included a bare-bones example & asset (exported gltf file from Blender) with gltf extras at all the relevant levels : scene, mesh, material --- ## Changelog - adds "SceneGltfExtras" injected at the scene level if any - adds "MeshGltfExtras", injected at the mesh level if any - adds "MaterialGltfExtras", injected at the mesh level if any: ie if a mesh has a material that has gltf extras, the component will be injected there. |
||
![]() |
061bee7e3c
|
fix: upgrade to winit v0.30 (#13366)
# Objective - Upgrade winit to v0.30 - Fixes https://github.com/bevyengine/bevy/issues/13331 ## Solution This is a rewrite/adaptation of the new trait system described and implemented in `winit` v0.30. ## Migration Guide The custom UserEvent is now renamed as WakeUp, used to wake up the loop if anything happens outside the app (a new [custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d) shows this behavior. The internal `UpdateState` has been removed and replaced internally by the AppLifecycle. When changed, the AppLifecycle is sent as an event. The `UpdateMode` now accepts only two values: `Continuous` and `Reactive`, but the latter exposes 3 new properties to enable reactive to device, user or window events. The previous `UpdateMode::Reactive` is now equivalent to `UpdateMode::reactive()`, while `UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`. The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now contains the possible values of the application state inside the event loop: * `Idle`: the loop has not started yet * `Running` (previously called `Started`): the loop is running * `WillSuspend`: the loop is going to be suspended * `Suspended`: the loop is suspended * `WillResume`: the loop is going to be resumed Note: the `Resumed` state has been removed since the resumed app is just running. Finally, now that `winit` enables this, it extends the `WinitPlugin` to support custom events. ## Test platforms - [x] Windows - [x] MacOs - [x] Linux (x11) - [x] Linux (Wayland) - [x] Android - [x] iOS - [x] WASM/WebGPU - [x] WASM/WebGL2 ## Outstanding issues / regressions - [ ] iOS: build failed in CI - blocking, but may just be flakiness - [x] Cross-platform: when the window is maximised, changes in the scale factor don't apply, to make them apply one has to make the window smaller again. (Re-maximising keeps the updated scale factor) - non-blocking, but good to fix - [ ] Android: it's pretty easy to quickly open and close the app and then the music keeps playing when suspended. - non-blocking but worrying - [ ] Web: the application will hang when switching tabs - Not new, duplicate of https://github.com/bevyengine/bevy/issues/13486 - [ ] Cross-platform?: Screenshot failure, `ERROR present_frames: wgpu_core::present: No work has been submitted for this frame before` taking the first screenshot, but after pressing space - non-blocking, but good to fix --------- Co-authored-by: François <francois.mockers@vleue.com> |
||
![]() |
f67ae29338
|
Create a primitive sampling showcase example (#13519)
# Objective - Show + Visually Test that 3D primitive sampling works - Make an example that looks nice. ## Solution - Added a `sampling_primitives` examples which shows all the 3D primitives being sampled, with a firefly aesthetic.  ## Testing - `cargo run --example sampling_primitives` - Haven't tested WASM. ## Changelog ### Added - Added a new example, `sampling_primitives`, to showcase all the 3D sampleable primitives. ## Additional notes: This example borrowed a bunch of code from the other sampling example, by @mweatherley. In future updates this example should be updated with new 3D primitives as they become sampleable. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> |
||
![]() |
787df44288
|
Example for random sampling (#13507)
# Objective We introduced a bunch of neat random sampling stuff in this release; we should do a good job of showing people how to use it, and writing examples is part of this. ## Solution A new Math example, `random_sampling`, shows off the `ShapeSample` API functionality. For the moment, it renders a cube and allows the user to sample points from its interior or boundary in sets of either 1 or 100: <img width="1440" alt="Screenshot 2024-05-25 at 1 16 08 PM" src="https://github.com/bevyengine/bevy/assets/2975848/9cb6f53f-c89a-42c2-8907-b11d294c402a"> On the level of code, these are reflected by two ways of using `ShapeSample`: ```rust // Get a single random Vec3: let sample: Vec3 = match *mode { Mode::Interior => shape.0.sample_interior(rng), Mode::Boundary => shape.0.sample_boundary(rng), }; ``` ```rust // Get 100 random Vec3s: let samples: Vec<Vec3> = match *mode { Mode::Interior => { let dist = shape.0.interior_dist(); dist.sample_iter(&mut rng).take(100).collect() } Mode::Boundary => { let dist = shape.0.boundary_dist(); dist.sample_iter(&mut rng).take(100).collect() } }; ``` ## Testing Run the example! ## Discussion Maybe in the future it would be nice to show off all of the different shapes that we have implemented `ShapeSample` for, but I wanted to start just by demonstrating the functionality. Here, I chose a cube because it's simple and because it looks good rendered transparently with backface culling disabled. |
||
![]() |
f398674e51
|
Implement opt-in sharp screen-space reflections for the deferred renderer, with improved raymarching code. (#13418)
This commit, a revamp of #12959, implements screen-space reflections (SSR), which approximate real-time reflections based on raymarching through the depth buffer and copying samples from the final rendered frame. This patch is a relatively minimal implementation of SSR, so as to provide a flexible base on which to customize and build in the future. However, it's based on the production-quality [raymarching code by Tomasz Stachowiak](https://gist.github.com/h3r2tic/9c8356bdaefbe80b1a22ae0aaee192db). For a general basic overview of screen-space reflections, see [1](https://lettier.github.io/3d-game-shaders-for-beginners/screen-space-reflection.html). The raymarching shader uses the basic algorithm of tracing forward in large steps, refining that trace in smaller increments via binary search, and then using the secant method. No temporal filtering or roughness blurring, is performed at all; for this reason, SSR currently only operates on very shiny surfaces. No acceleration via the hierarchical Z-buffer is implemented (though note that https://github.com/bevyengine/bevy/pull/12899 will add the infrastructure for this). Reflections are traced at full resolution, which is often considered slow. All of these improvements and more can be follow-ups. SSR is built on top of the deferred renderer and is currently only supported in that mode. Forward screen-space reflections are possible albeit uncommon (though e.g. *Doom Eternal* uses them); however, they require tracing from the previous frame, which would add complexity. This patch leaves the door open to implementing SSR in the forward rendering path but doesn't itself have such an implementation. Screen-space reflections aren't supported in WebGL 2, because they require sampling from the depth buffer, which Naga can't do because of a bug (`sampler2DShadow` is incorrectly generated instead of `sampler2D`; this is the same reason why depth of field is disabled on that platform). To add screen-space reflections to a camera, use the `ScreenSpaceReflectionsBundle` bundle or the `ScreenSpaceReflectionsSettings` component. In addition to `ScreenSpaceReflectionsSettings`, `DepthPrepass` and `DeferredPrepass` must also be present for the reflections to show up. The `ScreenSpaceReflectionsSettings` component contains several settings that artists can tweak, and also comes with sensible defaults. A new example, `ssr`, has been added. It's loosely based on the [three.js ocean sample](https://threejs.org/examples/webgl_shaders_ocean.html), but all the assets are original. Note that the three.js demo has no screen-space reflections and instead renders a mirror world. In contrast to #12959, this demo tests not only a cube but also a more complex model (the flight helmet). ## Changelog ### Added * Screen-space reflections can be enabled for very smooth surfaces by adding the `ScreenSpaceReflections` component to a camera. Deferred rendering must be enabled for the reflections to appear.   |
||
![]() |
ec01c2dc45
|
New circular primitives: Arc2d , CircularSector , CircularSegment (#13482)
# Objective Adopted #11748 ## Solution I've rebased on main to fix the merge conflicts. ~~Not quite ready to merge yet~~ * Clippy is happy and the tests are passing, but... * ~~The new shapes in `examples/2d/2d_shapes.rs` don't look right at all~~ Never mind, looks like radians and degrees just got mixed up at some point? * I have updated one doc comment based on a review in the original PR. --------- Co-authored-by: Alexis "spectria" Horizon <spectria.limina@gmail.com> Co-authored-by: Alexis "spectria" Horizon <118812919+spectria-limina@users.noreply.github.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Ben Harper <ben@tukom.org> |
||
![]() |
1d950e6195
|
Allow AssetServer::load to acquire a guard item. (#13051)
# Objective Supercedes #12881 . Added a simple implementation that allows the user to react to multiple asset loads both synchronously and asynchronously. ## Solution Added `load_acquire`, that holds an item and drops it when loading is finished or failed. When used synchronously Hold an `Arc<()>`, check for `Arc::strong_count() == 1` when all loading completed. When used asynchronously Hold a `SemaphoreGuard`, await on `acquire_all` for completion. This implementation has more freedom than the original in my opinion. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
![]() |
5db52663b3
|
bevy_reflect: Custom attributes (#11659)
# Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`) |
||
![]() |
19bfa41768
|
Implement volumetric fog and volumetric lighting, also known as light shafts or god rays. (#13057)
This commit implements a more physically-accurate, but slower, form of fog than the `bevy_pbr::fog` module does. Notably, this *volumetric fog* allows for light beams from directional lights to shine through, creating what is known as *light shafts* or *god rays*. To add volumetric fog to a scene, add `VolumetricFogSettings` to the camera, and add `VolumetricLight` to directional lights that you wish to be volumetric. `VolumetricFogSettings` has numerous settings that allow you to define the accuracy of the simulation, as well as the look of the fog. Currently, only interaction with directional lights that have shadow maps is supported. Note that the overhead of the effect scales directly with the number of directional lights in use, so apply `VolumetricLight` sparingly for the best results. The overall algorithm, which is implemented as a postprocessing effect, is a combination of the techniques described in [Scratchapixel] and [this blog post]. It uses raymarching in screen space, transformed into shadow map space for sampling and combined with physically-based modeling of absorption and scattering. Bevy employs the widely-used [Henyey-Greenstein phase function] to model asymmetry; this essentially allows light shafts to fade into and out of existence as the user views them. Volumetric rendering is a huge subject, and I deliberately kept the scope of this commit small. Possible follow-ups include: 1. Raymarching at a lower resolution. 2. A post-processing blur (especially useful when combined with (1)). 3. Supporting point lights and spot lights. 4. Supporting lights with no shadow maps. 5. Supporting irradiance volumes and reflection probes. 6. Voxel components that reuse the volumetric fog code to create voxel shapes. 7. *Horizon: Zero Dawn*-style clouds. These are all useful, but out of scope of this patch for now, to keep things tidy and easy to review. A new example, `volumetric_fog`, has been added to demonstrate the effect. ## Changelog ### Added * A new component, `VolumetricFog`, is available, to allow for a more physically-accurate, but more resource-intensive, form of fog. * A new component, `VolumetricLight`, can be placed on directional lights to make them interact with `VolumetricFog`. Notably, this allows such lights to emit light shafts/god rays.   [Scratchapixel]: https://www.scratchapixel.com/lessons/3d-basic-rendering/volume-rendering-for-developers/intro-volume-rendering.html [this blog post]: https://www.alexandre-pestana.com/volumetric-lights/ [Henyey-Greenstein phase function]: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction |
||
![]() |
df31b808c3
|
Implement fast depth of field as a postprocessing effect. (#13009)
This commit implements the [depth of field] effect, simulating the blur of objects out of focus of the virtual lens. Either the [hexagonal bokeh] effect or a faster Gaussian blur may be used. In both cases, the implementation is a simple separable two-pass convolution. This is not the most physically-accurate real-time bokeh technique that exists; Unreal Engine has [a more accurate implementation] of "cinematic depth of field" from 2018. However, it's simple, and most engines provide something similar as a fast option, often called "mobile" depth of field. The general approach is outlined in [a blog post from 2017]. We take advantage of the fact that both Gaussian blurs and hexagonal bokeh blurs are *separable*. This means that their 2D kernels can be reduced to a small number of 1D kernels applied one after another, asymptotically reducing the amount of work that has to be done. Gaussian blurs can be accomplished by blurring horizontally and then vertically, while hexagonal bokeh blurs can be done with a vertical blur plus a diagonal blur, plus two diagonal blurs. In both cases, only two passes are needed. Bokeh requires the first pass to have a second render target and requires two subpasses in the second pass, which decreases its performance relative to the Gaussian blur. The bokeh blur is generally more aesthetically pleasing than the Gaussian blur, as it simulates the effect of a camera more accurately. The shape of the bokeh circles are determined by the number of blades of the aperture. In our case, we use a hexagon, which is usually considered specific to lower-quality cameras. (This is a downside of the fast hexagon approach compared to the higher-quality approaches.) The blur amount is generally specified by the [f-number], which we use to compute the focal length from the film size and FOV. By default, we simulate standard cinematic cameras of f/1 and [Super 35]. The developer can customize these values as desired. A new example has been added to demonstrate depth of field. It allows customization of the mode (Gaussian vs. bokeh), focal distance and f-numbers. The test scene is inspired by a [blog post on depth of field in Unity]; however, the effect is implemented in a completely different way from that blog post, and all the assets (textures, etc.) are original. Bokeh depth of field:  Gaussian depth of field:  No depth of field:  [depth of field]: https://en.wikipedia.org/wiki/Depth_of_field [hexagonal bokeh]: https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited/ [a more accurate implementation]: https://epicgames.ent.box.com/s/s86j70iamxvsuu6j35pilypficznec04 [a blog post from 2017]: https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited/ [f-number]: https://en.wikipedia.org/wiki/F-number [Super 35]: https://en.wikipedia.org/wiki/Super_35 [blog post on depth of field in Unity]: https://catlikecoding.com/unity/tutorials/advanced-rendering/depth-of-field/ ## Changelog ### Added * A depth of field postprocessing effect is now available, to simulate objects being out of focus of the camera. To use it, add `DepthOfFieldSettings` to an entity containing a `Camera3d` component. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Bram Buurlage <brambuurlage@gmail.com> |
||
![]() |
42ba9dfaea
|
Separate state crate (#13216)
# Objective Extracts the state mechanisms into a new crate called "bevy_state". This comes with a few goals: - state wasn't really an inherent machinery of the ecs system, and so keeping it within bevy_ecs felt forced - by mixing it in with bevy_ecs, the maintainability of our more robust state system was significantly compromised moving state into a new crate makes it easier to encapsulate as it's own feature, and easier to read and understand since it's no longer a single, massive file. ## Solution move the state-related elements from bevy_ecs to a new crate ## Testing - Did you test these changes? If so, how? all the automated tests migrated and passed, ran the pre-existing examples without changes to validate. --- ## Migration Guide Since bevy_state is now gated behind the `bevy_state` feature, projects that use state but don't use the `default-features` will need to add that feature flag. Since it is no longer part of bevy_ecs, projects that use bevy_ecs directly will need to manually pull in `bevy_state`, trigger the StateTransition schedule, and handle any of the elements that bevy_app currently sets up. --------- Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com> |
||
![]() |
d9d305dab5
|
Headless renderer example has been added (#13006)
# Objective Fixes #11457. Fixes #22. ## Solution Based on [another headless application](https://github.com/richardanaya/headless/) --- ## Changelog - Adopted to bevy 0.14 --------- Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
77ed72bc16
|
Implement clearcoat per the Filament and the KHR_materials_clearcoat specifications. (#13031)
Clearcoat is a separate material layer that represents a thin translucent layer of a material. Examples include (from the [Filament spec]) car paint, soda cans, and lacquered wood. This commit implements support for clearcoat following the Filament and Khronos specifications, marking the beginnings of support for multiple PBR layers in Bevy. The [`KHR_materials_clearcoat`] specification describes the clearcoat support in glTF. In Blender, applying a clearcoat to the Principled BSDF node causes the clearcoat settings to be exported via this extension. As of this commit, Bevy parses and reads the extension data when present in glTF. Note that the `gltf` crate has no support for `KHR_materials_clearcoat`; this patch therefore implements the JSON semantics manually. Clearcoat is integrated with `StandardMaterial`, but the code is behind a series of `#ifdef`s that only activate when clearcoat is present. Additionally, the `pbr_feature_layer_material_textures` Cargo feature must be active in order to enable support for clearcoat factor maps, clearcoat roughness maps, and clearcoat normal maps. This approach mirrors the same pattern used by the existing transmission feature and exists to avoid running out of texture bindings on platforms like WebGL and WebGPU. Note that constant clearcoat factors and roughness values *are* supported in the browser; only the relatively-less-common maps are disabled on those platforms. This patch refactors the lighting code in `StandardMaterial` significantly in order to better support multiple layers in a natural way. That code was due for a refactor in any case, so this is a nice improvement. A new demo, `clearcoat`, has been added. It's based on [the corresponding three.js demo], but all the assets (aside from the skybox and environment map) are my original work. [Filament spec]: https://google.github.io/filament/Filament.html#materialsystem/clearcoatmodel [`KHR_materials_clearcoat`]: https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md [the corresponding three.js demo]: https://threejs.org/examples/webgl_materials_physical_clearcoat.html   ## Changelog ### Added * `StandardMaterial` now supports a clearcoat layer, which represents a thin translucent layer over an underlying material. * The glTF loader now supports the `KHR_materials_clearcoat` extension, representing materials with clearcoat layers. ## Migration Guide * The lighting functions in the `pbr_lighting` WGSL module now have clearcoat parameters, if `STANDARD_MATERIAL_CLEARCOAT` is defined. * The `R` reflection vector parameter has been removed from some lighting functions, as it was unused. |
||
![]() |
088960f597
|
Example with repeated texture (#13176)
# Objective Fixes #11136 . Fixes https://github.com/bevyengine/bevy/pull/11161. ## Solution - Set image sampler with repeated mode for u and v - set uv_transform of StandardMaterial to resizing params ## Testing Got this view on example run  |
||
![]() |
40837501b4
|
examples: Add Dynamic Types reflection example (#13220)
# Objective Dynamic types can be tricky to understand and work with in bevy_reflect. There should be an example that shows what they are and how they're used. ## Solution Add a `Dynamic Types` reflection example. I'm planning to go through the reflection examples, adding new ones and updating old ones. And I think this walkthrough style tends to work best. Due to the amount of text and associated explanation, it might fit better in a dedicated reflection chapter of the WIP Bevy Book. However, I think it might be valuable to have some public-facing tutorials for these concepts. Let me know if there any thoughts or critiques with the example— both in content and this overall structure! ## Testing To test these changes, you can run the example locally: ``` cargo run --example dynamic_types ``` --- ## Changelog - Add `Dynamic Types` reflection example |
||
![]() |
d390420093
|
Implement Auto Exposure plugin (#12792)
# Objective - Add auto exposure/eye adaptation to the bevy render pipeline. - Support features that users might expect from other engines: - Metering masks - Compensation curves - Smooth exposure transitions This PR is based on an implementation I already built for a personal project before https://github.com/bevyengine/bevy/pull/8809 was submitted, so I wasn't able to adopt that PR in the proper way. I've still drawn inspiration from it, so @fintelia should be credited as well. ## Solution An auto exposure compute shader builds a 64 bin histogram of the scene's luminance, and then adjusts the exposure based on that histogram. Using a histogram allows the system to ignore outliers like shadows and specular highlights, and it allows to give more weight to certain areas based on a mask. --- ## Changelog - Added: AutoExposure plugin that allows to adjust a camera's exposure based on it's scene's luminance. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
31835ff76d
|
Implement visibility ranges, also known as hierarchical levels of detail (HLODs). (#12916)
Implement visibility ranges, also known as hierarchical levels of detail (HLODs). This commit introduces a new component, `VisibilityRange`, which allows developers to specify camera distances in which meshes are to be shown and hidden. Hiding meshes happens early in the rendering pipeline, so this feature can be used for level of detail optimization. Additionally, this feature is properly evaluated per-view, so different views can show different levels of detail. This feature differs from proper mesh LODs, which can be implemented later. Engines generally implement true mesh LODs later in the pipeline; they're typically more efficient than HLODs with GPU-driven rendering. However, mesh LODs are more limited than HLODs, because they require the lower levels of detail to be meshes with the same vertex layout and shader (and perhaps the same material) as the original mesh. Games often want to use objects other than meshes to replace distant models, such as *octahedral imposters* or *billboard imposters*. The reason why the feature is called *hierarchical level of detail* is that HLODs can replace multiple meshes with a single mesh when the camera is far away. This can be useful for reducing drawcall count. Note that `VisibilityRange` doesn't automatically propagate down to children; it must be placed on every mesh. Crossfading between different levels of detail is supported, using the standard 4x4 ordered dithering pattern from [1]. The shader code to compute the dithering patterns should be well-optimized. The dithering code is only active when visibility ranges are in use for the mesh in question, so that we don't lose early Z. Cascaded shadow maps show the HLOD level of the view they're associated with. Point light and spot light shadow maps, which have no CSMs, display all HLOD levels that are visible in any view. To support this efficiently and avoid doing visibility checks multiple times, we precalculate all visible HLOD levels for each entity with a `VisibilityRange` during the `check_visibility_range` system. A new example, `visibility_range`, has been added to the tree, as well as a new low-poly version of the flight helmet model to go with it. It demonstrates use of the visibility range feature to provide levels of detail. [1]: https://en.wikipedia.org/wiki/Ordered_dithering#Threshold_map [^1]: Unreal doesn't have a feature that exactly corresponds to visibility ranges, but Unreal's HLOD system serves roughly the same purpose. ## Changelog ### Added * A new `VisibilityRange` component is available to conditionally enable entity visibility at camera distances, with optional crossfade support. This can be used to implement different levels of detail (LODs). ## Screenshots High-poly model:  Low-poly model up close:  Crossfading between the two:  --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
b8832dc862
|
Computed State & Sub States (#11426)
## Summary/Description This PR extends states to allow support for a wider variety of state types and patterns, by providing 3 distinct types of state: - Standard [`States`] can only be changed by manually setting the [`NextState<S>`] resource. These states are the baseline on which the other state types are built, and can be used on their own for many simple patterns. See the [state example](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/state.rs) for a simple use case - these are the states that existed so far in Bevy. - [`SubStates`] are children of other states - they can be changed manually using [`NextState<S>`], but are removed from the [`World`] if the source states aren't in the right state. See the [sub_states example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/sub_states.rs) for a simple use case based on the derive macro, or read the trait docs for more complex scenarios. - [`ComputedStates`] are fully derived from other states - they provide a [`compute`](ComputedStates::compute) method that takes in the source states and returns their derived value. They are particularly useful for situations where a simplified view of the source states is necessary - such as having an `InAMenu` computed state derived from a source state that defines multiple distinct menus. See the [computed state example](https://github.com/lee-orr/bevy/blob/derived_state/examples/ecs/computed_states.rscomputed_states.rs) to see a sampling of uses for these states. # Objective This PR is another attempt at allowing Bevy to better handle complex state objects in a manner that doesn't rely on strict equality. While my previous attempts (https://github.com/bevyengine/bevy/pull/10088 and https://github.com/bevyengine/bevy/pull/9957) relied on complex matching capacities at the point of adding a system to application, this one instead relies on deterministically deriving simple states from more complex ones. As a result, it does not require any special macros, nor does it change any other interactions with the state system once you define and add your derived state. It also maintains a degree of distinction between `State` and just normal application state - your derivations have to end up being discreet pre-determined values, meaning there is less of a risk/temptation to place a significant amount of logic and data within a given state. ### Addition - Sub States closes #9942 After some conversation with Maintainers & SMEs, a significant concern was that people might attempt to use this feature as if it were sub-states, and find themselves unable to use it appropriately. Since `ComputedState` is mainly a state matching feature, while `SubStates` are more of a state mutation related feature - but one that is easy to add with the help of the machinery introduced by `ComputedState`, it was added here as well. The relevant discussion is here: https://discord.com/channels/691052431525675048/1200556329803186316 ## Solution closes #11358 The solution is to create a new type of state - one implementing `ComputedStates` - which is deterministically tied to one or more other states. Implementors write a function to transform the source states into the computed state, and it gets triggered whenever one of the source states changes. In addition, we added the `FreelyMutableState` trait , which is implemented as part of the derive macro for `States`. This allows us to limit use of `NextState<S>` to states that are actually mutable, preventing mis-use of `ComputedStates`. --- ## Changelog - Added `ComputedStates` trait - Added `FreelyMutableState` trait - Converted `NextState` resource to an Enum, with `Unchanged` and `Pending` - Added `App::add_computed_state::<S: ComputedStates>()`, to allow for easily adding derived states to an App. - Moved the `StateTransition` schedule label from `bevy_app` to `bevy_ecs` - but maintained the export in `bevy_app` for continuity. - Modified the process for updating states. Instead of just having an `apply_state_transition` system that can be added anywhere, we now have a multi-stage process that has to run within the `StateTransition` label. First, all the state changes are calculated - manual transitions rely on `apply_state_transition`, while computed transitions run their computation process before both call `internal_apply_state_transition` to apply the transition, send out the transition event, trigger dependent states, and record which exit/transition/enter schedules need to occur. Once all the states have been updated, the transition schedules are called - first the exit schedules, then transition schedules and finally enter schedules. - Added `SubStates` trait - Adjusted `apply_state_transition` to be a no-op if the `State<S>` resource doesn't exist ## Migration Guide If the user accessed the NextState resource's value directly or created them from scratch they will need to adjust to use the new enum variants: - if they created a `NextState(Some(S))` - they should now use `NextState::Pending(S)` - if they created a `NextState(None)` -they should now use `NextState::Unchanged` - if they matched on the `NextState` value, they would need to make the adjustments above If the user manually utilized `apply_state_transition`, they should instead use systems that trigger the `StateTransition` schedule. --- ## Future Work There is still some future potential work in the area, but I wanted to keep these potential features and changes separate to keep the scope here contained, and keep the core of it easy to understand and use. However, I do want to note some of these things, both as inspiration to others and an illustration of what this PR could unlock. - `NextState::Remove` - Now that the `State` related mechanisms all utilize options (#11417), it's fairly easy to add support for explicit state removal. And while `ComputedStates` can add and remove themselves, right now `FreelyMutableState`s can't be removed from within the state system. While it existed originally in this PR, it is a different question with a separate scope and usability concerns - so having it as it's own future PR seems like the best approach. This feature currently lives in a separate branch in my fork, and the differences between it and this PR can be seen here: https://github.com/lee-orr/bevy/pull/5 - `NextState::ReEnter` - this would allow you to trigger exit & entry systems for the current state type. We can potentially also add a `NextState::ReEnterRecirsive` to also re-trigger any states that depend on the current one. - More mechanisms for `State` updates - This PR would finally make states that aren't a set of exclusive Enums useful, and with that comes the question of setting state more effectively. Right now, to update a state you either need to fully create the new state, or include the `Res<Option<State<S>>>` resource in your system, clone the state, mutate it, and then use `NextState.set(my_mutated_state)` to make it the pending next state. There are a few other potential methods that could be implemented in future PRs: - Inverse Compute States - these would essentially be compute states that have an additional (manually defined) function that can be used to nudge the source states so that they result in the computed states having a given value. For example, you could use set the `IsPaused` state, and it would attempt to pause or unpause the game by modifying the `AppState` as needed. - Closure-based state modification - this would involve adding a `NextState.modify(f: impl Fn(Option<S> -> Option<S>)` method, and then you can pass in closures or function pointers to adjust the state as needed. - Message-based state modification - this would involve either creating states that can respond to specific messages, similar to Elm or Redux. These could either use the `NextState` mechanism or the Event mechanism. - ~`SubStates` - which are essentially a hybrid of computed and manual states. In the simplest (and most likely) version, they would work by having a computed element that determines whether the state should exist, and if it should has the capacity to add a new version in, but then any changes to it's content would be freely mutated.~ this feature is now part of this PR. See above. - Lastly, since states are getting more complex there might be value in moving them out of `bevy_ecs` and into their own crate, or at least out of the `schedule` module into a `states` module. #11087 As mentioned, all these future work elements are TBD and are explicitly not part of this PR - I just wanted to provide them as potential explorations for the future. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Marcel Champagne <voiceofmarcel@gmail.com> Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> |
||
![]() |
961b24deaf
|
Implement filmic color grading. (#13121)
This commit expands Bevy's existing tonemapping feature to a complete set of filmic color grading tools, matching those of engines like Unity, Unreal, and Godot. The following features are supported: * White point adjustment. This is inspired by Unity's implementation of the feature, but simplified and optimized. *Temperature* and *tint* control the adjustments to the *x* and *y* chromaticity values of [CIE 1931]. Following Unity, the adjustments are made relative to the [D65 standard illuminant] in the [LMS color space]. * Hue rotation. This simply converts the RGB value to [HSV], alters the hue, and converts back. * Color correction. This allows the *gamma*, *gain*, and *lift* values to be adjusted according to the standard [ASC CDL combined function]. * Separate color correction for shadows, midtones, and highlights. Blender's source code was used as a reference for the implementation of this. The midtone ranges can be adjusted by the user. To avoid abrupt color changes, a small crossfade is used between the different sections of the image, again following Blender's formulas. A new example, `color_grading`, has been added, offering a GUI to change all the color grading settings. It uses the same test scene as the existing `tonemapping` example, which has been factored out into a shared glTF scene. [CIE 1931]: https://en.wikipedia.org/wiki/CIE_1931_color_space [D65 standard illuminant]: https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D [LMS color space]: https://en.wikipedia.org/wiki/LMS_color_space [HSV]: https://en.wikipedia.org/wiki/HSL_and_HSV [ASC CDL combined function]: https://en.wikipedia.org/wiki/ASC_CDL#Combined_Function ## Changelog ### Added * Many new filmic color grading options have been added to the `ColorGrading` component. ## Migration Guide * `ColorGrading::gamma` and `ColorGrading::pre_saturation` are now set separately for the `shadows`, `midtones`, and `highlights` sections. You can migrate code with the `ColorGrading::all_sections` and `ColorGrading::all_sections_mut` functions, which access and/or update all sections at once. * `ColorGrading::post_saturation` and `ColorGrading::exposure` are now fields of `ColorGrading::global`. ## Screenshots   |
||
![]() |
ade70b3925
|
Per-Object Motion Blur (#9924)
https://github.com/bevyengine/bevy/assets/2632925/e046205e-3317-47c3-9959-fc94c529f7e0 # Objective - Adds per-object motion blur to the core 3d pipeline. This is a common effect used in games and other simulations. - Partially resolves #4710 ## Solution - This is a post-process effect that uses the depth and motion vector buffers to estimate per-object motion blur. The implementation is combined from knowledge from multiple papers and articles. The approach itself, and the shader are quite simple. Most of the effort was in wiring up the bevy rendering plumbing, and properly specializing for HDR and MSAA. - To work with MSAA, the MULTISAMPLED_SHADING wgpu capability is required. I've extracted this code from #9000. This is because the prepass buffers are multisampled, and require accessing with `textureLoad` as opposed to the widely compatible `textureSample`. - Added an example to demonstrate the effect of motion blur parameters. ## Future Improvements - While this approach does have limitations, it's one of the most commonly used, and is much better than camera motion blur, which does not consider object velocity. For example, this implementation allows a dolly to track an object, and that object will remain unblurred while the background is blurred. The biggest issue with this implementation is that blur is constrained to the boundaries of objects which results in hard edges. There are solutions to this by either dilating the object or the motion vector buffer, or by taking a different approach such as https://casual-effects.com/research/McGuire2012Blur/index.html - I'm using a noise PRNG function to jitter samples. This could be replaced with a blue noise texture lookup or similar, however after playing with the parameters, it gives quite nice results with 4 samples, and is significantly better than the artifacts generated when not jittering. --- ## Changelog - Added: per-object motion blur. This can be enabled and configured by adding the `MotionBlurBundle` to a camera entity. --------- Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com> |
||
![]() |
d59c859a35
|
new example: sprite animation in response to an event (#12996)
# Objective - animating a sprite in response to an event is a [common beginner problem](https://www.reddit.com/r/bevy/comments/13xx4v7/sprite_animation_in_bevy/) ## Solution - provide a simple example to show how to animate a sprite in response to an event --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
10af274d4e
|
Created loading screen example (#12863)
Allows the user to select a scene to load, then a loading screen is shown until all assets are loaded, and pipelines compiled. # Objective - Fixes #12654 ## Solution - Add desired assets to be monitored to a list. - While there are assets that are not fully loaded, show a loading screen. - Once all assets are loaded, and pipelines compiled, show the scene that was loaded. |
||
![]() |
c0aa5170bc
|
Add example for using .meta files (#12882)
# Objective - Fixes #12411 - Add an example demonstrating the usage of asset meta files. ## Solution - Add a new example displaying a basic scene of three pixelated images - Apply a .meta file to one of the assets setting Nearest filtering - Use AssetServer::load_with_settings on the last one as another way to achieve the same effect - The result is one blurry image and two crisp images demonstrating a common scenario in which changing settings are useful. |
||
![]() |
08b41878d7
|
Add gpu readback example (#12877)
# Objective
- It's pretty common for users to want to read data back from the gpu
and into the main world
## Solution
- Add a simple example that shows how to read data back from the gpu and
send it to the main world using a channel.
- The example is largely based on this wgpu example but adapted to bevy
-
|
||
![]() |
eb82ec047e
|
Remove stepping from default features (#12847)
# Objective Fix #11931 ## Solution - Make stepping a non-default feature - Adjust documentation and examples - In particular, make the breakout example not show the stepping prompt if compiled without the feature (shows a log message instead) --- ## Changelog - Removed `bevy_debug_stepping` from default features ## Migration Guide The system-by-system stepping feature is now disabled by default; to use it, enable the `bevy_debug_stepping` feature explicitly: ```toml [dependencies] bevy = { version = "0.14", features = ["bevy_debug_stepping"] } ``` Code using [`Stepping`](https://docs.rs/bevy/latest/bevy/ecs/schedule/struct.Stepping.html) will still compile with the feature disabled, but will print a runtime error message to the console if the application attempts to enable stepping. --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
93fd02e8ea
|
remove DeterministicRenderingConfig (#12811)
# Objective - Since #12453, `DeterministicRenderingConfig` doesn't do anything ## Solution - Remove it --- ## Migration Guide - Removed `DeterministicRenderingConfig`. There shouldn't be any z fighting anymore in the rendering even without setting `stable_sort_z_fighting` |
||
![]() |
df76fd4a1b
|
Programmed soundtrack example (#12774)
Created soundtrack example, fade-in and fade-out features, added new assets, and updated credits. # Objective - Fixes #12651 ## Solution - Created a resource to hold the track list. - The audio assets are then loaded by the asset server and added to the track list. - Once the game is in a specific state, an `AudioBundle` is spawned and plays the appropriate track. - The audio volume starts at zero and is then incremented gradually until it reaches full volume. - Once the game state changes, the current track fades out, and a new one fades in at the same time, offering a relatively seamless transition. - Once a track is completely faded out, it is despawned from the app. - Game state changes are simulated through a `Timer` for simplicity. - Track change system is only run if there is a change in the `GameState` resource. - All tracks are used according to their respective licenses. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
fee824413f
|
Support wireframes for 2D meshes (#12135)
# Objective Wireframes are currently supported for 3D meshes using the `WireframePlugin` in `bevy_pbr`. This PR adds the same functionality for 2D meshes. Closes #5881. ## Solution Since there's no easy way to share material implementations between 2D, 3D, and UI, this is mostly a straight copy and rename from the original plugin into `bevy_sprite`. <img width="1392" alt="image" src="https://github.com/bevyengine/bevy/assets/3961616/7aca156f-448a-4c7e-89b8-0a72c5919769"> --- ## Changelog - Added `Wireframe2dPlugin` and related types to support 2D wireframes. - Added an example to demonstrate how to use 2D wireframes --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
![]() |
4f20faaa43
|
Meshlet rendering (initial feature) (#10164)
# Objective - Implements a more efficient, GPU-driven (https://github.com/bevyengine/bevy/issues/1342) rendering pipeline based on meshlets. - Meshes are split into small clusters of triangles called meshlets, each of which acts as a mini index buffer into the larger mesh data. Meshlets can be compressed, streamed, culled, and batched much more efficiently than monolithic meshes.   # Misc * Future work: https://github.com/bevyengine/bevy/issues/11518 * Nanite reference: https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf Two pass occlusion culling explained very well: https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501 --------- Co-authored-by: Ricky Taylor <rickytaylor26@gmail.com> Co-authored-by: vero <email@atlasdostal.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: atlas dostal <rodol@rivalrebels.com> |
||
![]() |
887bc27a6f
|
Animatable for colors (#12614)
# Objective - Fixes #12202 ## Solution - Implements `Animatable` for all color types implementing arithmetic operations. - the colors returned by `Animatable`s methods are already clamped. - Adds a `color_animation.rs` example. - Implements the `*Assign` operators for color types that already had the corresponding operators. This is just a 'nice to have' and I am happy to remove this if it's not wanted. --- ## Changelog - `bevy_animation` now depends on `bevy_color`. - `LinearRgba`, `Laba`, `Oklaba` and `Xyza` implement `Animatable`. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
![]() |
e7a31d000e
|
Add border radius to UI nodes (adopted) (#12500)
# Objective Implements border radius for UI nodes. Adopted from #8973, but excludes shadows. ## Solution - Add a component `BorderRadius` which contains a radius value for each corner of the UI node. - Use a fragment shader to generate the rounded corners using a signed distance function. <img width="50%" src="https://github.com/bevyengine/bevy/assets/26204416/16b2ba95-e274-4ce7-adb2-34cc41a776a5"></img> ## Changelog - `BorderRadius`: New component that holds the border radius values. - `NodeBundle` & `ButtonBundle`: Added a `border_radius: BorderRadius` field. - `extract_uinode_borders`: Stripped down, most of the work is done in the shader now. Borders are no longer assembled from multiple rects, instead the shader uses a signed distance function to draw the border. - `UiVertex`: Added size, border and radius fields. - `UiPipeline`: Added three vertex attributes to the vertex buffer layout, to accept the UI node's size, border thickness and border radius. - Examples: Added rounded corners to the UI element in the `button` example, and a `rounded_borders` example. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com> |
||
![]() |
325f0fd982
|
Alignment API for Transforms (#12187)
# Objective - Closes #11793 - Introduces a general API for aligning local coordinates of Transforms with given vectors. ## Solution - We introduce `Transform::align`, which allows a rotation to be specified by four pieces of alignment data, as explained by the documentation: ````rust /// Rotates this [`Transform`] so that the `main_axis` vector, reinterpreted in local coordinates, points /// in the given `main_direction`, while `secondary_axis` points towards `secondary_direction`. /// /// For example, if a spaceship model has its nose pointing in the X-direction in its own local coordinates /// and its dorsal fin pointing in the Y-direction, then `align(Vec3::X, v, Vec3::Y, w)` will make the spaceship's /// nose point in the direction of `v`, while the dorsal fin does its best to point in the direction `w`. /// /// More precisely, the [`Transform::rotation`] produced will be such that: /// * applying it to `main_axis` results in `main_direction` /// * applying it to `secondary_axis` produces a vector that lies in the half-plane generated by `main_direction` and /// `secondary_direction` (with positive contribution by `secondary_direction`) /// /// [`Transform::look_to`] is recovered, for instance, when `main_axis` is `Vec3::NEG_Z` (the [`Transform::forward`] /// direction in the default orientation) and `secondary_axis` is `Vec3::Y` (the [`Transform::up`] direction in the default /// orientation). (Failure cases may differ somewhat.) /// /// In some cases a rotation cannot be constructed. Another axis will be picked in those cases: /// * if `main_axis` or `main_direction` is zero, `Vec3::X` takes its place /// * if `secondary_axis` or `secondary_direction` is zero, `Vec3::Y` takes its place /// * if `main_axis` is parallel with `secondary_axis` or `main_direction` is parallel with `secondary_direction`, /// a rotation is constructed which takes `main_axis` to `main_direction` along a great circle, ignoring the secondary /// counterparts /// /// Example /// ``` /// # use bevy_math::{Vec3, Quat}; /// # use bevy_transform::components::Transform; /// let mut t1 = Transform::IDENTITY; /// let mut t2 = Transform::IDENTITY; /// t1.align(Vec3::ZERO, Vec3::Z, Vec3::ZERO, Vec3::X); /// t2.align(Vec3::X, Vec3::Z, Vec3::Y, Vec3::X); /// assert_eq!(t1.rotation, t2.rotation); /// /// t1.align(Vec3::X, Vec3::Z, Vec3::X, Vec3::Y); /// assert_eq!(t1.rotation, Quat::from_rotation_arc(Vec3::X, Vec3::Z)); /// ``` pub fn align( &mut self, main_axis: Vec3, main_direction: Vec3, secondary_axis: Vec3, secondary_direction: Vec3, ) { //... } ```` - We introduce `Transform::aligned_by`, the returning-Self version of `align`: ````rust pub fn aligned_by( mut self, main_axis: Vec3, main_direction: Vec3, secondary_axis: Vec3, secondary_direction: Vec3, ) -> Self { //... } ```` - We introduce an example (examples/transforms/align.rs) that shows the usage of this API. It is likely to be mathier than most other `Transform` APIs, so when run, the example demonstrates what the API does in space: <img width="1440" alt="Screenshot 2024-03-12 at 11 01 19 AM" src="https://github.com/bevyengine/bevy/assets/2975848/884b3cc3-cbd9-48ae-8f8c-49a677c59dfe"> --- ## Changelog - Added methods `align`, `aligned_by` to `Transform`. - Added transforms/align.rs to examples. --- ## Discussion ### On the form of `align` The original issue linked above suggests an API similar to that of the existing `Transform::look_to` method: ````rust pub fn align_to(&mut self, direction: Vec3, up: Vec3) { //... } ```` Not allowing an input axis of some sort that is to be aligned with `direction` would not really solve the problem in the issue, since the user could easily be in a scenario where they have to compose with another rotation on their own (undesirable). This leads to something like: ````rust pub fn align_to(&mut self, axis: Vec3, direction: Vec3, up: Vec3) { //... } ```` However, this still has two problems: - If the vector that the user wants to align is parallel to the Y-axis, then the API basically does not work (we cannot fully specify a rotation) - More generally, it does not give the user the freedom to specify which direction is to be treated as the local "up" direction, so it fails as a general alignment API Specifying both leads us to the present situation, with two local axis inputs (`main_axis` and `secondary_axis`) and two target directions (`main_direction` and `secondary_direction`). This might seem a little cumbersome for general use, but for the time being I stand by the decision not to expand further without prompting from users. I'll expand on this below. ### Additional APIs? Presently, this PR introduces only `align` and `aligned_by`. Other potentially useful bundles of API surface arrange into a few different categories: 1. Inferring direction from position, a la `Transform::look_at`, which might look something like this: ````rust pub fn align_at(&mut self, axis: Vec3, target: Vec3, up: Vec3) { self.align(axis, target - self.translation, Vec3::Y, up); } ```` (This is simple but still runs into issues when the user wants to point the local Y-axis somewhere.) 2. Filling in some data for the user for common use-cases; e.g.: ````rust pub fn align_x(&mut self, direction: Vec3, up: Vec3) { self.align(Vec3::X, direction, Vec3::Y, up); } ```` (Here, use of the `up` vector doesn't lose any generality, but it might be less convenient to specify than something else. This does naturally leave open the question of what `align_y` would look like if we provided it.) Morally speaking, I do think that the `up` business is more pertinent when the intention is to work with cameras, which the `look_at` and `look_to` APIs seem to cover pretty well. If that's the case, then I'm not sure what the ideal shape for these API functions would be, since it seems like a lot of input would have to be baked into the function definitions. For some cases, this might not be the end of the world: ````rust pub fn align_x_z(&mut self, direction: Vec3, weak_direction: Vec3) { self.align(Vec3::X, direction, Vec3::Z, weak_direction); } ```` (However, this is not symmetrical in x and z, so you'd still need six API functions just to support the standard positive coordinate axes, and if you support negative axes then things really start to balloon.) The reasons that these are not actually produced in this PR are as follows: 1. Without prompting from actual users in the wild, it is unknown to me whether these additional APIs would actually see a lot of use. Extending these to our users in the future would be trivial if we see there is a demand for something specific from the above-mentioned categories. 2. As discussed above, there are so many permutations of these that could be provided that trying to do so looks like it risks unduly ballooning the API surface for this feature. 3. Finally, and most importantly, creating these helper functions in user-space is trivial, since they all just involve specializing `align` to particular inputs; e.g.: ````rust fn align_ship(ship_transform: &mut Transform, nose_direction: Vec3, dorsal_direction: Vec3) { ship_transform.align(Ship::NOSE, nose_direction, Ship::DORSAL, dorsal_direction); } ```` With that in mind, I would prefer instead to focus on making the documentation and examples for a thin API as clear as possible, so that users can get a grip on the tool and specialize it for their own needs when they feel the desire to do so. ### `Dir3`? As in the case of `Transform::look_to` and `Transform::look_at`, the inputs to this function are, morally speaking, *directions* rather than vectors (actually, if we're being pedantic, the input is *really really* a pair of orthonormal frames), so it's worth asking whether we should really be using `Dir3` as inputs instead of `Vec3`. I opted for `Vec3` for the following reasons: 1. Specifying a `Dir3` in user-space is just more annoying than providing a `Vec3`. Even in the most basic cases (e.g. providing a vector literal), you still have to do error handling or call an unsafe unwrap in your function invocations. 2. The existing API mentioned above uses `Vec3`, so we are just adhering to the same thing. Of course, the use of `Vec3` has its own downsides; it can be argued that the replacement of zero-vectors with fixed ones (which we do in `Transform::align` as well as `Transform::look_to`) more-or-less amounts to failing silently. ### Future steps The question of additional APIs was addressed above. For me, the main thing here to handle more immediately is actually just upstreaming this API (or something similar and slightly mathier) to `glam::Quat`. The reason that this would be desirable for users is that this API currently only works with `Transform`s even though all it's actually doing is specifying a rotation. Upstreaming to `glam::Quat`, properly done, could buy a lot basically for free, since a number of `Transform` methods take a rotation as an input. Using these together would require a little bit of mathematical savvy, but it opens up some good things (e.g. `Transform::rotate_around`). |
||
![]() |
2d29954034
|
Fps overlay (#12382)
# Objective - Part of #12351 - Add fps overlay ## Solution - Create `FpsOverlayPlugin` - Allow for configuration through resource `FpsOverlayConfig` - Allow for configuration during runtime ### Preview on default settings  --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
dfdf2b9ea4
|
Implement the AnimationGraph , allowing for multiple animations to be blended together. (#11989)
This is an implementation of RFC #51: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md Note that the implementation strategy is different from the one outlined in that RFC, because two-phase animation has now landed. # Objective Bevy needs animation blending. The RFC for this is [RFC 51]. ## Solution This is an implementation of the RFC. Note that the implementation strategy is different from the one outlined there, because two-phase animation has now landed. This is just a draft to get the conversation started. Currently we're missing a few things: - [x] A fully-fleshed-out mechanism for transitions - [x] A serialization format for `AnimationGraph`s - [x] Examples are broken, other than `animated_fox` - [x] Documentation --- ## Changelog ### Added * The `AnimationPlayer` has been reworked to support blending multiple animations together through an `AnimationGraph`, and as such will no longer function unless a `Handle<AnimationGraph>` has been added to the entity containing the player. See [RFC 51] for more details. * Transition functionality has moved from the `AnimationPlayer` to a new component, `AnimationTransitions`, which works in tandem with the `AnimationGraph`. ## Migration Guide * `AnimationPlayer`s can no longer play animations by themselves and need to be paired with a `Handle<AnimationGraph>`. Code that was using `AnimationPlayer` to play animations will need to create an `AnimationGraph` asset first, add a node for the clip (or clips) you want to play, and then supply the index of that node to the `AnimationPlayer`'s `play` method. * The `AnimationPlayer::play_with_transition()` method has been removed and replaced with the `AnimationTransitions` component. If you were previously using `AnimationPlayer::play_with_transition()`, add all animations that you were playing to the `AnimationGraph`, and create an `AnimationTransitions` component to manage the blending between them. [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md --------- Co-authored-by: Rob Parrett <robparrett@gmail.com> |
||
![]() |
6f2ecdf822
|
We must have googly eyes (new Game example) (#12331)
# Objective - We must have googly eyes. - Also it would be nice if there was an example of a desk toy application (like the old NEKO.EXE). ## Solution - Created an example with googly eyed Bevy logo under examples/games/desktoy.rs. --- ## Changelog - Added "Desk Toy" game example showcasing window transparency and hit test. --------- Co-authored-by: Rob Parrett <robparrett@gmail.com> |
||
![]() |
e3b318f599
|
Add extra_asset_source example (#11824)
# Objective - Make it easier to figure out how to add asset sources ## Solution - Add an example that adds an asset source, it functions almost identical to the embedded_asset example - Move the file from the embedded_asset example into a `files/` folder |
||
![]() |
fc202f2e3d
|
Slicing support for texture atlas (#12059)
# Objective Follow up to #11600 and #10588 https://github.com/bevyengine/bevy/issues/11944 made clear that some people want to use slicing with texture atlases ## Changelog * Added support for `TextureAtlas` slicing and tiling. `SpriteSheetBundle` and `AtlasImageBundle` can now use `ImageScaleMode` * Added new `ui_texture_atlas_slice` example using a texture sheet <img width="798" alt="Screenshot 2024-02-23 at 11 58 35" src="https://github.com/bevyengine/bevy/assets/26703856/47a8b764-127c-4a06-893f-181703777501"> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com> |
||
![]() |
4673fb3e57
|
Example for axes gizmos (#12299)
# Objective - Follow-up to #12211 - Introduces an example project that demonstrates the implementation and behavior of `Gizmos::axes` for an entity with a `Transform` component. ## Solution In order to demonstrate how `Gizmo::axes` can be used and behaves in practice, we introduce an example of a simple scene containing a pair of cuboids locked in a grotesque, inscrutable dance: the two are repeatedly given random `Transform`s which they interpolate to, showing how the axes move with objects as they translate, rotate, and scale. <img width="1023" alt="Screenshot 2024-03-04 at 1 16 33 PM" src="https://github.com/bevyengine/bevy/assets/2975848/c1ff4794-6722-491c-8522-f59801645139"> On the implementation side, we demonstrate how to draw axes for entities, automatically sizing them according to their bounding boxes (so that the axes will be visible): ````rust fn draw_axes(mut gizmos: Gizmos, query: Query<(&Transform, &Aabb), With<ShowAxes>>) { for (&transform, &aabb) in &query { let length = aabb.half_extents.length(); gizmos.axes(transform, length); } } ```` --- ## Changelog - Created examples/gizmos/axes.rs. - Added 'axes' example to Cargo.toml. |
||
![]() |
14c20a6c9c
|
Add access to App within LogPlugin::update_subscriber (#12045)
# Objective In my library, [`bevy_dev_console`](https://github.com/doonv/bevy_dev_console) I need access to `App` within `LogPlugin::update_subscriber` in order to communicate with the `App` from my custom `Layer`. ## Solution Give access to `App`. --- ## Changelog - Added access to `App` within `LogPlugin::update_subscriber` ## Migration Guide `LogPlugin::update_subscriber` now has a `&mut App` parameter. If you don't need access to `App`, you can ignore the parameter with an underscore (`_`). ```diff,rust - fn update_subscriber(subscriber: BoxedSubscriber) -> BoxedSubscriber { + fn update_subscriber(_: &mut App, subscriber: BoxedSubscriber) -> BoxedSubscriber { Box::new(subscriber.with(CustomLayer)) } ``` |
||
![]() |
de0ed293fa
|
Add basic light gizmos (#12228)
# Objective - Part of #9400. - Add light gizmos for `SpotLight`, `PointLight` and `DirectionalLight`. ## Solution - Add a `ShowLightGizmo` and its related gizmo group and plugin, that shows a gizmo for all lights of an entities when inserted on it. Light display can also be toggled globally through the gizmo config in the same way it can already be done for `Aabb`s. - Add distinct segment setters for height and base one `Cone3dBuilder`. This allow having a properly rounded base without too much edges along the height. The doc comments explain how to ensure height and base connect when setting different values. Gizmo for the three light types without radius with the depth bias set to -1:  With Radius:  Possible future improvements: - Add a billboarded sprite with a distinct sprite for each light type. - Display the intensity of the light somehow (no idea how to represent that apart from some text). --- ## Changelog ### Added - The new `ShowLightGizmo`, part of the `LightGizmoPlugin` and configurable globally with `LightGizmoConfigGroup`, allows drawing gizmo for `PointLight`, `SpotLight` and `DirectionalLight`. The gizmos color behavior can be controlled with the `LightGizmoColor` member of `ShowLightGizmo` and `LightGizmoConfigGroup`. - The cone gizmo builder (`Cone3dBuilder`) now allows setting a differing number of segments for the base and height. --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
![]() |
165c360070
|
Update wgpu to v0.19.3 and unpin web-sys . (#12247)
# Objective This PR unpins `web-sys` so that unrelated projects that have `bevy_render` in their workspace can finally update their `web-sys`. More details in and fixes #12246. ## Solution * Update `wgpu` from 0.19.1 to 0.19.3. * Remove the `web-sys` pin. * Update docs and wasm helper to remove the now-stale `--cfg=web_sys_unstable_apis` Rust flag. --- ## Changelog Updated `wgpu` to v0.19.3 and removed `web-sys` pin. |
||
![]() |
94ff123d7f
|
Component Lifecycle Hooks and a Deferred World (#10756)
# Objective - Provide a reliable and performant mechanism to allows users to keep components synchronized with external sources: closing/opening sockets, updating indexes, debugging etc. - Implement a generic mechanism to provide mutable access to the world without allowing structural changes; this will not only be used here but is a foundational piece for observers, which are key for a performant implementation of relations. ## Solution - Implement a new type `DeferredWorld` (naming is not important, `StaticWorld` is also suitable) that wraps a world pointer and prevents user code from making any structural changes to the ECS; spawning entities, creating components, initializing resources etc. - Add component lifecycle hooks `on_add`, `on_insert` and `on_remove` that can be assigned callbacks in user code. --- ## Changelog - Add new `DeferredWorld` type. - Add new world methods: `register_component::<T>` and `register_component_with_descriptor`. These differ from `init_component` in that they provide mutable access to the created `ComponentInfo` but will panic if the component is already in any archetypes. These restrictions serve two purposes: 1. Prevent users from defining hooks for components that may already have associated hooks provided in another plugin. (a use case better served by observers) 2. Ensure that when an `Archetype` is created it gets the appropriate flags to early-out when triggering hooks. - Add methods to `ComponentInfo`: `on_add`, `on_insert` and `on_remove` to be used to register hooks of the form `fn(DeferredWorld, Entity, ComponentId)` - Modify `BundleInserter`, `BundleSpawner` and `EntityWorldMut` to trigger component hooks when appropriate. - Add bit flags to `Archetype` indicating whether or not any contained components have each type of hook, this can be expanded for other flags as needed. - Add `component_hooks` example to illustrate usage. Try it out! It's fun to mash keys. ## Safety The changes to component insertion, removal and deletion involve a large amount of unsafe code and it's fair for that to raise some concern. I have attempted to document it as clearly as possible and have confirmed that all the hooks examples are accepted by `cargo miri` as not causing any undefined behavior. The largest issue is in ensuring there are no outstanding references when passing a `DeferredWorld` to the hooks which requires some use of raw pointers (as was already happening to some degree in those places) and I have taken some time to ensure that is the case but feel free to let me know if I've missed anything. ## Performance These changes come with a small but measurable performance cost of between 1-5% on `add_remove` benchmarks and between 1-3% on `insert` benchmarks. One consideration to be made is the existence of the current `RemovedComponents` which is on average more costly than the addition of `on_remove` hooks due to the early-out, however hooks doesn't completely remove the need for `RemovedComponents` as there is a chance you want to respond to the removal of a component that already has an `on_remove` hook defined in another plugin, so I have not removed it here. I do intend to deprecate it with the introduction of observers in a follow up PR. ## Discussion Questions - Currently `DeferredWorld` implements `Deref` to `&World` which makes sense conceptually, however it does cause some issues with rust-analyzer providing autocomplete for `&mut World` references which is annoying. There are alternative implementations that may address this but involve more code churn so I have attempted them here. The other alternative is to not implement `Deref` at all but that leads to a large amount of API duplication. - `DeferredWorld`, `StaticWorld`, something else? - In adding support for hooks to `EntityWorldMut` I encountered some unfortunate difficulties with my desired API. If commands are flushed after each call i.e. `world.spawn() // flush commands .insert(A) // flush commands` the entity may be despawned while `EntityWorldMut` still exists which is invalid. An alternative was then to add `self.world.flush_commands()` to the drop implementation for `EntityWorldMut` but that runs into other problems for implementing functions like `into_unsafe_entity_cell`. For now I have implemented a `.flush()` which will flush the commands and consume `EntityWorldMut` or users can manually run `world.flush_commands()` after using `EntityWorldMut`. - In order to allowing querying on a deferred world we need implementations of `WorldQuery` to not break our guarantees of no structural changes through their `UnsafeWorldCell`. All our implementations do this, but there isn't currently any safety documentation specifying what is or isn't allowed for an implementation, just for the caller, (they also shouldn't be aliasing components they didn't specify access for etc.) is that something we should start doing? (see 10752) Please check out the example `component_hooks` or the tests in `bundle.rs` for usage examples. I will continue to expand this description as I go. See #10839 for a more ergonomic API built on top of this one that isn't subject to the same restrictions and supports `SystemParam` dependency injection. |
||
![]() |
a463d0c637
|
Fixed iOS documentation typo for xcrun command (#12112)
``` $ xcrun simctl devices list Unrecognized subcommand: devices usage: simctl [--set <path>] [--profiles <path>] <subcommand> ... simctl help [subcommand] Command line utility to control the Simulator ``` # Objective - The `examples/README.md` contains an invalid example command for listing iOS devices using `xcrun`. ## Solution - Update example command to omit the current invalid subcommand "`devices`". |
||
![]() |
d3e839a8e5
|
Move gizmos examples in the separate folder (#11916)
# Objective Move Gizmo examples into the separate directory Fixes #11899 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> |
||
![]() |
b446374392
|
Dedicated primitive example (#11697)
I just implemented this to record a video for the new blog post, but I figured it would also make a good dedicated example. This also allows us to remove a lot of code from the 2d/3d gizmo examples since it supersedes this portion of code. Depends on: https://github.com/bevyengine/bevy/pull/11699 |
||
![]() |
ab16f5ed6a
|
UI Texture 9 slice (#11600)
> Follow up to #10588 > Closes #11749 (Supersedes #11756) Enable Texture slicing for the following UI nodes: - `ImageBundle` - `ButtonBundle` <img width="739" alt="Screenshot 2024-01-29 at 13 57 43" src="https://github.com/bevyengine/bevy/assets/26703856/37675681-74eb-4689-ab42-024310cf3134"> I also added a collection of `fantazy-ui-borders` from [Kenney's](www.kenney.nl) assets, with the appropriate license (CC). If it's a problem I can use the same textures as the `sprite_slice` example # Work done Added the `ImageScaleMode` component to the targetted bundles, most of the logic is directly reused from `bevy_sprite`. The only additional internal component is the UI specific `ComputedSlices`, which does the same thing as its spritee equivalent but adapted to UI code. Again the slicing is not compatible with `TextureAtlas`, it's something I need to tackle more deeply in the future # Fixes * [x] I noticed that `TextureSlicer::compute_slices` could infinitely loop if the border was larger that the image half extents, now an error is triggered and the texture will fallback to being stretched * [x] I noticed that when using small textures with very small *tiling* options we could generate hundred of thousands of slices. Now I set a minimum size of 1 pixel per slice, which is already ridiculously small, and a warning will be sent at runtime when slice count goes above 1000 * [x] Sprite slicing with `flip_x` or `flip_y` would give incorrect results, correct flipping is now supported to both sprites and ui image nodes thanks to @odecay observation # GPU Alternative I create a separate branch attempting to implementing 9 slicing and tiling directly through the `ui.wgsl` fragment shader. It works but requires sending more data to the GPU: - slice border - tiling factors And more importantly, the actual quad *scale* which is hard to put in the shader with the current code, so that would be for a later iteration |
||
![]() |
4c15dd0fc5
|
Implement irradiance volumes. (#10268)
# Objective Bevy could benefit from *irradiance volumes*, also known as *voxel global illumination* or simply as light probes (though this term is not preferred, as multiple techniques can be called light probes). Irradiance volumes are a form of baked global illumination; they work by sampling the light at the centers of each voxel within a cuboid. At runtime, the voxels surrounding the fragment center are sampled and interpolated to produce indirect diffuse illumination. ## Solution This is divided into two sections. The first is copied and pasted from the irradiance volume module documentation and describes the technique. The second part consists of notes on the implementation. ### Overview An *irradiance volume* is a cuboid voxel region consisting of regularly-spaced precomputed samples of diffuse indirect light. They're ideal if you have a dynamic object such as a character that can move about static non-moving geometry such as a level in a game, and you want that dynamic object to be affected by the light bouncing off that static geometry. To use irradiance volumes, you need to precompute, or *bake*, the indirect light in your scene. Bevy doesn't currently come with a way to do this. Fortunately, [Blender] provides a [baking tool] as part of the Eevee renderer, and its irradiance volumes are compatible with those used by Bevy. The [`bevy-baked-gi`] project provides a tool, `export-blender-gi`, that can extract the baked irradiance volumes from the Blender `.blend` file and package them up into a `.ktx2` texture for use by the engine. See the documentation in the `bevy-baked-gi` project for more details as to this workflow. Like all light probes in Bevy, irradiance volumes are 1×1×1 cubes that can be arbitrarily scaled, rotated, and positioned in a scene with the [`bevy_transform::components::Transform`] component. The 3D voxel grid will be stretched to fill the interior of the cube, and the illumination from the irradiance volume will apply to all fragments within that bounding region. Bevy's irradiance volumes are based on Valve's [*ambient cubes*] as used in *Half-Life 2* ([Mitchell 2006], slide 27). These encode a single color of light from the six 3D cardinal directions and blend the sides together according to the surface normal. The primary reason for choosing ambient cubes is to match Blender, so that its Eevee renderer can be used for baking. However, they also have some advantages over the common second-order spherical harmonics approach: ambient cubes don't suffer from ringing artifacts, they are smaller (6 colors for ambient cubes as opposed to 9 for spherical harmonics), and evaluation is faster. A smaller basis allows for a denser grid of voxels with the same storage requirements. If you wish to use a tool other than `export-blender-gi` to produce the irradiance volumes, you'll need to pack the irradiance volumes in the following format. The irradiance volume of resolution *(Rx, Ry, Rz)* is expected to be a 3D texture of dimensions *(Rx, 2Ry, 3Rz)*. The unnormalized texture coordinate *(s, t, p)* of the voxel at coordinate *(x, y, z)* with side *S* ∈ *{-X, +X, -Y, +Y, -Z, +Z}* is as follows: ```text s = x t = y + ⎰ 0 if S ∈ {-X, -Y, -Z} ⎱ Ry if S ∈ {+X, +Y, +Z} ⎧ 0 if S ∈ {-X, +X} p = z + ⎨ Rz if S ∈ {-Y, +Y} ⎩ 2Rz if S ∈ {-Z, +Z} ``` Visually, in a left-handed coordinate system with Y up, viewed from the right, the 3D texture looks like a stacked series of voxel grids, one for each cube side, in this order: | **+X** | **+Y** | **+Z** | | ------ | ------ | ------ | | **-X** | **-Y** | **-Z** | A terminology note: Other engines may refer to irradiance volumes as *voxel global illumination*, *VXGI*, or simply as *light probes*. Sometimes *light probe* refers to what Bevy calls a reflection probe. In Bevy, *light probe* is a generic term that encompasses all cuboid bounding regions that capture indirect illumination, whether based on voxels or not. Note that, if binding arrays aren't supported (e.g. on WebGPU or WebGL 2), then only the closest irradiance volume to the view will be taken into account during rendering. [*ambient cubes*]: https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf [Mitchell 2006]: https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf [Blender]: http://blender.org/ [baking tool]: https://docs.blender.org/manual/en/latest/render/eevee/render_settings/indirect_lighting.html [`bevy-baked-gi`]: https://github.com/pcwalton/bevy-baked-gi ### Implementation notes This patch generalizes light probes so as to reuse as much code as possible between irradiance volumes and the existing reflection probes. This approach was chosen because both techniques share numerous similarities: 1. Both irradiance volumes and reflection probes are cuboid bounding regions. 2. Both are responsible for providing baked indirect light. 3. Both techniques involve presenting a variable number of textures to the shader from which indirect light is sampled. (In the current implementation, this uses binding arrays.) 4. Both irradiance volumes and reflection probes require gathering and sorting probes by distance on CPU. 5. Both techniques require the GPU to search through a list of bounding regions. 6. Both will eventually want to have falloff so that we can smoothly blend as objects enter and exit the probes' influence ranges. (This is not implemented yet to keep this patch relatively small and reviewable.) To do this, we generalize most of the methods in the reflection probes patch #11366 to be generic over a trait, `LightProbeComponent`. This trait is implemented by both `EnvironmentMapLight` (for reflection probes) and `IrradianceVolume` (for irradiance volumes). Using a trait will allow us to add more types of light probes in the future. In particular, I highly suspect we will want real-time reflection planes for mirrors in the future, which can be easily slotted into this framework. ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added * A new `IrradianceVolume` asset type is available for baked voxelized light probes. You can bake the global illumination using Blender or another tool of your choice and use it in Bevy to apply indirect illumination to dynamic objects. |
||
![]() |
ab9447ac32
|
Add example for bounding volumes and intersection tests (#11666)
# Objective - Create an example for bounding volumes and intersection tests ## Solution - Add an example with a few bounding volumes, created from primitives - Allow the user to cycle trough the different intersection tests |
||
![]() |
5c52d0aeee
|
System Stepping implemented as Resource (#8453)
# Objective Add interactive system debugging capabilities to bevy, providing step/break/continue style capabilities to running system schedules. * Original implementation: #8063 - `ignore_stepping()` everywhere was too much complexity * Schedule-config & Resource discussion: #8168 - Decided on selective adding of Schedules & Resource-based control ## Solution Created `Stepping` Resource. This resource can be used to enable stepping on a per-schedule basis. Systems within schedules can be individually configured to: * AlwaysRun: Ignore any stepping state and run every frame * NeverRun: Never run while stepping is enabled - this allows for disabling of systems while debugging * Break: If we're running the full frame, stop before this system is run Stepping provides two modes of execution that reflect traditional debuggers: * Step-based: Only execute one system at a time * Continue/Break: Run all systems, but stop before running a system marked as Break ### Demo https://user-images.githubusercontent.com/857742/233630981-99f3bbda-9ca6-4cc4-a00f-171c4946dc47.mov Breakout has been modified to use Stepping. The game runs normally for a couple of seconds, then stepping is enabled and the game appears to pause. A list of Schedules & Systems appears with a cursor at the first System in the list. The demo then steps forward full frames using the spacebar until the ball is about to hit a brick. Then we step system by system as the ball impacts a brick, showing the cursor moving through the individual systems. Finally the demo switches back to frame stepping as the ball changes course. ### Limitations Due to architectural constraints in bevy, there are some cases systems stepping will not function as a user would expect. #### Event-driven systems Stepping does not support systems that are driven by `Event`s as events are flushed after 1-2 frames. Although game systems are not running while stepping, ignored systems are still running every frame, so events will be flushed. This presents to the user as stepping the event-driven system never executes the system. It does execute, but the events have already been flushed. This can be resolved by changing event handling to use a buffer for events, and only dropping an event once all readers have read it. The work-around to allow these systems to properly execute during stepping is to have them ignore stepping: `app.add_systems(event_driven_system.ignore_stepping())`. This was done in the breakout example to ensure sound played even while stepping. #### Conditional Systems When a system is stepped, it is given an opportunity to run. If the conditions of the system say it should not run, it will not. Similar to Event-driven systems, if a system is conditional, and that condition is only true for a very small time window, then stepping the system may not execute the system. This includes depending on any sort of external clock. This exhibits to the user as the system not always running when it is stepped. A solution to this limitation is to ensure any conditions are consistent while stepping is enabled. For example, all systems that modify any state the condition uses should also enable stepping. #### State-transition Systems Stepping is configured on the per-`Schedule` level, requiring the user to have a `ScheduleLabel`. To support state-transition systems, bevy generates needed schedules dynamically. Currently it’s very difficult (if not impossible, I haven’t verified) for the user to get the labels for these schedules. Without ready access to the dynamically generated schedules, and a resolution for the `Event` lifetime, **stepping of the state-transition systems is not supported** --- ## Changelog - `Schedule::run()` updated to consult `Stepping` Resource to determine which Systems to run each frame - Added `Schedule.label` as a `BoxedSystemLabel`, along with supporting `Schedule::set_label()` and `Schedule::label()` methods - `Stepping` needed to know which `Schedule` was running, and prior to this PR, `Schedule` didn't track its own label - Would have preferred to add `Schedule::with_label()` and remove `Schedule::new()`, but this PR touches enough already - Added calls to `Schedule.set_label()` to `App` and `World` as needed - Added `Stepping` resource - Added `Stepping::begin_frame()` system to `MainSchedulePlugin` - Run before `Main::run_main()` - Notifies any `Stepping` Resource a new render frame is starting ## Migration Guide - Add a call to `Schedule::set_label()` for any custom `Schedule` - This is only required if the `Schedule` will be stepped --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
176223b406
|
Fix embedded asset path manipulation (#10383)
# Objective Fixes #10377 ## Solution Use `Path::strip_prefix` instead of `str::split`. Avoid any explicit "/" characters in path manipulation. --- ## Changelog - Added: example of embedded asset loading - Added: support embedded assets in external crates - Fixed: resolution of embedded assets - Fixed: unexpected runtime panic during asset path resolution ## Migration Guide No API changes. --------- Co-authored-by: Shane Celis <shane.celis@gmail.com> |
||
![]() |
2bf481c03b
|
Add Meshable trait and implement meshing for 2D primitives (#11431)
# Objective The first part of #10569, split up from #11007. The goal is to implement meshing support for Bevy's new geometric primitives, starting with 2D primitives. 3D meshing will be added in a follow-up, and we can consider removing the old mesh shapes completely. ## Solution Add a `Meshable` trait that primitives need to implement to support meshing, as suggested by the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/12-primitive-shapes.md#meshing). ```rust /// A trait for shapes that can be turned into a [`Mesh`]. pub trait Meshable { /// The output of [`Self::mesh`]. This can either be a [`Mesh`] /// or a builder used for creating a [`Mesh`]. type Output; /// Creates a [`Mesh`] for a shape. fn mesh(&self) -> Self::Output; } ``` This PR implements it for the following primitives: - `Circle` - `Ellipse` - `Rectangle` - `RegularPolygon` - `Triangle2d` The `mesh` method typically returns a builder-like struct such as `CircleMeshBuilder`. This is needed to support shape-specific configuration for things like mesh resolution or UV configuration: ```rust meshes.add(Circle { radius: 0.5 }.mesh().resolution(64)); ``` Note that if no configuration is needed, you can even skip calling `mesh` because `From<MyPrimitive>` is implemented for `Mesh`: ```rust meshes.add(Circle { radius: 0.5 }); ``` I also updated the `2d_shapes` example to use primitives, and tweaked the colors to have better contrast against the dark background. Before:  After:  Here you can see the UVs and different facing directions: (taken from #11007, so excuse the 3D primitives at the bottom left)  --- ## Changelog - Added `bevy_render::mesh::primitives` module - Added `Meshable` trait and implemented it for: - `Circle` - `Ellipse` - `Rectangle` - `RegularPolygon` - `Triangle2d` - Implemented `Default` and `Copy` for several 2D primitives - Updated `2d_shapes` example to use primitives - Tweaked colors in `2d_shapes` example to have better contrast against the (new-ish) dark background --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
149a313850
|
Add an example demonstrating how to send and receive events in the same system (#11574)
# Objective - Sending and receiving events of the same type in the same system is a reasonably common need, generally due to event filtering. - However, actually doing so is non-trivial, as the borrow checker simultaneous hates mutable and immutable access. ## Solution - Demonstrate two sensible patterns for doing so. - Update the `ManualEventReader` docs to be more clear and link to this example. --------- Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> Co-authored-by: ickk <git@ickk.io> |
||
![]() |
35ac1b152e
|
Update to wgpu 0.19 and raw-window-handle 0.6 (#11280)
# Objective Keep core dependencies up to date. ## Solution Update the dependencies. wgpu 0.19 only supports raw-window-handle (rwh) 0.6, so bumping that was included in this. The rwh 0.6 version bump is just the simplest way of doing it. There might be a way we can take advantage of wgpu's new safe surface creation api, but I'm not familiar enough with bevy's window management to untangle it and my attempt ended up being a mess of lifetimes and rustc complaining about missing trait impls (that were implemented). Thanks to @MiniaczQ for the (much simpler) rwh 0.6 version bump code. Unblocks https://github.com/bevyengine/bevy/pull/9172 and https://github.com/bevyengine/bevy/pull/10812 ~~This might be blocked on cpal and oboe updating their ndk versions to 0.8, as they both currently target ndk 0.7 which uses rwh 0.5.2~~ Tested on android, and everything seems to work correctly (audio properly stops when minimized, and plays when re-focusing the app). --- ## Changelog - `wgpu` has been updated to 0.19! The long awaited arcanization has been merged (for more info, see https://gfx-rs.github.io/2023/11/24/arcanization.html), and Vulkan should now be working again on Intel GPUs. - Targeting WebGPU now requires that you add the new `webgpu` feature (setting the `RUSTFLAGS` environment variable to `--cfg=web_sys_unstable_apis` is still required). This feature currently overrides the `webgl2` feature if you have both enabled (the `webgl2` feature is enabled by default), so it is not recommended to add it as a default feature to libraries without putting it behind a flag that allows library users to opt out of it! In the future we plan on supporting wasm binaries that can target both webgl2 and webgpu now that wgpu added support for doing so (see https://github.com/bevyengine/bevy/issues/11505). - `raw-window-handle` has been updated to version 0.6. ## Migration Guide - `bevy_render::instance_index::get_instance_index()` has been removed as the webgl2 workaround is no longer required as it was fixed upstream in wgpu. The `BASE_INSTANCE_WORKAROUND` shaderdef has also been removed. - WebGPU now requires the new `webgpu` feature to be enabled. The `webgpu` feature currently overrides the `webgl2` feature so you no longer need to disable all default features and re-add them all when targeting `webgpu`, but binaries built with both the `webgpu` and `webgl2` features will only target the webgpu backend, and will only work on browsers that support WebGPU. - Places where you conditionally compiled things for webgl2 need to be updated because of this change, eg: - `#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]` becomes `#[cfg(any(not(feature = "webgl") ,not(target_arch = "wasm32"), feature = "webgpu"))]` - `#[cfg(all(feature = "webgl", target_arch = "wasm32"))]` becomes `#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]` - `if cfg!(all(feature = "webgl", target_arch = "wasm32"))` becomes `if cfg!(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))` - `create_texture_with_data` now also takes a `TextureDataOrder`. You can probably just set this to `TextureDataOrder::default()` - `TextureFormat`'s `block_size` has been renamed to `block_copy_size` - See the `wgpu` changelog for anything I might've missed: https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md --------- Co-authored-by: François <mockersf@gmail.com> |
||
![]() |
fb367dac72
|
Add Animated Material example (#11524)
# Objective - Fixes #11516 ## Solution - Add Animated Material example (colors are hue-cycling smoothly per-mesh)  Note: this example reproduces the perf issue found in #10610 pretty consistently, with and without the changes from that PR included. Frame time is sometimes around 4.3ms, other times around 12-14ms. Its pretty random per run. I think this clears #10610 for merge. |