bevy/examples/README.md

789 lines
47 KiB
Markdown
Raw Normal View History

Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
<!-- MD024 - The Headers from the Platform-Specific Examples should be identical -->
<!-- Use 'cargo run -p build-templated-pages -- build-example-page' to generate the final example README.md -->
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
<!-- markdownlint-disable-file MD024 -->
2020-08-17 23:02:59 +00:00
# Examples
These examples demonstrate the main features of Bevy and how to use them.
To run an example, use the command `cargo run --example <Example>`, and add the option `--features x11` or `--features wayland` to force the example to run on a specific window compositor, e.g.
```sh
cargo run --features wayland --example hello_world
```
2020-08-17 23:02:59 +00:00
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
**⚠️ Note: for users of releases on crates.io!**
There are often large differences and incompatible API changes between the latest [crates.io](https://crates.io/crates/bevy) release and the development version of Bevy in the git main branch!
If you are using a released version of bevy, you need to make sure you are viewing the correct version of the examples!
- Latest release: [https://github.com/bevyengine/bevy/tree/latest/examples](https://github.com/bevyengine/bevy/tree/latest/examples)
- Specific version, such as `0.4`: [https://github.com/bevyengine/bevy/tree/v0.4.0/examples](https://github.com/bevyengine/bevy/tree/v0.4.0/examples)
When you clone the repo locally to run the examples, use `git checkout` to get the correct version:
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
```bash
# `latest` always points to the newest release
git checkout latest
# or use a specific version
git checkout v0.4.0
```
---
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
## Table of Contents
- [Examples](#examples)
- [Table of Contents](#table-of-contents)
- [The Bare Minimum](#the-bare-minimum)
- [Hello, World!](#hello-world)
- [Cross-Platform Examples](#cross-platform-examples)
- [2D Rendering](#2d-rendering)
- [3D Rendering](#3d-rendering)
- [Animation](#animation)
- [Application](#application)
- [Assets](#assets)
- [Async Tasks](#async-tasks)
- [Audio](#audio)
- [Camera](#camera)
- [Dev tools](#dev-tools)
- [Diagnostics](#diagnostics)
- [ECS (Entity Component System)](#ecs-entity-component-system)
- [Games](#games)
- [Gizmos](#gizmos)
- [Input](#input)
- [Math](#math)
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.
2024-07-09 14:23:10 +00:00
- [Movement](#movement)
- [Picking](#picking)
2020-12-02 22:35:27 +00:00
- [Reflection](#reflection)
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::cool::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>
2024-09-23 18:36:16 +00:00
- [Remote Protocol](#remote-protocol)
- [Scene](#scene)
- [Shaders](#shaders)
- [State](#state)
- [Stress Tests](#stress-tests)
- [Time](#time)
- [Tools](#tools)
- [Transforms](#transforms)
- [UI (User Interface)](#ui-user-interface)
- [Window](#window)
- [Tests](#tests)
- [Platform-Specific Examples](#platform-specific-examples)
- [Android](#android)
- [Setup](#setup)
- [Build & Run](#build--run)
- [About `libc++_shared.so`](#about-libc_sharedso)
- [Old phones](#old-phones)
- [About `cargo-apk`](#about-cargo-apk)
- [iOS](#ios)
- [Setup](#setup-1)
- [Build & Run](#build--run-1)
- [Wasm](#wasm)
- [Setup](#setup-2)
- [Build & Run](#build--run-2)
- [WebGL2 and WebGPU](#webgl2-and-webgpu)
- [Audio in the browsers](#audio-in-the-browsers)
- [Optimizing](#optimizing)
- [Loading Assets](#loading-assets)
# The Bare Minimum
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
<!-- MD026 - Hello, World! looks better with the ! -->
<!-- markdownlint-disable-next-line MD026 -->
2020-08-17 23:02:59 +00:00
## Hello, World!
Example | Description
--- | ---
[`hello_world.rs`](./hello_world.rs) | Runs a minimal example that outputs "hello world"
2020-08-17 23:02:59 +00:00
# Cross-Platform Examples
2020-08-17 23:02:59 +00:00
## 2D Rendering
Example | Description
--- | ---
Revamp Bloom (#6677) ![image](https://user-images.githubusercontent.com/47158642/220197588-25e11022-02e4-45f3-b2e5-392c4ce7a025.png) Huge credit to @StarLederer, who did almost all of the work on this. We're just reusing this PR to keep everything in one place. # Objective 1. Make bloom more physically based. 1. Improve artistic control. 1. Allow to use bloom as screen blur. 1. Fix #6634. 1. Address #6655 (although the author makes incorrect conclusions). ## Solution 1. Set the default threshold to 0. 2. Lerp between bloom textures when `composite_mode: BloomCompositeMode::EnergyConserving`. 1. Use [a parametric function](https://starlederer.github.io/bloom) to control blend levels for each bloom texture. In the future this can be controlled per-pixel for things like lens dirt. 3. Implement BloomCompositeMode::Additive` for situations where the old school look is desired. ## Changelog * Bloom now looks different. * Added `BloomSettings:lf_boost`, `BloomSettings:lf_boost_curvature`, `BloomSettings::high_pass_frequency` and `BloomSettings::composite_mode`. * `BloomSettings::scale` removed. * `BloomSettings::knee` renamed to `BloomPrefilterSettings::softness`. * `BloomSettings::threshold` renamed to `BloomPrefilterSettings::threshold`. * The bloom example has been renamed to bloom_3d and improved. A bloom_2d example was added. ## Migration Guide * Refactor mentions of `BloomSettings::knee` and `BloomSettings::threshold` as `BloomSettings::prefilter_settings` where knee is now `softness`. * If defined without `..default()` add `..default()` to definitions of `BloomSettings` instances or manually define missing fields. * Adapt to Bloom looking visually different (if needed). Co-authored-by: Herman Lederer <germans.lederers@gmail.com>
2023-03-04 12:05:26 +00:00
[2D Bloom](../examples/2d/bloom_2d.rs) | Illustrates bloom post-processing in 2d
[2D Bounding Volume Intersections](../examples/2d/bounding_2d.rs) | Showcases bounding volumes and intersection tests
[2D Rotation](../examples/2d/rotation.rs) | Demonstrates rotating entities in 2D with quaternions
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: ![Old 2D shapes](https://github.com/bevyengine/bevy/assets/57632562/f1d8c2d5-55be-495f-8ed4-5890154b81ca) After: ![New 2D shapes](https://github.com/bevyengine/bevy/assets/57632562/f166c013-34b8-4752-800a-5517b284d978) Here you can see the UVs and different facing directions: (taken from #11007, so excuse the 3D primitives at the bottom left) ![UVs and facing directions](https://github.com/bevyengine/bevy/assets/57632562/eaf0be4e-187d-4b6d-8fb8-c996ba295a8a) --- ## 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>
2024-01-29 16:47:47 +00:00
[2D Shapes](../examples/2d/2d_shapes.rs) | Renders simple 2D primitive shapes like circles and polygons
[2D Viewport To World](../examples/2d/2d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world_2d` method
[2D Wireframe](../examples/2d/wireframe_2d.rs) | Showcases wireframes for 2d meshes
[Arc 2D Meshes](../examples/2d/mesh2d_arcs.rs) | Demonstrates UV-mapping of the circular segment and sector primitives
Add `Image` methods for easy access to a pixel's color (#10392) # Objective If you want to draw / generate images from the CPU, such as: - to create procedurally-generated assets - for games whose artstyle is best implemented by poking pixels directly from the CPU, instead of using shaders It is currently very unergonomic to do in Bevy, because you have to deal with the raw bytes inside `image.data`, take care of the pixel format, etc. ## Solution This PR adds some helper methods to `Image` for pixel manipulation. These methods allow you to use Bevy's user-friendly `Color` struct to read and write the colors of pixels, at arbitrary coordinates (specified as `UVec3` to support any texture dimension). They handle encoding/decoding to the `Image`s `TextureFormat`, incl. any sRGB conversion. While we are at it, also add methods to help with direct access to the raw bytes. It is now easy to compute the offset where the bytes of a specific pixel coordinate are found, or to just get a Rust slice to access them. Caveat: `Color` roundtrips are obviously going to be lossy for non-float `TextureFormat`s. Using `set_color_at` followed by `get_color_at` will return a different value, due to the data conversions involved (such as `f32` -> `u8` -> `f32` for the common `Rgba8UnormSrgb` texture format). Be careful when comparing colors (such as checking for a color you wrote before)! Also adding a new example: `cpu_draw` (under `2d`), to showcase these new APIs. --- ## Changelog ### Added - `Image` APIs for easy access to the colors of specific pixels. --------- Co-authored-by: Pascal Hertleif <killercup@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: ltdk <usr@ltdk.xyz>
2024-10-07 14:38:41 +00:00
[CPU Drawing](../examples/2d/cpu_draw.rs) | Manually read/write the pixels of a texture
[Custom glTF vertex attribute 2D](../examples/2d/custom_gltf_vertex_attribute.rs) | Renders a glTF mesh in 2D with a custom vertex attribute
[Manual Mesh 2D](../examples/2d/mesh2d_manual.rs) | Renders a custom mesh "manually" with "mid-level" renderer apis
[Mesh 2D](../examples/2d/mesh2d.rs) | Renders a 2d mesh
[Mesh 2D With Vertex Colors](../examples/2d/mesh2d_vertex_color_texture.rs) | Renders a 2d mesh with vertex color attributes
[Mesh2d Alpha Mode](../examples/2d/mesh2d_alpha_mode.rs) | Used to test alpha modes with mesh2d
[Move Sprite](../examples/2d/move_sprite.rs) | Changes the transform of a sprite
[Pixel Grid Snapping](../examples/2d/pixel_grid_snap.rs) | Shows how to create graphics that snap to the pixel grid by rendering to a texture in 2D
[Sprite](../examples/2d/sprite.rs) | Renders a sprite
[Sprite Animation](../examples/2d/sprite_animation.rs) | Animates a sprite in response to an event
[Sprite Flipping](../examples/2d/sprite_flipping.rs) | Renders a sprite flipped along an axis
[Sprite Sheet](../examples/2d/sprite_sheet.rs) | Renders an animated sprite
Sprite slicing and tiling (#10588) > Replaces #5213 # Objective Implement sprite tiling and [9 slice scaling](https://en.wikipedia.org/wiki/9-slice_scaling) for `bevy_sprite`. Allowing slice scaling and texture tiling. Basic scaling vs 9 slice scaling: ![Traditional_scaling_vs_9-slice_scaling](https://user-images.githubusercontent.com/26703856/177335801-27f6fa27-c569-4ce6-b0e6-4f54e8f4e80a.svg) Slicing example: <img width="481" alt="Screenshot 2022-07-05 at 15 05 49" src="https://user-images.githubusercontent.com/26703856/177336112-9e961af0-c0af-4197-aec9-430c1170a79d.png"> Tiling example: <img width="1329" alt="Screenshot 2023-11-16 at 13 53 32" src="https://github.com/bevyengine/bevy/assets/26703856/14db39b7-d9e0-4bc3-ba0e-b1f2db39ae8f"> # Solution - `SpriteBundlue` now has a `scale_mode` component storing a `SpriteScaleMode` enum with three variants: - `Stretched` (default) - `Tiled` to have sprites tile horizontally and/or vertically - `Sliced` allowing 9 slicing the texture and optionally tile some sections with a `Textureslicer`. - `bevy_sprite` has two extra systems to compute a `ComputedTextureSlices` if necessary,: - One system react to changes on `Sprite`, `Handle<Image>` or `SpriteScaleMode` - The other listens to `AssetEvent<Image>` to compute slices on sprites when the texture is ready or changed - I updated the `bevy_sprite` extraction stage to extract potentially multiple textures instead of one, depending on the presence of `ComputedTextureSlices` - I added two examples showcasing the slicing and tiling feature. The addition of `ComputedTextureSlices` as a cache is to avoid querying the image data, to retrieve its dimensions, every frame in a extract or prepare stage. Also it reacts to changes so we can have stuff like this (tiling example): https://github.com/bevyengine/bevy/assets/26703856/a349a9f3-33c3-471f-8ef4-a0e5dfce3b01 # Related - [ ] Once #5103 or #10099 is merged I can enable tiling and slicing for texture sheets as ui # To discuss There is an other option, to consider slice/tiling as part of the asset, using the new asset preprocessing but I have no clue on how to do it. Also, instead of retrieving the Image dimensions, we could use the same system as the sprite sheet and have the user give the image dimensions directly (grid). But I think it's less user friendly --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: ickshonpe <david.curthoys@googlemail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-15 15:40:06 +00:00
[Sprite Slice](../examples/2d/sprite_slice.rs) | Showcases slicing sprites into sections that can be scaled independently via the 9-patch technique
[Sprite Tile](../examples/2d/sprite_tile.rs) | Renders a sprite tiled in a grid
[Text 2D](../examples/2d/text2d.rs) | Generates text in 2D
[Texture Atlas](../examples/2d/texture_atlas.rs) | Generates a texture atlas (sprite sheet) from individual sprites
[Transparency in 2D](../examples/2d/transparency_2d.rs) | Demonstrates transparency in 2d
2020-08-17 23:02:59 +00:00
## 3D Rendering
Example | Description
--- | ---
Revamp Bloom (#6677) ![image](https://user-images.githubusercontent.com/47158642/220197588-25e11022-02e4-45f3-b2e5-392c4ce7a025.png) Huge credit to @StarLederer, who did almost all of the work on this. We're just reusing this PR to keep everything in one place. # Objective 1. Make bloom more physically based. 1. Improve artistic control. 1. Allow to use bloom as screen blur. 1. Fix #6634. 1. Address #6655 (although the author makes incorrect conclusions). ## Solution 1. Set the default threshold to 0. 2. Lerp between bloom textures when `composite_mode: BloomCompositeMode::EnergyConserving`. 1. Use [a parametric function](https://starlederer.github.io/bloom) to control blend levels for each bloom texture. In the future this can be controlled per-pixel for things like lens dirt. 3. Implement BloomCompositeMode::Additive` for situations where the old school look is desired. ## Changelog * Bloom now looks different. * Added `BloomSettings:lf_boost`, `BloomSettings:lf_boost_curvature`, `BloomSettings::high_pass_frequency` and `BloomSettings::composite_mode`. * `BloomSettings::scale` removed. * `BloomSettings::knee` renamed to `BloomPrefilterSettings::softness`. * `BloomSettings::threshold` renamed to `BloomPrefilterSettings::threshold`. * The bloom example has been renamed to bloom_3d and improved. A bloom_2d example was added. ## Migration Guide * Refactor mentions of `BloomSettings::knee` and `BloomSettings::threshold` as `BloomSettings::prefilter_settings` where knee is now `softness`. * If defined without `..default()` add `..default()` to definitions of `BloomSettings` instances or manually define missing fields. * Adapt to Bloom looking visually different (if needed). Co-authored-by: Herman Lederer <germans.lederers@gmail.com>
2023-03-04 12:05:26 +00:00
[3D Bloom](../examples/3d/bloom_3d.rs) | Illustrates bloom configuration using HDR and emissive materials
[3D Scene](../examples/3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting
[3D Shapes](../examples/3d/3d_shapes.rs) | A scene showcasing the built-in 3D shapes
[3D Viewport To World](../examples/3d/3d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world` method
[Animated Material](../examples/3d/animated_material.rs) | Shows how to animate material properties
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: ![Screenshot 2024-05-20 233414](https://github.com/bevyengine/bevy/assets/157897/379f1e42-24e9-40b6-a430-f7d1479d0335) Without anisotropy: ![Screenshot 2024-05-20 233420](https://github.com/bevyengine/bevy/assets/157897/aa220f05-b8e7-417c-9671-b242d4bf9fc4)
2024-06-03 23:46:06 +00:00
[Anisotropy](../examples/3d/anisotropy.rs) | Displays an example model with anisotropy
Temporal Antialiasing (TAA) (#7291) ![image](https://user-images.githubusercontent.com/47158642/214374911-412f0986-3927-4f7a-9a6c-413bdee6b389.png) # Objective - Implement an alternative antialias technique - TAA scales based off of view resolution, not geometry complexity - TAA filters textures, firefly pixels, and other aliasing not covered by MSAA - TAA additionally will reduce noise / increase quality in future stochastic rendering techniques - Closes https://github.com/bevyengine/bevy/issues/3663 ## Solution - Add a temporal jitter component - Add a motion vector prepass - Add a TemporalAntialias component and plugin - Combine existing MSAA and FXAA examples and add TAA ## Followup Work - Prepass motion vector support for skinned meshes - Move uniforms needed for motion vectors into a separate bind group, instead of using different bind group layouts - Reuse previous frame's GPU view buffer for motion vectors, instead of recomputing - Mip biasing for sharper textures, and or unjitter texture UVs https://github.com/bevyengine/bevy/issues/7323 - Compute shader for better performance - Investigate FSR techniques - Historical depth based disocclusion tests, for geometry disocclusion - Historical luminance/hue based tests, for shading disocclusion - Pixel "locks" to reduce blending rate / revamp history confidence mechanism - Orthographic camera support for TemporalJitter - Figure out COD's 1-tap bicubic filter --- ## Changelog - Added MotionVectorPrepass and TemporalJitter - Added TemporalAntialiasPlugin, TemporalAntialiasBundle, and TemporalAntialiasSettings --------- Co-authored-by: IceSentry <c.giguere42@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: Daniel Chia <danstryder@gmail.com> Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> Co-authored-by: Brandon Dyer <brandondyer64@gmail.com> Co-authored-by: Edgar Geier <geieredgar@gmail.com>
2023-03-27 22:22:40 +00:00
[Anti-aliasing](../examples/3d/anti_aliasing.rs) | Compares different anti-aliasing methods
Add Distance and Atmospheric Fog support (#6412) <img width="1392" alt="image" src="https://user-images.githubusercontent.com/418473/203873533-44c029af-13b7-4740-8ea3-af96bd5867c9.png"> <img width="1392" alt="image" src="https://user-images.githubusercontent.com/418473/203873549-36be7a23-b341-42a2-8a9f-ceea8ac7a2b8.png"> # Objective - Add support for the “classic” distance fog effect, as well as a more advanced atmospheric fog effect. ## Solution This PR: - Introduces a new `FogSettings` component that controls distance fog per-camera. - Adds support for three widely used “traditional” fog falloff modes: `Linear`, `Exponential` and `ExponentialSquared`, as well as a more advanced `Atmospheric` fog; - Adds support for directional light influence over fog color; - Extracts fog via `ExtractComponent`, then uses a prepare system that sets up a new dynamic uniform struct (`Fog`), similar to other mesh view types; - Renders fog in PBR material shader, as a final adjustment to the `output_color`, after PBR is computed (but before tone mapping); - Adds a new `StandardMaterial` flag to enable fog; (`fog_enabled`) - Adds convenience methods for easier artistic control when creating non-linear fog types; - Adds documentation around fog. --- ## Changelog ### Added - Added support for distance-based fog effects for PBR materials, controllable per-camera via the new `FogSettings` component; - Added `FogFalloff` enum for selecting between three widely used “traditional” fog falloff modes: `Linear`, `Exponential` and `ExponentialSquared`, as well as a more advanced `Atmospheric` fog;
2023-01-29 15:28:56 +00:00
[Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
[Auto Exposure](../examples/3d/auto_exposure.rs) | A scene showcasing auto exposure
Standard Material Blend Modes (#6644) # Objective - This PR adds support for blend modes to the PBR `StandardMaterial`. <img width="1392" alt="Screenshot 2022-11-18 at 20 00 56" src="https://user-images.githubusercontent.com/418473/202820627-0636219a-a1e5-437a-b08b-b08c6856bf9c.png"> <img width="1392" alt="Screenshot 2022-11-18 at 20 01 01" src="https://user-images.githubusercontent.com/418473/202820615-c8d43301-9a57-49c4-bd21-4ae343c3e9ec.png"> ## Solution - The existing `AlphaMode` enum is extended, adding three more modes: `AlphaMode::Premultiplied`, `AlphaMode::Add` and `AlphaMode::Multiply`; - All new modes are rendered in the existing `Transparent3d` phase; - The existing mesh flags for alpha mode are reorganized for a more compact/efficient representation, and new values are added; - `MeshPipelineKey::TRANSPARENT_MAIN_PASS` is refactored into `MeshPipelineKey::BLEND_BITS`. - `AlphaMode::Opaque` and `AlphaMode::Mask(f32)` share a single opaque pipeline key: `MeshPipelineKey::BLEND_OPAQUE`; - `Blend`, `Premultiplied` and `Add` share a single premultiplied alpha pipeline key, `MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA`. In the shader, color values are premultiplied accordingly (or not) depending on the blend mode to produce the three different results after PBR/tone mapping/dithering; - `Multiply` uses its own independent pipeline key, `MeshPipelineKey::BLEND_MULTIPLY`; - Example and documentation are provided. --- ## Changelog ### Added - Added support for additive and multiplicative blend modes in the PBR `StandardMaterial`, via `AlphaMode::Add` and `AlphaMode::Multiply`; - Added support for premultiplied alpha in the PBR `StandardMaterial`, via `AlphaMode::Premultiplied`;
2023-01-21 21:46:53 +00:00
[Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes
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 ![Screenshot 2024-06-04 180304](https://github.com/bevyengine/bevy/assets/157897/3631c64f-a615-44fe-91ca-7f04df0a54b2) ![Screenshot 2024-06-04 180743](https://github.com/bevyengine/bevy/assets/157897/ee055cbf-4314-49c5-8bfa-8d8a17bd52bb) ## Changelog ### Added * Chromatic aberration is now available as a built-in postprocessing effect. To use it, add `ChromaticAberration` to your camera.
2024-07-15 13:59:02 +00:00
[Built-in postprocessing](../examples/3d/post_processing.rs) | Demonstrates the built-in postprocessing features
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 ![image](https://github.com/user-attachments/assets/75ac45fc-d75d-4664-8ef6-ff7865297c25) - Left Half: Perspective Projection - Right Half: Orthographic Projection - Small boxes in order: - Sub view of the left half of the full image - Sub view moving from the top left to the bottom right of the full image - Sub view of the full image (acting as a control) - Large box: No sub view <details> <summary>Shortened camera setup of `camera_sub_view` example</summary> ```rust // Main perspective Camera commands.spawn(Camera3dBundle { transform, ..default() }); // Perspective camera left half commands.spawn(Camera3dBundle { camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view camera to the left half of the full image full_size: uvec2(500, 500), offset: ivec2(0, 0), size: uvec2(250, 500), }), order: 1, ..default() }, transform, ..default() }); // Perspective camera moving commands.spawn(( Camera3dBundle { camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view camera to a fifth of the full view and // move it in another system full_size: uvec2(500, 500), offset: ivec2(0, 0), size: uvec2(100, 100), }), order: 2, ..default() }, transform, ..default() }, MovingCameraMarker, )); // Perspective camera control commands.spawn(Camera3dBundle { camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view to the full image, to ensure that it matches // the projection without sub view full_size: uvec2(450, 450), offset: ivec2(0, 0), size: uvec2(450, 450), }), order: 3, ..default() }, transform, ..default() }); // Main orthographic camera commands.spawn(Camera3dBundle { projection: OrthographicProjection { ... } .into(), camera: Camera { order: 4, ..default() }, transform, ..default() }); // Orthographic camera left half commands.spawn(Camera3dBundle { projection: OrthographicProjection { ... } .into(), camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view camera to the left half of the full image full_size: uvec2(500, 500), offset: ivec2(0, 0), size: uvec2(250, 500), }), order: 5, ..default() }, transform, ..default() }); // Orthographic camera moving commands.spawn(( Camera3dBundle { projection: OrthographicProjection { ... } .into(), camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view camera to a fifth of the full view and // move it in another system full_size: uvec2(500, 500), offset: ivec2(0, 0), size: uvec2(100, 100), }), order: 6, ..default() }, transform, ..default() }, MovingCameraMarker, )); // Orthographic camera control commands.spawn(Camera3dBundle { projection: OrthographicProjection { ... } .into(), camera: Camera { sub_camera_view: Some(SubCameraView { // Set the sub view to the full image, to ensure that it matches // the projection without sub view full_size: uvec2(450, 450), offset: ivec2(0, 0), size: uvec2(450, 450), }), order: 7, ..default() }, transform, ..default() }); ``` </details>
2024-10-01 14:11:24 +00:00
[Camera sub view](../examples/3d/camera_sub_view.rs) | Demonstrates using different sub view effects on a camera
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 ![Screenshot 2024-04-19 101143](https://github.com/bevyengine/bevy/assets/157897/3444bcb5-5c20-490c-b0ad-53759bd47ae2) ![Screenshot 2024-04-19 102054](https://github.com/bevyengine/bevy/assets/157897/6e953944-75b8-49ef-bc71-97b0a53b3a27) ## 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.
2024-05-05 22:57:05 +00:00
[Clearcoat](../examples/3d/clearcoat.rs) | Demonstrates the clearcoat PBR feature
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 ![Screenshot 2024-04-27 143144](https://github.com/bevyengine/bevy/assets/157897/c1de5894-917d-4101-b5c9-e644d141a941) ![Screenshot 2024-04-27 143216](https://github.com/bevyengine/bevy/assets/157897/da393c8a-d747-42f5-b47c-6465044c788d)
2024-05-02 12:18:59 +00:00
[Color grading](../examples/3d/color_grading.rs) | Demonstrates color grading
Deferred Renderer (#9258) # Objective - Add a [Deferred Renderer](https://en.wikipedia.org/wiki/Deferred_shading) to Bevy. - This allows subsequent passes to access per pixel material information before/during shading. - Accessing this per pixel material information is needed for some features, like GI. It also makes other features (ex. Decals) simpler to implement and/or improves their capability. There are multiple approaches to accomplishing this. The deferred shading approach works well given the limitations of WebGPU and WebGL2. Motivation: [I'm working on a GI solution for Bevy](https://youtu.be/eH1AkL-mwhI) # Solution - The deferred renderer is implemented with a prepass and a deferred lighting pass. - The prepass renders opaque objects into the Gbuffer attachment (`Rgba32Uint`). The PBR shader generates a `PbrInput` in mostly the same way as the forward implementation and then [packs it into the Gbuffer](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/render/pbr.wgsl#L168). - The deferred lighting pass unpacks the `PbrInput` and [feeds it into the pbr() function](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl#L65), then outputs the shaded color data. - There is now a resource [DefaultOpaqueRendererMethod](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/material.rs#L599) that can be used to set the default render method for opaque materials. If materials return `None` from [opaque_render_method()](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/material.rs#L131) the `DefaultOpaqueRendererMethod` will be used. Otherwise, custom materials can also explicitly choose to only support Deferred or Forward by returning the respective [OpaqueRendererMethod](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/material.rs#L603) - Deferred materials can be used seamlessly along with both opaque and transparent forward rendered materials in the same scene. The [deferred rendering example](https://github.com/DGriffin91/bevy/blob/deferred/examples/3d/deferred_rendering.rs) does this. - The deferred renderer does not support MSAA. If any deferred materials are used, MSAA must be disabled. Both TAA and FXAA are supported. - Deferred rendering supports WebGL2/WebGPU. ## Custom deferred materials - Custom materials can support both deferred and forward at the same time. The [StandardMaterial](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/render/pbr.wgsl#L166) does this. So does [this example](https://github.com/DGriffin91/bevy_glowy_orb_tutorial/blob/deferred/assets/shaders/glowy.wgsl#L56). - Custom deferred materials that require PBR lighting can create a `PbrInput`, write it to the deferred GBuffer and let it be rendered by the `PBRDeferredLightingPlugin`. - Custom deferred materials that require custom lighting have two options: 1. Use the base_color channel of the `PbrInput` combined with the `STANDARD_MATERIAL_FLAGS_UNLIT_BIT` flag. [Example.](https://github.com/DGriffin91/bevy_glowy_orb_tutorial/blob/deferred/assets/shaders/glowy.wgsl#L56) (If the unlit bit is set, the base_color is stored as RGB9E5 for extra precision) 2. A Custom Deferred Lighting pass can be created, either overriding the default, or running in addition. The a depth buffer is used to limit rendering to only the required fragments for each deferred lighting pass. Materials can set their respective depth id via the [deferred_lighting_pass_id](https://github.com/DGriffin91/bevy/blob/b79182d2a32cac28c4213c2457a53ac2cc885332/crates/bevy_pbr/src/prepass/prepass_io.wgsl#L95) attachment. The custom deferred lighting pass plugin can then set [its corresponding depth](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/deferred/deferred_lighting.wgsl#L37). Then with the lighting pass using [CompareFunction::Equal](https://github.com/DGriffin91/bevy/blob/ec1465559f2c82001830e908fc02ff1d7c2efe51/crates/bevy_pbr/src/deferred/mod.rs#L335), only the fragments with a depth that equal the corresponding depth written in the material will be rendered. Custom deferred lighting plugins can also be created to render the StandardMaterial. The default deferred lighting plugin can be bypassed with `DefaultPlugins.set(PBRDeferredLightingPlugin { bypass: true })` --------- Co-authored-by: nickrart <nickolas.g.russell@gmail.com>
2023-10-12 22:10:38 +00:00
[Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines
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: ![Screenshot 2024-04-17 152535](https://github.com/bevyengine/bevy/assets/157897/702f0008-1c8a-4cf3-b077-4110f8c46584) Gaussian depth of field: ![Screenshot 2024-04-17 152542](https://github.com/bevyengine/bevy/assets/157897/f4ece47a-520e-4483-a92d-f4fa760795d3) No depth of field: ![Screenshot 2024-04-17 152547](https://github.com/bevyengine/bevy/assets/157897/9444e6aa-fcae-446c-b66b-89469f1a1325) [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>
2024-05-13 18:23:56 +00:00
[Depth of field](../examples/3d/depth_of_field.rs) | Demonstrates depth of field
Add Distance and Atmospheric Fog support (#6412) <img width="1392" alt="image" src="https://user-images.githubusercontent.com/418473/203873533-44c029af-13b7-4740-8ea3-af96bd5867c9.png"> <img width="1392" alt="image" src="https://user-images.githubusercontent.com/418473/203873549-36be7a23-b341-42a2-8a9f-ceea8ac7a2b8.png"> # Objective - Add support for the “classic” distance fog effect, as well as a more advanced atmospheric fog effect. ## Solution This PR: - Introduces a new `FogSettings` component that controls distance fog per-camera. - Adds support for three widely used “traditional” fog falloff modes: `Linear`, `Exponential` and `ExponentialSquared`, as well as a more advanced `Atmospheric` fog; - Adds support for directional light influence over fog color; - Extracts fog via `ExtractComponent`, then uses a prepare system that sets up a new dynamic uniform struct (`Fog`), similar to other mesh view types; - Renders fog in PBR material shader, as a final adjustment to the `output_color`, after PBR is computed (but before tone mapping); - Adds a new `StandardMaterial` flag to enable fog; (`fog_enabled`) - Adds convenience methods for easier artistic control when creating non-linear fog types; - Adds documentation around fog. --- ## Changelog ### Added - Added support for distance-based fog effects for PBR materials, controllable per-camera via the new `FogSettings` component; - Added `FogFalloff` enum for selecting between three widely used “traditional” fog falloff modes: `Linear`, `Exponential` and `ExponentialSquared`, as well as a more advanced `Atmospheric` fog;
2023-01-29 15:28:56 +00:00
[Fog](../examples/3d/fog.rs) | A scene showcasing the distance fog effect
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>
2024-07-16 03:14:12 +00:00
[Fog volumes](../examples/3d/fog_volumes.rs) | Demonstrates fog volumes
[Generate Custom Mesh](../examples/3d/generate_custom_mesh.rs) | Simple showcase of how to generate a custom mesh with a custom texture
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.
2024-02-06 23:23:20 +00:00
[Irradiance Volumes](../examples/3d/irradiance_volumes.rs) | Demonstrates irradiance volumes
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
[Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene
[Load glTF extras](../examples/3d/load_gltf_extras.rs) | Loads and renders a glTF file as a scene, including the gltf extras
Add mesh picking backend and `MeshRayCast` system parameter (#15800) # Objective Closes #15545. `bevy_picking` supports UI and sprite picking, but not mesh picking. Being able to pick meshes would be extremely useful for various games, tools, and our own examples, as well as scene editors and inspectors. So, we need a mesh picking backend! Luckily, [`bevy_mod_picking`](https://github.com/aevyrie/bevy_mod_picking) (which `bevy_picking` is based on) by @aevyrie already has a [backend for it](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) using [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast). As a side product of adding mesh picking, we also get support for performing ray casts on meshes! ## Solution Upstream a large chunk of the immediate-mode ray casting functionality from `bevy_mod_raycast`, and add a mesh picking backend based on `bevy_mod_picking`. Huge thanks to @aevyrie who did all the hard work on these incredible crates! All meshes are pickable by default. Picking can be disabled for individual entities by adding `PickingBehavior::IGNORE`, like normal. Or, if you want mesh picking to be entirely opt-in, you can set `MeshPickingBackendSettings::require_markers` to `true` and add a `RayCastPickable` component to the desired camera and target entities. You can also use the new `MeshRayCast` system parameter to cast rays into the world manually: ```rust fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); // Only ray cast against entities with the `Foo` component. let filter = |entity| foo_query.contains(entity); // Never early-exit. Note that you can change behavior per-entity. let early_exit_test = |_entity| false; // Ignore the visibility of entities. This allows ray casting hidden entities. let visibility = RayCastVisibility::Any; let settings = RayCastSettings::default() .with_filter(&filter) .with_early_exit_test(&early_exit_test) .with_visibility(visibility); // Cast the ray with the settings, returning a list of intersections. let hits = ray_cast.cast_ray(ray, &settings); } ``` This is largely a direct port, but I did make several changes to match our APIs better, remove things we don't need or that I think are unnecessary, and do some general improvements to code quality and documentation. ### Changes Relative to `bevy_mod_raycast` and `bevy_mod_picking` - Every `Raycast` and "raycast" has been renamed to `RayCast` and "ray cast" (similar reasoning as the "Naming" section in #15724) - `Raycast` system param has been renamed to `MeshRayCast` to avoid naming conflicts and to be explicit that it is not for colliders - `RaycastBackend` has been renamed to `MeshPickingBackend` - `RayCastVisibility` variants are now `Any`, `Visible`, and `VisibleInView` instead of `Ignore`, `MustBeVisible`, and `MustBeVisibleAndInView` - `NoBackfaceCulling` has been renamed to `RayCastBackfaces`, to avoid implying that it affects the rendering of backfaces for meshes (it doesn't) - `SimplifiedMesh` and `RayCastBackfaces` live near other ray casting API types, not in their own 10 LoC module - All intersection logic and types are in the same `intersections` module, not split across several modules - Some intersection types have been renamed to be clearer and more consistent - `IntersectionData` -> `RayMeshHit` - `RayHit` -> `RayTriangleHit` - General documentation and code quality improvements ### Removed / Not Ported - Removed unused ray helpers and types, like `PrimitiveIntersection` - Removed getters on intersection types, and made their properties public - There is no `2d` feature, and `Raycast::mesh_query` and `Raycast::mesh2d_query` have been merged into `MeshRayCast::mesh_query`, which handles both 2D and 3D - I assume this existed previously because `Mesh2dHandle` used to be in `bevy_sprite`. Now both the 2D and 3D mesh are in `bevy_render`. - There is no `debug` feature or ray debug rendering - There is no deferred API (`RaycastSource`) - There is no `CursorRayPlugin` (the picking backend handles this) ### Note for Reviewers In case it's helpful, the [first commit](https://github.com/bevyengine/bevy/commit/281638ef10ebd25f783bee2533509fdf756f7ab4) here is essentially a one-to-one port. The rest of the commits are primarily refactoring and cleaning things up in the ways listed earlier, as well as changes to the module structure. It may also be useful to compare the original [picking backend](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) and [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast) to this PR. Feel free to mention if there are any changes that I should revert or something I should not include in this PR. ## Testing I tested mesh picking and relevant components in some examples, for both 2D and 3D meshes, and added a new `mesh_picking` example. I also ~~stole~~ ported over the [ray-mesh intersection benchmark](https://github.com/aevyrie/bevy_mod_raycast/blob/dbc5ef32fe48997a1a7eeec7434d9dd8b829e52e/benches/ray_mesh_intersection.rs) from `bevy_mod_raycast`. --- ## Showcase Below is a version of the `2d_shapes` example modified to demonstrate 2D mesh picking. This is not included in this PR. https://github.com/user-attachments/assets/7742528c-8630-4c00-bacd-81576ac432bf And below is the new `mesh_picking` example: https://github.com/user-attachments/assets/b65c7a5a-fa3a-4c2d-8bbd-e7a2c772986e There is also a really cool new `mesh_ray_cast` example ported over from `bevy_mod_raycast`: https://github.com/user-attachments/assets/3c5eb6c0-bd94-4fb0-bec6-8a85668a06c9 --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Trent <2771466+tbillington@users.noreply.github.com> Co-authored-by: François Mockers <mockersf@gmail.com>
2024-10-13 17:24:19 +00:00
[Mesh Ray Cast](../examples/3d/mesh_ray_cast.rs) | Demonstrates ray casting with the `MeshRayCast` system parameter
[Meshlet](../examples/3d/meshlet.rs) | Meshlet rendering for dense high-poly scenes (experimental)
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>
2024-04-25 01:16:02 +00:00
[Motion Blur](../examples/3d/motion_blur.rs) | Demonstrates per-pixel motion blur
[Order Independent Transparency](../examples/3d/order_independent_transparency.rs) | Demonstrates how to use OIT
[Orthographic View](../examples/3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look in games or CAD applications)
Add parallax mapping to bevy PBR (#5928) # Objective Add a [parallax mapping] shader to bevy. Please note that this is a 3d technique, NOT a 2d sidescroller feature. ## Solution - Add related fields to `StandardMaterial` - update the pbr shader - Add an example taking advantage of parallax mapping A pre-existing implementation exists at: https://github.com/nicopap/bevy_mod_paramap/ The implementation is derived from: https://web.archive.org/web/20150419215321/http://sunandblackcat.com/tipFullView.php?l=eng&topicid=28 Further discussion on literature is found in the `bevy_mod_paramap` README. ### Limitations - The mesh silhouette isn't affected by the depth map. - The depth of the pixel does not reflect its visual position, resulting in artifacts for depth-dependent features such as fog or SSAO - GLTF does not define a height map texture, so somehow the user will always need to work around this limitation, though [an extension is in the works][gltf] ### Future work - It's possible to update the depth in the depth buffer to follow the parallaxed texture. This would enable interop with depth-based visual effects, it also allows `discard`ing pixels of materials when computed depth is higher than the one in depth buffer - Cheap lower quality single-sample method using [offset limiting] - Add distance fading, to disable parallaxing (relatively expensive) on distant objects - GLTF extension to allow defining height maps. Or a workaround implemented through a blender plugin to the GLTF exporter that uses the `extras` field to add height map. - [Quadratic surface vertex attributes][oliveira_3] to enable parallax mapping on bending surfaces and allow clean silhouetting. - noise based sampling, to limit the pancake artifacts. - Cone mapping ([GPU gems], [Simcity (2013)][simcity]). Requires preprocessing, increase depth map size, reduces sample count greatly. - [Quadtree parallax mapping][qpm] (also requires preprocessing) - Self-shadowing of parallax-mapped surfaces by modifying the shadow map - Generate depth map from normal map [link to slides], [blender question] https://user-images.githubusercontent.com/26321040/223563792-dffcc6ab-70e8-4ff9-90d1-b36c338695ad.mp4 [blender question]: https://blender.stackexchange.com/questions/89278/how-to-get-a-smooth-curvature-map-from-a-normal-map [link to slides]: https://developer.download.nvidia.com/assets/gamedev/docs/nmap2displacement.pdf [oliveira_3]: https://www.inf.ufrgs.br/~oliveira/pubs_files/Oliveira_Policarpo_RP-351_Jan_2005.pdf [GPU gems]: https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-18-relaxed-cone-stepping-relief-mapping [simcity]: https://community.simtropolis.com/omnibus/other-games/building-and-rendering-simcity-2013-r247/ [offset limiting]: https://raw.githubusercontent.com/marcusstenbeck/tncg14-parallax-mapping/master/documents/Parallax%20Mapping%20with%20Offset%20Limiting%20-%20A%20Per-Pixel%20Approximation%20of%20Uneven%20Surfaces.pdf [gltf]: https://github.com/KhronosGroup/glTF/pull/2196 [qpm]: https://www.gamedevs.org/uploads/quadtree-displacement-mapping-with-height-blending.pdf --- ## Changelog - Add a `depth_map` field to the `StandardMaterial`, it is a grayscale image where white represents bottom and black the top. If `depth_map` is set, bevy's pbr shader will use it to do [parallax mapping] to give an increased feel of depth to the material. This is similar to a displacement map, but with infinite precision at fairly low cost. - The fields `parallax_mapping_method`, `parallax_depth_scale` and `max_parallax_layer_count` allow finer grained control over the behavior of the parallax shader. - Add the `parallax_mapping` example to show off the effect. [parallax mapping]: https://en.wikipedia.org/wiki/Parallax_mapping --------- Co-authored-by: Robert Swain <robert.swain@gmail.com>
2023-04-15 10:25:14 +00:00
[Parallax Mapping](../examples/3d/parallax_mapping.rs) | Demonstrates use of a normal map and depth map for parallax mapping
[Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
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: ![Screenshot 2024-05-24 120012](https://github.com/bevyengine/bevy/assets/157897/0d35fe98-245b-44fb-8a43-8d0272a73b86) PCSS on: ![Screenshot 2024-05-24 115959](https://github.com/bevyengine/bevy/assets/157897/83397ef8-1317-49dd-bfb3-f8286d7610cd) --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
2024-09-18 18:07:17 +00:00
[Percentage-closer soft shadows](../examples/3d/pcss.rs) | Demonstrates percentage-closer soft shadows (PCSS)
[Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
[Query glTF primitives](../examples/3d/query_gltf_primitives.rs) | Query primitives in a glTF scene
Implement minimal reflection probes (fixed macOS, iOS, and Android). (#11366) This pull request re-submits #10057, which was backed out for breaking macOS, iOS, and Android. I've tested this version on macOS and Android and on the iOS simulator. # Objective This pull request implements *reflection probes*, which generalize environment maps to allow for multiple environment maps in the same scene, each of which has an axis-aligned bounding box. This is a standard feature of physically-based renderers and was inspired by [the corresponding feature in Blender's Eevee renderer]. ## Solution This is a minimal implementation of reflection probes that allows artists to define cuboid bounding regions associated with environment maps. For every view, on every frame, a system builds up a list of the nearest 4 reflection probes that are within the view's frustum and supplies that list to the shader. The PBR fragment shader searches through the list, finds the first containing reflection probe, and uses it for indirect lighting, falling back to the view's environment map if none is found. Both forward and deferred renderers are fully supported. A reflection probe is an entity with a pair of components, *LightProbe* and *EnvironmentMapLight* (as well as the standard *SpatialBundle*, to position it in the world). The *LightProbe* component (along with the *Transform*) defines the bounding region, while the *EnvironmentMapLight* component specifies the associated diffuse and specular cubemaps. A frequent question is "why two components instead of just one?" The advantages of this setup are: 1. It's readily extensible to other types of light probes, in particular *irradiance volumes* (also known as ambient cubes or voxel global illumination), which use the same approach of bounding cuboids. With a single component that applies to both reflection probes and irradiance volumes, we can share the logic that implements falloff and blending between multiple light probes between both of those features. 2. It reduces duplication between the existing *EnvironmentMapLight* and these new reflection probes. Systems can treat environment maps attached to cameras the same way they treat environment maps applied to reflection probes if they wish. Internally, we gather up all environment maps in the scene and place them in a cubemap array. At present, this means that all environment maps must have the same size, mipmap count, and texture format. A warning is emitted if this restriction is violated. We could potentially relax this in the future as part of the automatic mipmap generation work, which could easily do texture format conversion as part of its preprocessing. An easy way to generate reflection probe cubemaps is to bake them in Blender and use the `export-blender-gi` tool that's part of the [`bevy-baked-gi`] project. This tool takes a `.blend` file containing baked cubemaps as input and exports cubemap images, pre-filtered with an embedded fork of the [glTF IBL Sampler], alongside a corresponding `.scn.ron` file that the scene spawner can use to recreate the reflection probes. Note that this is intentionally a minimal implementation, to aid reviewability. Known issues are: * Reflection probes are basically unsupported on WebGL 2, because WebGL 2 has no cubemap arrays. (Strictly speaking, you can have precisely one reflection probe in the scene if you have no other cubemaps anywhere, but this isn't very useful.) * Reflection probes have no falloff, so reflections will abruptly change when objects move from one bounding region to another. * As mentioned before, all cubemaps in the world of a given type (diffuse or specular) must have the same size, format, and mipmap count. Future work includes: * Blending between multiple reflection probes. * A falloff/fade-out region so that reflected objects disappear gradually instead of vanishing all at once. * Irradiance volumes for voxel-based global illumination. This should reuse much of the reflection probe logic, as they're both GI techniques based on cuboid bounding regions. * Support for WebGL 2, by breaking batches when reflection probes are used. These issues notwithstanding, I think it's best to land this with roughly the current set of functionality, because this patch is useful as is and adding everything above would make the pull request significantly larger and harder to review. --- ## Changelog ### Added * A new *LightProbe* component is available that specifies a bounding region that an *EnvironmentMapLight* applies to. The combination of a *LightProbe* and an *EnvironmentMapLight* offers *reflection probe* functionality similar to that available in other engines. [the corresponding feature in Blender's Eevee renderer]: https://docs.blender.org/manual/en/latest/render/eevee/light_probes/reflection_cubemaps.html [`bevy-baked-gi`]: https://github.com/pcwalton/bevy-baked-gi [glTF IBL Sampler]: https://github.com/KhronosGroup/glTF-IBL-Sampler
2024-01-19 07:33:52 +00:00
[Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes
[Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images
[Rotate Environment Map](../examples/3d/rotate_environment_map.rs) | Demonstrates how to rotate the skybox and the environment map simultaneously
Screen Space Ambient Occlusion (SSAO) MVP (#7402) ![image](https://github.com/bevyengine/bevy/assets/47158642/dbb62645-f639-4f2b-b84b-26fd915c186d) # Objective - Add Screen space ambient occlusion (SSAO). SSAO approximates small-scale, local occlusion of _indirect_ diffuse light between objects. SSAO does not apply to direct lighting, such as point or directional lights. - This darkens creases, e.g. on staircases, and gives nice contact shadows where objects meet, giving entities a more "grounded" feel. - Closes https://github.com/bevyengine/bevy/issues/3632. ## Solution - Implement the GTAO algorithm. - https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf - https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf - Source code heavily based on [Intel's XeGTAO](https://github.com/GameTechDev/XeGTAO/blob/0d177ce06bfa642f64d8af4de1197ad1bcb862d4/Source/Rendering/Shaders/XeGTAO.hlsli). - Add an SSAO bevy example. ## Algorithm Overview * Run a depth and normal prepass * Create downscaled mips of the depth texture (preprocess_depths pass) * GTAO pass - for each pixel, take several random samples from the depth+normal buffers, reconstruct world position, raytrace in screen space to estimate occlusion. Rather then doing completely random samples on a hemisphere, you choose random _slices_ of the hemisphere, and then can analytically compute the full occlusion of that slice. Also compute edges based on depth differences here. * Spatial denoise pass - bilateral blur, using edge detection to not blur over edges. This is the final SSAO result. * Main pass - if SSAO exists, sample the SSAO texture, and set occlusion to be the minimum of ssao/material occlusion. This then feeds into the rest of the PBR shader as normal. --- ## Future Improvements - Maybe remove the low quality preset for now (too noisy) - WebGPU fallback (see below) - Faster depth->world position (see reverted code) - Bent normals - Try interleaved gradient noise or spatiotemporal blue noise - Replace the spatial denoiser with a combined spatial+temporal denoiser - Render at half resolution and use a bilateral upsample - Better multibounce approximation (https://drive.google.com/file/d/1SyagcEVplIm2KkRD3WQYSO9O0Iyi1hfy/view) ## Far-Future Performance Improvements - F16 math (missing naga-wgsl support https://github.com/gfx-rs/naga/issues/1884) - Faster coordinate space conversion for normals - Faster depth mipchain creation (https://github.com/GPUOpen-Effects/FidelityFX-SPD) (wgpu/naga does not currently support subgroup ops) - Deinterleaved SSAO for better cache efficiency (https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf) ## Other Interesting Papers - Visibility bitmask (https://link.springer.com/article/10.1007/s00371-022-02703-y, https://cdrinmatane.github.io/posts/cgspotlight-slides/) - Screen space diffuse lighting (https://github.com/Patapom/GodComplex/blob/master/Tests/TestHBIL/2018%20Mayaux%20-%20Horizon-Based%20Indirect%20Lighting%20(HBIL).pdf) ## Platform Support * SSAO currently does not work on DirectX12 due to issues with wgpu and naga: * https://github.com/gfx-rs/wgpu/pull/3798 * https://github.com/gfx-rs/naga/pull/2353 * SSAO currently does not work on WebGPU because r16float is not a valid storage texture format https://gpuweb.github.io/gpuweb/wgsl/#storage-texel-formats. We can fix this with a fallback to r32float. --- ## Changelog - Added ScreenSpaceAmbientOcclusionSettings, ScreenSpaceAmbientOcclusionQualityLevel, and ScreenSpaceAmbientOcclusionBundle --------- Co-authored-by: IceSentry <c.giguere42@gmail.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> Co-authored-by: Daniel Chia <danstryder@gmail.com> Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com> Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> Co-authored-by: Brandon Dyer <brandondyer64@gmail.com> Co-authored-by: Edgar Geier <geieredgar@gmail.com> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-18 21:05:55 +00:00
[Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion
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. ![Screenshot 2024-05-18 143555](https://github.com/bevyengine/bevy/assets/157897/b8675b39-8a89-433e-a34e-1b9ee1233267) ![Screenshot 2024-05-18 143606](https://github.com/bevyengine/bevy/assets/157897/cc9e1cd0-9951-464a-9a08-e589210e5606)
2024-05-27 13:43:40 +00:00
[Screen Space Reflections](../examples/3d/ssr.rs) | Demonstrates screen space reflections with water ripples
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
2024-08-22 19:43:14 +00:00
[Scrolling fog](../examples/3d/scrolling_fog.rs) | Demonstrates how to create the effect of fog moving in the wind
[Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene
[Shadow Caster and Receiver](../examples/3d/shadow_caster_receiver.rs) | Demonstrates how to prevent meshes from casting/receiving shadows in a 3d scene
Support array / cubemap / cubemap array textures in KTX2 (#5325) # Objective - Fix / support KTX2 array / cubemap / cubemap array textures - Fixes #4495 . Supersedes #4514 . ## Solution - Add `Option<TextureViewDescriptor>` to `Image` to enable configuration of the `TextureViewDimension` of a texture. - This allows users to set `D2Array`, `D3`, `Cube`, `CubeArray` or whatever they need - Automatically configure this when loading KTX2 - Transcode all layers and faces instead of just one - Use the UASTC block size of 128 bits, and the number of blocks in x/y for a given mip level in order to determine the offset of the layer and face within the KTX2 mip level data - `wgpu` wants data ordered as layer 0 mip 0..n, layer 1 mip 0..n, etc. See https://docs.rs/wgpu/latest/wgpu/util/trait.DeviceExt.html#tymethod.create_texture_with_data - Reorder the data KTX2 mip X layer Y face Z to `wgpu` layer Y face Z mip X order - Add a `skybox` example to demonstrate / test loading cubemaps from PNG and KTX2, including ASTC 4x4, BC7, and ETC2 compression for support everywhere. Note that you need to enable the `ktx2,zstd` features to be able to load the compressed textures. --- ## Changelog - Fixed: KTX2 array / cubemap / cubemap array textures - Fixes: Validation failure for compressed textures stored in KTX2 where the width/height are not a multiple of the block dimensions. - Added: `Image` now has an `Option<TextureViewDescriptor>` field to enable configuration of the texture view. This is useful for configuring the `TextureViewDimension` when it is not just a plain 2D texture and the loader could/did not identify what it should be. Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-30 07:02:58 +00:00
[Skybox](../examples/3d/skybox.rs) | Load a cubemap texture onto a cube like a skybox and cycle through different compressed texture formats.
[Spherical Area Lights](../examples/3d/spherical_area_lights.rs) | Demonstrates how point light radius values affect light behavior
[Split Screen](../examples/3d/split_screen.rs) | Demonstrates how to render two cameras to the same window to accomplish "split screen"
Spotlights (#4715) # Objective add spotlight support ## Solution / Changelog - add spotlight angles (inner, outer) to ``PointLight`` struct. emitted light is linearly attenuated from 100% to 0% as angle tends from inner to outer. Direction is taken from the existing transform rotation. - add spotlight direction (vec3) and angles (f32,f32) to ``GpuPointLight`` struct (60 bytes -> 80 bytes) in ``pbr/render/lights.rs`` and ``mesh_view_bind_group.wgsl`` - reduce no-buffer-support max point light count to 204 due to above - use spotlight data to attenuate light in ``pbr.wgsl`` - do additional cluster culling on spotlights to minimise cost in ``assign_lights_to_clusters`` - changed one of the lights in the lighting demo to a spotlight - also added a ``spotlight`` demo - probably not justified but so reviewers can see it more easily ## notes increasing the size of the GpuPointLight struct on my machine reduces the FPS of ``many_lights -- sphere`` from ~150fps to 140fps. i thought this was a reasonable tradeoff, and felt better than handling spotlights separately which is possible but would mean introducing a new bind group, refactoring light-assignment code and adding new spotlight-specific code in pbr.wgsl. the FPS impact for smaller numbers of lights should be very small. the cluster culling strategy reintroduces the cluster aabb code which was recently removed... sorry. the aabb is used to get a cluster bounding sphere, which can then be tested fairly efficiently using the strategy described at the end of https://bartwronski.com/2017/04/13/cull-that-cone/. this works well with roughly cubic clusters (where the cluster z size is close to the same as x/y size), less well for other cases like single Z slice / tiled forward rendering. In the worst case we will end up just keeping the culling of the equivalent point light. Co-authored-by: François <mockersf@gmail.com>
2022-07-08 19:57:43 +00:00
[Spotlight](../examples/3d/spotlight.rs) | Illustrates spot lights
[Texture](../examples/3d/texture.rs) | Shows configuration of texture materials
[Tonemapping](../examples/3d/tonemapping.rs) | Compares tonemapping options
`StandardMaterial` Light Transmission (#8015) # Objective <img width="1920" alt="Screenshot 2023-04-26 at 01 07 34" src="https://user-images.githubusercontent.com/418473/234467578-0f34187b-5863-4ea1-88e9-7a6bb8ce8da3.png"> This PR adds both diffuse and specular light transmission capabilities to the `StandardMaterial`, with support for screen space refractions. This enables realistically representing a wide range of real-world materials, such as: - Glass; (Including frosted glass) - Transparent and translucent plastics; - Various liquids and gels; - Gemstones; - Marble; - Wax; - Paper; - Leaves; - Porcelain. Unlike existing support for transparency, light transmission does not rely on fixed function alpha blending, and therefore works with both `AlphaMode::Opaque` and `AlphaMode::Mask` materials. ## Solution - Introduces a number of transmission related fields in the `StandardMaterial`; - For specular transmission: - Adds logic to take a view main texture snapshot after the opaque phase; (in order to perform screen space refractions) - Introduces a new `Transmissive3d` phase to the renderer, to which all meshes with `transmission > 0.0` materials are sent. - Calculates a light exit point (of the approximate mesh volume) using `ior` and `thickness` properties - Samples the snapshot texture with an adaptive number of taps across a `roughness`-controlled radius enabling “blurry” refractions - For diffuse transmission: - Approximates transmitted diffuse light by using a second, flipped + displaced, diffuse-only Lambertian lobe for each light source. ## To Do - [x] Figure out where `fresnel_mix()` is taking place, if at all, and where `dielectric_specular` is being calculated, if at all, and update them to use the `ior` value (Not a blocker, just a nice-to-have for more correct BSDF) - To the _best of my knowledge, this is now taking place, after 964340cdd. The fresnel mix is actually "split" into two parts in our implementation, one `(1 - fresnel(...))` in the transmission, and `fresnel()` in the light implementations. A surface with more reflectance now will produce slightly dimmer transmission towards the grazing angle, as more of the light gets reflected. - [x] Add `transmission_texture` - [x] Add `diffuse_transmission_texture` - [x] Add `thickness_texture` - [x] Add `attenuation_distance` and `attenuation_color` - [x] Connect values to glTF loader - [x] `transmission` and `transmission_texture` - [x] `thickness` and `thickness_texture` - [x] `ior` - [ ] `diffuse_transmission` and `diffuse_transmission_texture` (needs upstream support in `gltf` crate, not a blocker) - [x] Add support for multiple screen space refraction “steps” - [x] Conditionally create no transmission snapshot texture at all if `steps == 0` - [x] Conditionally enable/disable screen space refraction transmission snapshots - [x] Read from depth pre-pass to prevent refracting pixels in front of the light exit point - [x] Use `interleaved_gradient_noise()` function for sampling blur in a way that benefits from TAA - [x] Drill down a TAA `#define`, tweak some aspects of the effect conditionally based on it - [x] Remove const array that's crashing under HLSL (unless a new `naga` release with https://github.com/gfx-rs/naga/pull/2496 comes out before we merge this) - [ ] Look into alternatives to the `switch` hack for dynamically indexing the const array (might not be needed, compilers seem to be decent at expanding it) - [ ] Add pipeline keys for gating transmission (do we really want/need this?) - [x] Tweak some material field/function names? ## A Note on Texture Packing _This was originally added as a comment to the `specular_transmission_texture`, `thickness_texture` and `diffuse_transmission_texture` documentation, I removed it since it was more confusing than helpful, and will likely be made redundant/will need to be updated once we have a better infrastructure for preprocessing assets_ Due to how channels are mapped, you can more efficiently use a single shared texture image for configuring the following: - R - `specular_transmission_texture` - G - `thickness_texture` - B - _unused_ - A - `diffuse_transmission_texture` The `KHR_materials_diffuse_transmission` glTF extension also defines a `diffuseTransmissionColorTexture`, that _we don't currently support_. One might choose to pack the intensity and color textures together, using RGB for the color and A for the intensity, in which case this packing advice doesn't really apply. --- ## Changelog - Added a new `Transmissive3d` render phase for rendering specular transmissive materials with screen space refractions - Added rendering support for transmitted environment map light on the `StandardMaterial` as a fallback for screen space refractions - Added `diffuse_transmission`, `specular_transmission`, `thickness`, `ior`, `attenuation_distance` and `attenuation_color` to the `StandardMaterial` - Added `diffuse_transmission_texture`, `specular_transmission_texture`, `thickness_texture` to the `StandardMaterial`, gated behind a new `pbr_transmission_textures` cargo feature (off by default, for maximum hardware compatibility) - Added `Camera3d::screen_space_specular_transmission_steps` for controlling the number of “layers of transparency” rendered for transmissive objects - Added a `TransmittedShadowReceiver` component for enabling shadows in (diffusely) transmitted light. (disabled by default, as it requires carefully setting up the `thickness` to avoid self-shadow artifacts) - Added support for the `KHR_materials_transmission`, `KHR_materials_ior` and `KHR_materials_volume` glTF extensions - Renamed items related to temporal jitter for greater consistency ## Migration Guide - `SsaoPipelineKey::temporal_noise` has been renamed to `SsaoPipelineKey::temporal_jitter` - The `TAA` shader def (controlled by the presence of the `TemporalAntiAliasSettings` component in the camera) has been replaced with the `TEMPORAL_JITTER` shader def (controlled by the presence of the `TemporalJitter` component in the camera) - `MeshPipelineKey::TAA` has been replaced by `MeshPipelineKey::TEMPORAL_JITTER` - The `TEMPORAL_NOISE` shader def has been consolidated with `TEMPORAL_JITTER`
2023-10-31 20:59:02 +00:00
[Transmission](../examples/3d/transmission.rs) | Showcases light transmission in the PBR material
[Transparency in 3D](../examples/3d/transparency_3d.rs) | Demonstrates transparency in 3d
[Two Passes](../examples/3d/two_passes.rs) | Renders two 3d passes to the same window from different perspectives
[Update glTF Scene](../examples/3d/update_gltf_scene.rs) | Update a scene from a glTF file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene
[Vertex Colors](../examples/3d/vertex_colors.rs) | Shows the use of vertex colors
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: ![Screenshot 2024-04-09 185541](https://github.com/bevyengine/bevy/assets/157897/7e8be017-7187-4471-8866-974e2d8f2623) Low-poly model up close: ![Screenshot 2024-04-09 185546](https://github.com/bevyengine/bevy/assets/157897/429603fe-6bb7-4246-8b4e-b4888fd1d3a0) Crossfading between the two: ![Screenshot 2024-04-09 185604](https://github.com/bevyengine/bevy/assets/157897/86d0d543-f8f3-49ec-8fe5-caa4d0784fd4) --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-05-03 00:11:35 +00:00
[Visibility range](../examples/3d/visibility_range.rs) | Demonstrates visibility ranges
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. ![Screenshot 2024-04-21 162808](https://github.com/bevyengine/bevy/assets/157897/7a1fc81d-eed5-4735-9419-286c496391a9) ![Screenshot 2024-04-21 132005](https://github.com/bevyengine/bevy/assets/157897/e6d3b5ca-8f59-488d-a3de-15e95aaf4995) [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
2024-05-16 17:13:18 +00:00
[Volumetric fog](../examples/3d/volumetric_fog.rs) | Demonstrates volumetric fog and lighting
[Wireframe](../examples/3d/wireframe.rs) | Showcases wireframe rendering
2020-08-17 23:02:59 +00:00
## Animation
Example | Description
--- | ---
[Animated Fox](../examples/animation/animated_fox.rs) | Plays an animation from a skinned glTF
[Animated Transform](../examples/animation/animated_transform.rs) | Create and play an animation defined by code that operates on the `Transform` component
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.
2024-09-23 17:14:12 +00:00
[Animated UI](../examples/animation/animated_ui.rs) | Shows how to use animation clips to animate UI properties
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. ![CleanShot 2024-10-05 at 02 41 57](https://github.com/user-attachments/assets/0bb47db7-24f9-4504-88f1-40e375b89b1b) --------- Co-authored-by: Matty <weatherleymatthew@gmail.com> Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com> Co-authored-by: François Mockers <francois.mockers@vleue.com>
2024-10-06 10:03:05 +00:00
[Animation Events](../examples/animation/animation_events.rs) | Demonstrate how to use animation events
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>
2024-03-07 20:22:42 +00:00
[Animation Graph](../examples/animation/animation_graph.rs) | Blends multiple animations together with a graph
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.)
2024-09-02 17:10:34 +00:00
[Animation Masks](../examples/animation/animation_masks.rs) | Demonstrates animation masks
[Color animation](../examples/animation/color_animation.rs) | Demonstrates how to animate colors using mixing and splines in different color spaces
[Custom Skinned Mesh](../examples/animation/custom_skinned_mesh.rs) | Skinned mesh example with mesh and joints data defined in code
Simplified easing curves (#15711) # Objective Simplify the API surrounding easing curves. Broaden the base of types that support easing. ## Solution There is now a single library function, `easing_curve`, which constructs a unit-parametrized easing curve between two values based on an `EaseFunction`: ```rust /// Given a `start` and `end` value, create a curve parametrized over [the unit interval] /// that connects them, using the given [ease function] to determine the form of the /// curve in between. /// /// [the unit interval]: Interval::UNIT /// [ease function]: EaseFunction pub fn easing_curve<T: Ease>(start: T, end: T, ease_fn: EaseFunction) -> EasingCurve<T> { //... } ``` As this shows, the type of the output curve is generic only in `T`. In particular, as long as `T` is `Reflect` (and `FromReflect` etc. — i.e., a standard "well-behaved" reflectable type), `EasingCurve<T>` is also `Reflect`, and there is no special field handling nonsense. Therefore, `EasingCurve` is the kind of thing that would be able to be easily changed in an editor. This is made possible by storing the actual `EaseFunction` on `EasingCurve<T>` instead of indirecting through some kind of function type (which generally leads to issues with reflection). The types that can be eased are those that implement a trait `Ease`: ```rust /// A type whose values can be eased between. /// /// This requires the construction of an interpolation curve that actually extends /// beyond the curve segment that connects two values, because an easing curve may /// extrapolate before the starting value and after the ending value. This is /// especially common in easing functions that mimic elastic or springlike behavior. pub trait Ease: Sized { /// Given `start` and `end` values, produce a curve with [unlimited domain] /// that: /// - takes a value equivalent to `start` at `t = 0` /// - takes a value equivalent to `end` at `t = 1` /// - has constant speed everywhere, including outside of `[0, 1]` /// /// [unlimited domain]: Interval::EVERYWHERE fn interpolating_curve_unbounded(start: &Self, end: &Self) -> impl Curve<Self>; } ``` (I know, I know, yet *another* interpolation trait. See 'Future direction'.) The other existing easing functions from the previous version of this module have also become new members of `EaseFunction`: `Linear`, `Steps`, and `Elastic` (which maybe needs a different name). The latter two are parametrized. ## Testing Tested using the `easing_functions` example. I also axed the `cubic_curve` example which was of questionable value and replaced it with `eased_motion`, which uses this API in the context of animation: https://github.com/user-attachments/assets/3c802992-6b9b-4b56-aeb1-a47501c29ce2 --- ## Future direction Morally speaking, `Ease` is incredibly similar to `StableInterpolate`. Probably, we should just merge `StableInterpolate` into `Ease`, and then make `SmoothNudge` an automatic extension trait of `Ease`. The reason I didn't do that is that `StableInterpolate` is not implemented for `VectorSpace` because of concerns about the `Color` types, and I wanted to avoid controversy. I think that may be a good idea though. As Alice mentioned before, we should also probably get rid of the `interpolation` dependency. The parametrized `Elastic` variant probably also needs some additional work (e.g. renaming, in/out/in-out variants, etc.) if we want to keep it.
2024-10-08 19:45:13 +00:00
[Eased Motion](../examples/animation/eased_motion.rs) | Demonstrates the application of easing curves to animate an object
[Easing Functions](../examples/animation/easing_functions.rs) | Showcases the built-in easing functions
Add morph targets (#8158) # Objective - Add morph targets to `bevy_pbr` (closes #5756) & load them from glTF - Supersedes #3722 - Fixes #6814 [Morph targets][1] (also known as shape interpolation, shape keys, or blend shapes) allow animating individual vertices with fine grained controls. This is typically used for facial expressions. By specifying multiple poses as vertex offset, and providing a set of weight of each pose, it is possible to define surprisingly realistic transitions between poses. Blending between multiple poses also allow composition. Morph targets are part of the [gltf standard][2] and are a feature of Unity and Unreal, and babylone.js, it is only natural to implement them in bevy. ## Solution This implementation of morph targets uses a 3d texture where each pixel is a component of an animated attribute. Each layer is a different target. We use a 2d texture for each target, because the number of attribute×components×animated vertices is expected to always exceed the maximum pixel row size limit of webGL2. It copies fairly closely the way skinning is implemented on the CPU side, while on the GPU side, the shader morph target implementation is a relatively trivial detail. We add an optional `morph_texture` to the `Mesh` struct. The `morph_texture` is built through a method that accepts an iterator over attribute buffers. The `MorphWeights` component, user-accessible, controls the blend of poses used by mesh instances (so that multiple copy of the same mesh may have different weights), all the weights are uploaded to a uniform buffer of 256 `f32`. We limit to 16 poses per mesh, and a total of 256 poses. More literature: * Old babylone.js implementation (vertex attribute-based): https://www.eternalcoding.com/dev-log-1-morph-targets/ * Babylone.js implementation (similar to ours): https://www.youtube.com/watch?v=LBPRmGgU0PE * GPU gems 3: https://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-3-directx-10-blend-shapes-breaking-limits * Development discord thread https://discord.com/channels/691052431525675048/1083325980615114772 https://user-images.githubusercontent.com/26321040/231181046-3bca2ab2-d4d9-472e-8098-639f1871ce2e.mp4 https://github.com/bevyengine/bevy/assets/26321040/d2a0c544-0ef8-45cf-9f99-8c3792f5a258 ## Acknowledgements * Thanks to `storytold` for sponsoring the feature * Thanks to `superdump` and `james7132` for guidance and help figuring out stuff ## Future work - Handling of less and more attributes (eg: animated uv, animated arbitrary attributes) - Dynamic pose allocation (so that zero-weighted poses aren't uploaded to GPU for example, enables much more total poses) - Better animation API, see #8357 ---- ## Changelog - Add morph targets to bevy meshes - Support up to 64 poses per mesh of individually up to 116508 vertices, animation currently strictly limited to the position, normal and tangent attributes. - Load a morph target using `Mesh::set_morph_targets` - Add `VisitMorphTargets` and `VisitMorphAttributes` traits to `bevy_render`, this allows defining morph targets (a fairly complex and nested data structure) through iterators (ie: single copy instead of passing around buffers), see documentation of those traits for details - Add `MorphWeights` component exported by `bevy_render` - `MorphWeights` control mesh's morph target weights, blending between various poses defined as morph targets. - `MorphWeights` are directly inherited by direct children (single level of hierarchy) of an entity. This allows controlling several mesh primitives through a unique entity _as per GLTF spec_. - Add `MorphTargetNames` component, naming each indices of loaded morph targets. - Load morph targets weights and buffers in `bevy_gltf` - handle morph targets animations in `bevy_animation` (previously, it was a `warn!` log) - Add the `MorphStressTest.gltf` asset for morph targets testing, taken from the glTF samples repo, CC0. - Add morph target manipulation to `scene_viewer` - Separate the animation code in `scene_viewer` from the rest of the code, reducing `#[cfg(feature)]` noise - Add the `morph_targets.rs` example to show off how to manipulate morph targets, loading `MorpStressTest.gltf` ## Migration Guide - (very specialized, unlikely to be touched by 3rd parties) - `MeshPipeline` now has a single `mesh_layouts` field rather than separate `mesh_layout` and `skinned_mesh_layout` fields. You should handle all possible mesh bind group layouts in your implementation - You should also handle properly the new `MORPH_TARGETS` shader def and mesh pipeline key. A new function is exposed to make this easier: `setup_moprh_and_skinning_defs` - The `MeshBindGroup` is now `MeshBindGroups`, cached bind groups are now accessed through the `get` method. [1]: https://en.wikipedia.org/wiki/Morph_target_animation [2]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#morph-targets --------- Co-authored-by: François <mockersf@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-22 20:00:01 +00:00
[Morph Targets](../examples/animation/morph_targets.rs) | Plays an animation from a glTF file with meshes with morph targets
[glTF Skinned Mesh](../examples/animation/gltf_skinned_mesh.rs) | Skinned mesh example with mesh and joints data loaded from a glTF file
2020-08-17 23:02:59 +00:00
## Application
Example | Description
--- | ---
[Advanced log layers](../examples/app/log_layers_ecs.rs) | Illustrate how to transfer data between log layers and Bevy's ECS
[Custom Loop](../examples/app/custom_loop.rs) | Demonstrates how to create a custom runner (to update an app manually)
[Drag and Drop](../examples/app/drag_and_drop.rs) | An example that shows how to handle drag and drop in an app
[Empty](../examples/app/empty.rs) | An empty application (does nothing)
[Empty with Defaults](../examples/app/empty_defaults.rs) | An empty application with default plugins
[Headless](../examples/app/headless.rs) | An application that runs without default plugins
[Headless Renderer](../examples/app/headless_renderer.rs) | An application that runs with no window, but renders into image file
[Log layers](../examples/app/log_layers.rs) | Illustrate how to add custom log layers
[Logs](../examples/app/logs.rs) | Illustrate how to use generate log output
[No Renderer](../examples/app/no_renderer.rs) | An application that runs with default plugins and displays an empty window, but without an actual renderer
[Plugin](../examples/app/plugin.rs) | Demonstrates the creation and registration of a custom plugin
[Plugin Group](../examples/app/plugin_group.rs) | Demonstrates the creation and registration of a custom plugin group
[Return after Run](../examples/app/return_after_run.rs) | Show how to return to main after the Bevy app has exited
[Thread Pool Resources](../examples/app/thread_pool_resources.rs) | Creates and customizes the internal thread pool
[Without Winit](../examples/app/without_winit.rs) | Create an application without winit (runs single time, no event loop)
2020-08-17 23:02:59 +00:00
## Assets
Example | Description
--- | ---
[Alter Mesh](../examples/asset/alter_mesh.rs) | Shows how to modify the underlying asset of a Mesh after spawning.
[Alter Sprite](../examples/asset/alter_sprite.rs) | Shows how to modify texture assets after spawning.
Added Method to Allow Pipelined Asset Loading (#10565) # Objective - Fixes #10518 ## Solution I've added a method to `LoadContext`, `load_direct_with_reader`, which mirrors the behaviour of `load_direct` with a single key difference: it is provided with the `Reader` by the caller, rather than getting it from the contained `AssetServer`. This allows for an `AssetLoader` to process its `Reader` stream, and then directly hand the results off to the `LoadContext` to handle further loading. The outer `AssetLoader` can control how the `Reader` is interpreted by providing a relevant `AssetPath`. For example, a Gzip decompression loader could process the asset `images/my_image.png.gz` by decompressing the bytes, then handing the decompressed result to the `LoadContext` with the new path `images/my_image.png.gz/my_image.png`. This intuitively reflects the nature of contained assets, whilst avoiding unintended behaviour, since the generated path cannot be a real file path (a file and folder of the same name cannot coexist in most file-systems). ```rust #[derive(Asset, TypePath)] pub struct GzAsset { pub uncompressed: ErasedLoadedAsset, } #[derive(Default)] pub struct GzAssetLoader; impl AssetLoader for GzAssetLoader { type Asset = GzAsset; type Settings = (); type Error = GzAssetLoaderError; fn load<'a>( &'a self, reader: &'a mut Reader, _settings: &'a (), load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> { Box::pin(async move { let compressed_path = load_context.path(); let file_name = compressed_path .file_name() .ok_or(GzAssetLoaderError::IndeterminateFilePath)? .to_string_lossy(); let uncompressed_file_name = file_name .strip_suffix(".gz") .ok_or(GzAssetLoaderError::IndeterminateFilePath)?; let contained_path = compressed_path.join(uncompressed_file_name); let mut bytes_compressed = Vec::new(); reader.read_to_end(&mut bytes_compressed).await?; let mut decoder = GzDecoder::new(bytes_compressed.as_slice()); let mut bytes_uncompressed = Vec::new(); decoder.read_to_end(&mut bytes_uncompressed)?; // Now that we have decompressed the asset, let's pass it back to the // context to continue loading let mut reader = VecReader::new(bytes_uncompressed); let uncompressed = load_context .load_direct_with_reader(&mut reader, contained_path) .await?; Ok(GzAsset { uncompressed }) }) } fn extensions(&self) -> &[&str] { &["gz"] } } ``` Because this example is so prudent, I've included an `asset_decompression` example which implements this exact behaviour: ```rust fn main() { App::new() .add_plugins(DefaultPlugins) .init_asset::<GzAsset>() .init_asset_loader::<GzAssetLoader>() .add_systems(Startup, setup) .add_systems(Update, decompress::<Image>) .run(); } fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { commands.spawn(Camera2dBundle::default()); commands.spawn(( Compressed::<Image> { compressed: asset_server.load("data/compressed_image.png.gz"), ..default() }, Sprite::default(), TransformBundle::default(), VisibilityBundle::default(), )); } fn decompress<A: Asset>( mut commands: Commands, asset_server: Res<AssetServer>, mut compressed_assets: ResMut<Assets<GzAsset>>, query: Query<(Entity, &Compressed<A>)>, ) { for (entity, Compressed { compressed, .. }) in query.iter() { let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else { continue; }; let uncompressed = uncompressed.take::<A>().unwrap(); commands .entity(entity) .remove::<Compressed<A>>() .insert(asset_server.add(uncompressed)); } } ``` A key limitation to this design is how to type the internally loaded asset, since the example `GzAssetLoader` is unaware of the internal asset type `A`. As such, in this example I store the contained asset as an `ErasedLoadedAsset`, and leave it up to the consumer of the `GzAsset` to handle typing the final result, which is the purpose of the `decompress` system. This limitation can be worked around by providing type information to the `GzAssetLoader`, such as `GzAssetLoader<Image, ImageAssetLoader>`, but this would require registering the asset loader for every possible decompression target. Aside from this limitation, nested asset containerisation works as an end user would expect; if the user registers a `TarAssetLoader`, and a `GzAssetLoader`, then they can load assets with compound containerisation, such as `images.tar.gz`. --- ## Changelog - Added `LoadContext::load_direct_with_reader` - Added `asset_decompression` example ## Notes - While I believe my implementation of a Gzip asset loader is reasonable, I haven't included it as a public feature of `bevy_asset` to keep the scope of this PR as focussed as possible. - I have included `flate2` as a `dev-dependency` for the example; it is not included in the main dependency graph.
2023-11-16 17:47:31 +00:00
[Asset Decompression](../examples/asset/asset_decompression.rs) | Demonstrates loading a compressed asset
[Asset Loading](../examples/asset/asset_loading.rs) | Demonstrates various methods to load assets
[Asset Processing](../examples/asset/processing/asset_processing.rs) | Demonstrates how to process and load custom assets
[Asset Settings](../examples/asset/asset_settings.rs) | Demonstrates various methods of applying settings when loading an asset
[Custom Asset](../examples/asset/custom_asset.rs) | Implements a custom asset loader
Bevy Asset V2 (#8624) # Bevy Asset V2 Proposal ## Why Does Bevy Need A New Asset System? Asset pipelines are a central part of the gamedev process. Bevy's current asset system is missing a number of features that make it non-viable for many classes of gamedev. After plenty of discussions and [a long community feedback period](https://github.com/bevyengine/bevy/discussions/3972), we've identified a number missing features: * **Asset Preprocessing**: it should be possible to "preprocess" / "compile" / "crunch" assets at "development time" rather than when the game starts up. This enables offloading expensive work from deployed apps, faster asset loading, less runtime memory usage, etc. * **Per-Asset Loader Settings**: Individual assets cannot define their own loaders that override the defaults. Additionally, they cannot provide per-asset settings to their loaders. This is a huge limitation, as many asset types don't provide all information necessary for Bevy _inside_ the asset. For example, a raw PNG image says nothing about how it should be sampled (ex: linear vs nearest). * **Asset `.meta` files**: assets should have configuration files stored adjacent to the asset in question, which allows the user to configure asset-type-specific settings. These settings should be accessible during the pre-processing phase. Modifying a `.meta` file should trigger a re-processing / re-load of the asset. It should be possible to configure asset loaders from the meta file. * **Processed Asset Hot Reloading**: Changes to processed assets (or their dependencies) should result in re-processing them and re-loading the results in live Bevy Apps. * **Asset Dependency Tracking**: The current bevy_asset has no good way to wait for asset dependencies to load. It punts this as an exercise for consumers of the loader apis, which is unreasonable and error prone. There should be easy, ergonomic ways to wait for assets to load and block some logic on an asset's entire dependency tree loading. * **Runtime Asset Loading**: it should be (optionally) possible to load arbitrary assets dynamically at runtime. This necessitates being able to deploy and run the asset server alongside Bevy Apps on _all platforms_. For example, we should be able to invoke the shader compiler at runtime, stream scenes from sources like the internet, etc. To keep deployed binaries (and startup times) small, the runtime asset server configuration should be configurable with different settings compared to the "pre processor asset server". * **Multiple Backends**: It should be possible to load assets from arbitrary sources (filesystems, the internet, remote asset serves, etc). * **Asset Packing**: It should be possible to deploy assets in compressed "packs", which makes it easier and more efficient to distribute assets with Bevy Apps. * **Asset Handoff**: It should be possible to hold a "live" asset handle, which correlates to runtime data, without actually holding the asset in memory. Ex: it must be possible to hold a reference to a GPU mesh generated from a "mesh asset" without keeping the mesh data in CPU memory * **Per-Platform Processed Assets**: Different platforms and app distributions have different capabilities and requirements. Some platforms need lower asset resolutions or different asset formats to operate within the hardware constraints of the platform. It should be possible to define per-platform asset processing profiles. And it should be possible to deploy only the assets required for a given platform. These features have architectural implications that are significant enough to require a full rewrite. The current Bevy Asset implementation got us this far, but it can take us no farther. This PR defines a brand new asset system that implements most of these features, while laying the foundations for the remaining features to be built. ## Bevy Asset V2 Here is a quick overview of the features introduced in this PR. * **Asset Preprocessing**: Preprocess assets at development time into more efficient (and configurable) representations * **Dependency Aware**: Dependencies required to process an asset are tracked. If an asset's processed dependency changes, it will be reprocessed * **Hot Reprocessing/Reloading**: detect changes to asset source files, reprocess them if they have changed, and then hot-reload them in Bevy Apps. * **Only Process Changes**: Assets are only re-processed when their source file (or meta file) has changed. This uses hashing and timestamps to avoid processing assets that haven't changed. * **Transactional and Reliable**: Uses write-ahead logging (a technique commonly used by databases) to recover from crashes / forced-exits. Whenever possible it avoids full-reprocessing / only uncompleted transactions will be reprocessed. When the processor is running in parallel with a Bevy App, processor asset writes block Bevy App asset reads. Reading metadata + asset bytes is guaranteed to be transactional / correctly paired. * **Portable / Run anywhere / Database-free**: The processor does not rely on an in-memory database (although it uses some database techniques for reliability). This is important because pretty much all in-memory databases have unsupported platforms or build complications. * **Configure Processor Defaults Per File Type**: You can say "use this processor for all files of this type". * **Custom Processors**: The `Processor` trait is flexible and unopinionated. It can be implemented by downstream plugins. * **LoadAndSave Processors**: Most asset processing scenarios can be expressed as "run AssetLoader A, save the results using AssetSaver X, and then load the result using AssetLoader B". For example, load this png image using `PngImageLoader`, which produces an `Image` asset and then save it using `CompressedImageSaver` (which also produces an `Image` asset, but in a compressed format), which takes an `Image` asset as input. This means if you have an `AssetLoader` for an asset, you are already half way there! It also means that you can share AssetSavers across multiple loaders. Because `CompressedImageSaver` accepts Bevy's generic Image asset as input, it means you can also use it with some future `JpegImageLoader`. * **Loader and Saver Settings**: Asset Loaders and Savers can now define their own settings types, which are passed in as input when an asset is loaded / saved. Each asset can define its own settings. * **Asset `.meta` files**: configure asset loaders, their settings, enable/disable processing, and configure processor settings * **Runtime Asset Dependency Tracking** Runtime asset dependencies (ex: if an asset contains a `Handle<Image>`) are tracked by the asset server. An event is emitted when an asset and all of its dependencies have been loaded * **Unprocessed Asset Loading**: Assets do not require preprocessing. They can be loaded directly. A processed asset is just a "normal" asset with some extra metadata. Asset Loaders don't need to know or care about whether or not an asset was processed. * **Async Asset IO**: Asset readers/writers use async non-blocking interfaces. Note that because Rust doesn't yet support async traits, there is a bit of manual Boxing / Future boilerplate. This will hopefully be removed in the near future when Rust gets async traits. * **Pluggable Asset Readers and Writers**: Arbitrary asset source readers/writers are supported, both by the processor and the asset server. * **Better Asset Handles** * **Single Arc Tree**: Asset Handles now use a single arc tree that represents the lifetime of the asset. This makes their implementation simpler, more efficient, and allows us to cheaply attach metadata to handles. Ex: the AssetPath of a handle is now directly accessible on the handle itself! * **Const Typed Handles**: typed handles can be constructed in a const context. No more weird "const untyped converted to typed at runtime" patterns! * **Handles and Ids are Smaller / Faster To Hash / Compare**: Typed `Handle<T>` is now much smaller in memory and `AssetId<T>` is even smaller. * **Weak Handle Usage Reduction**: In general Handles are now considered to be "strong". Bevy features that previously used "weak `Handle<T>`" have been ported to `AssetId<T>`, which makes it statically clear that the features do not hold strong handles (while retaining strong type information). Currently Handle::Weak still exists, but it is very possible that we can remove that entirely. * **Efficient / Dense Asset Ids**: Assets now have efficient dense runtime asset ids, which means we can avoid expensive hash lookups. Assets are stored in Vecs instead of HashMaps. There are now typed and untyped ids, which means we no longer need to store dynamic type information in the ID for typed handles. "AssetPathId" (which was a nightmare from a performance and correctness standpoint) has been entirely removed in favor of dense ids (which are retrieved for a path on load) * **Direct Asset Loading, with Dependency Tracking**: Assets that are defined at runtime can still have their dependencies tracked by the Asset Server (ex: if you create a material at runtime, you can still wait for its textures to load). This is accomplished via the (currently optional) "asset dependency visitor" trait. This system can also be used to define a set of assets to load, then wait for those assets to load. * **Async folder loading**: Folder loading also uses this system and immediately returns a handle to the LoadedFolder asset, which means folder loading no longer blocks on directory traversals. * **Improved Loader Interface**: Loaders now have a specific "top level asset type", which makes returning the top-level asset simpler and statically typed. * **Basic Image Settings and Processing**: Image assets can now be processed into the gpu-friendly Basic Universal format. The ImageLoader now has a setting to define what format the image should be loaded as. Note that this is just a minimal MVP ... plenty of additional work to do here. To demo this, enable the `basis-universal` feature and turn on asset processing. * **Simpler Audio Play / AudioSink API**: Asset handle providers are cloneable, which means the Audio resource can mint its own handles. This means you can now do `let sink_handle = audio.play(music)` instead of `let sink_handle = audio_sinks.get_handle(audio.play(music))`. Note that this might still be replaced by https://github.com/bevyengine/bevy/pull/8424. **Removed Handle Casting From Engine Features**: Ex: FontAtlases no longer use casting between handle types ## Using The New Asset System ### Normal Unprocessed Asset Loading By default the `AssetPlugin` does not use processing. It behaves pretty much the same way as the old system. If you are defining a custom asset, first derive `Asset`: ```rust #[derive(Asset)] struct Thing { value: String, } ``` Initialize the asset: ```rust app.init_asset:<Thing>() ``` Implement a new `AssetLoader` for it: ```rust #[derive(Default)] struct ThingLoader; #[derive(Serialize, Deserialize, Default)] pub struct ThingSettings { some_setting: bool, } impl AssetLoader for ThingLoader { type Asset = Thing; type Settings = ThingSettings; fn load<'a>( &'a self, reader: &'a mut Reader, settings: &'a ThingSettings, load_context: &'a mut LoadContext, ) -> BoxedFuture<'a, Result<Thing, anyhow::Error>> { Box::pin(async move { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes).await?; // convert bytes to value somehow Ok(Thing { value }) }) } fn extensions(&self) -> &[&str] { &["thing"] } } ``` Note that this interface will get much cleaner once Rust gets support for async traits. `Reader` is an async futures_io::AsyncRead. You can stream bytes as they come in or read them all into a `Vec<u8>`, depending on the context. You can use `let handle = load_context.load(path)` to kick off a dependency load, retrieve a handle, and register the dependency for the asset. Then just register the loader in your Bevy app: ```rust app.init_asset_loader::<ThingLoader>() ``` Now just add your `Thing` asset files into the `assets` folder and load them like this: ```rust fn system(asset_server: Res<AssetServer>) { let handle = Handle<Thing> = asset_server.load("cool.thing"); } ``` You can check load states directly via the asset server: ```rust if asset_server.load_state(&handle) == LoadState::Loaded { } ``` You can also listen for events: ```rust fn system(mut events: EventReader<AssetEvent<Thing>>, handle: Res<SomeThingHandle>) { for event in events.iter() { if event.is_loaded_with_dependencies(&handle) { } } } ``` Note the new `AssetEvent::LoadedWithDependencies`, which only fires when the asset is loaded _and_ all dependencies (and their dependencies) have loaded. Unlike the old asset system, for a given asset path all `Handle<T>` values point to the same underlying Arc. This means Handles can cheaply hold more asset information, such as the AssetPath: ```rust // prints the AssetPath of the handle info!("{:?}", handle.path()) ``` ### Processed Assets Asset processing can be enabled via the `AssetPlugin`. When developing Bevy Apps with processed assets, do this: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev())) ``` This runs the `AssetProcessor` in the background with hot-reloading. It reads assets from the `assets` folder, processes them, and writes them to the `.imported_assets` folder. Asset loads in the Bevy App will wait for a processed version of the asset to become available. If an asset in the `assets` folder changes, it will be reprocessed and hot-reloaded in the Bevy App. When deploying processed Bevy apps, do this: ```rust app.add_plugins(DefaultPlugins.set(AssetPlugin::processed())) ``` This does not run the `AssetProcessor` in the background. It behaves like `AssetPlugin::unprocessed()`, but reads assets from `.imported_assets`. When the `AssetProcessor` is running, it will populate sibling `.meta` files for assets in the `assets` folder. Meta files for assets that do not have a processor configured look like this: ```rust ( meta_format_version: "1.0", asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: FromExtension, ), ), ) ``` This is metadata for an image asset. For example, if you have `assets/my_sprite.png`, this could be the metadata stored at `assets/my_sprite.png.meta`. Meta files are totally optional. If no metadata exists, the default settings will be used. In short, this file says "load this asset with the ImageLoader and use the file extension to determine the image type". This type of meta file is supported in all AssetPlugin modes. If in `Unprocessed` mode, the asset (with the meta settings) will be loaded directly. If in `ProcessedDev` mode, the asset file will be copied directly to the `.imported_assets` folder. The meta will also be copied directly to the `.imported_assets` folder, but with one addition: ```rust ( meta_format_version: "1.0", processed_info: Some(( hash: 12415480888597742505, full_hash: 14344495437905856884, process_dependencies: [], )), asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: FromExtension, ), ), ) ``` `processed_info` contains `hash` (a direct hash of the asset and meta bytes), `full_hash` (a hash of `hash` and the hashes of all `process_dependencies`), and `process_dependencies` (the `path` and `full_hash` of every process_dependency). A "process dependency" is an asset dependency that is _directly_ used when processing the asset. Images do not have process dependencies, so this is empty. When the processor is enabled, you can use the `Process` metadata config: ```rust ( meta_format_version: "1.0", asset: Process( processor: "bevy_asset::processor::process::LoadAndSave<bevy_render::texture::image_loader::ImageLoader, bevy_render::texture::compressed_image_saver::CompressedImageSaver>", settings: ( loader_settings: ( format: FromExtension, ), saver_settings: ( generate_mipmaps: true, ), ), ), ) ``` This configures the asset to use the `LoadAndSave` processor, which runs an AssetLoader and feeds the result into an AssetSaver (which saves the given Asset and defines a loader to load it with). (for terseness LoadAndSave will likely get a shorter/friendlier type name when [Stable Type Paths](#7184) lands). `LoadAndSave` is likely to be the most common processor type, but arbitrary processors are supported. `CompressedImageSaver` saves an `Image` in the Basis Universal format and configures the ImageLoader to load it as basis universal. The `AssetProcessor` will read this meta, run it through the LoadAndSave processor, and write the basis-universal version of the image to `.imported_assets`. The final metadata will look like this: ```rust ( meta_format_version: "1.0", processed_info: Some(( hash: 905599590923828066, full_hash: 9948823010183819117, process_dependencies: [], )), asset: Load( loader: "bevy_render::texture::image_loader::ImageLoader", settings: ( format: Format(Basis), ), ), ) ``` To try basis-universal processing out in Bevy examples, (for example `sprite.rs`), change `add_plugins(DefaultPlugins)` to `add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev()))` and run with the `basis-universal` feature enabled: `cargo run --features=basis-universal --example sprite`. To create a custom processor, there are two main paths: 1. Use the `LoadAndSave` processor with an existing `AssetLoader`. Implement the `AssetSaver` trait, register the processor using `asset_processor.register_processor::<LoadAndSave<ImageLoader, CompressedImageSaver>>(image_saver.into())`. 2. Implement the `Process` trait directly and register it using: `asset_processor.register_processor(thing_processor)`. You can configure default processors for file extensions like this: ```rust asset_processor.set_default_processor::<ThingProcessor>("thing") ``` There is one more metadata type to be aware of: ```rust ( meta_format_version: "1.0", asset: Ignore, ) ``` This will ignore the asset during processing / prevent it from being written to `.imported_assets`. The AssetProcessor stores a transaction log at `.imported_assets/log` and uses it to gracefully recover from unexpected stops. This means you can force-quit the processor (and Bevy Apps running the processor in parallel) at arbitrary times! `.imported_assets` is "local state". It should _not_ be checked into source control. It should also be considered "read only". In practice, you _can_ modify processed assets and processed metadata if you really need to test something. But those modifications will not be represented in the hashes of the assets, so the processed state will be "out of sync" with the source assets. The processor _will not_ fix this for you. Either revert the change after you have tested it, or delete the processed files so they can be re-populated. ## Open Questions There are a number of open questions to be discussed. We should decide if they need to be addressed in this PR and if so, how we will address them: ### Implied Dependencies vs Dependency Enumeration There are currently two ways to populate asset dependencies: * **Implied via AssetLoaders**: if an AssetLoader loads an asset (and retrieves a handle), a dependency is added to the list. * **Explicit via the optional Asset::visit_dependencies**: if `server.load_asset(my_asset)` is called, it will call `my_asset.visit_dependencies`, which will grab dependencies that have been manually defined for the asset via the Asset trait impl (which can be derived). This means that defining explicit dependencies is optional for "loaded assets". And the list of dependencies is always accurate because loaders can only produce Handles if they register dependencies. If an asset was loaded with an AssetLoader, it only uses the implied dependencies. If an asset was created at runtime and added with `asset_server.load_asset(MyAsset)`, it will use `Asset::visit_dependencies`. However this can create a behavior mismatch between loaded assets and equivalent "created at runtime" assets if `Assets::visit_dependencies` doesn't exactly match the dependencies produced by the AssetLoader. This behavior mismatch can be resolved by completely removing "implied loader dependencies" and requiring `Asset::visit_dependencies` to supply dependency data. But this creates two problems: * It makes defining loaded assets harder and more error prone: Devs must remember to manually annotate asset dependencies with `#[dependency]` when deriving `Asset`. For more complicated assets (such as scenes), the derive likely wouldn't be sufficient and a manual `visit_dependencies` impl would be required. * Removes the ability to immediately kick off dependency loads: When AssetLoaders retrieve a Handle, they also immediately kick off an asset load for the handle, which means it can start loading in parallel _before_ the asset finishes loading. For large assets, this could be significant. (although this could be mitigated for processed assets if we store dependencies in the processed meta file and load them ahead of time) ### Eager ProcessorDev Asset Loading I made a controversial call in the interest of fast startup times ("time to first pixel") for the "processor dev mode configuration". When initializing the AssetProcessor, current processed versions of unchanged assets are yielded immediately, even if their dependencies haven't been checked yet for reprocessing. This means that non-current-state-of-filesystem-but-previously-valid assets might be returned to the App first, then hot-reloaded if/when their dependencies change and the asset is reprocessed. Is this behavior desirable? There is largely one alternative: do not yield an asset from the processor to the app until all of its dependencies have been checked for changes. In some common cases (load dependency has not changed since last run) this will increase startup time. The main question is "by how much" and is that slower startup time worth it in the interest of only yielding assets that are true to the current state of the filesystem. Should this be configurable? I'm starting to think we should only yield an asset after its (historical) dependencies have been checked for changes + processed as necessary, but I'm curious what you all think. ### Paths Are Currently The Only Canonical ID / Do We Want Asset UUIDs? In this implementation AssetPaths are the only canonical asset identifier (just like the previous Bevy Asset system and Godot). Moving assets will result in re-scans (and currently reprocessing, although reprocessing can easily be avoided with some changes). Asset renames/moves will break code and assets that rely on specific paths, unless those paths are fixed up. Do we want / need "stable asset uuids"? Introducing them is very possible: 1. Generate a UUID and include it in .meta files 2. Support UUID in AssetPath 3. Generate "asset indices" which are loaded on startup and map UUIDs to paths. 4 (maybe). Consider only supporting UUIDs for processed assets so we can generate quick-to-load indices instead of scanning meta files. The main "pro" is that assets referencing UUIDs don't need to be migrated when a path changes. The main "con" is that UUIDs cannot be "lazily resolved" like paths. They need a full view of all assets to answer the question "does this UUID exist". Which means UUIDs require the AssetProcessor to fully finish startup scans before saying an asset doesnt exist. And they essentially require asset pre-processing to use in apps, because scanning all asset metadata files at runtime to resolve a UUID is not viable for medium-to-large apps. It really requires a pre-generated UUID index, which must be loaded before querying for assets. I personally think this should be investigated in a separate PR. Paths aren't going anywhere ... _everyone_ uses filesystems (and filesystem-like apis) to manage their asset source files. I consider them permanent canonical asset information. Additionally, they behave well for both processed and unprocessed asset modes. Given that Bevy is supporting both, this feels like the right canonical ID to start with. UUIDS (and maybe even other indexed-identifier types) can be added later as necessary. ### Folder / File Naming Conventions All asset processing config currently lives in the `.imported_assets` folder. The processor transaction log is in `.imported_assets/log`. Processed assets are added to `.imported_assets/Default`, which will make migrating to processed asset profiles (ex: a `.imported_assets/Mobile` profile) a non-breaking change. It also allows us to create top-level files like `.imported_assets/log` without it being interpreted as an asset. Meta files currently have a `.meta` suffix. Do we like these names and conventions? ### Should the `AssetPlugin::processed_dev` configuration enable `watch_for_changes` automatically? Currently it does (which I think makes sense), but it does make it the only configuration that enables watch_for_changes by default. ### Discuss on_loaded High Level Interface: This PR includes a very rough "proof of concept" `on_loaded` system adapter that uses the `LoadedWithDependencies` event in combination with `asset_server.load_asset` dependency tracking to support this pattern ```rust fn main() { App::new() .init_asset::<MyAssets>() .add_systems(Update, on_loaded(create_array_texture)) .run(); } #[derive(Asset, Clone)] struct MyAssets { #[dependency] picture_of_my_cat: Handle<Image>, #[dependency] picture_of_my_other_cat: Handle<Image>, } impl FromWorld for ArrayTexture { fn from_world(world: &mut World) -> Self { picture_of_my_cat: server.load("meow.png"), picture_of_my_other_cat: server.load("meeeeeeeow.png"), } } fn spawn_cat(In(my_assets): In<MyAssets>, mut commands: Commands) { commands.spawn(SpriteBundle { texture: my_assets.picture_of_my_cat.clone(), ..default() }); commands.spawn(SpriteBundle { texture: my_assets.picture_of_my_other_cat.clone(), ..default() }); } ``` The implementation is _very_ rough. And it is currently unsafe because `bevy_ecs` doesn't expose some internals to do this safely from inside `bevy_asset`. There are plenty of unanswered questions like: * "do we add a Loadable" derive? (effectively automate the FromWorld implementation above) * Should `MyAssets` even be an Asset? (largely implemented this way because it elegantly builds on `server.load_asset(MyAsset { .. })` dependency tracking). We should think hard about what our ideal API looks like (and if this is a pattern we want to support). Not necessarily something we need to solve in this PR. The current `on_loaded` impl should probably be removed from this PR before merging. ## Clarifying Questions ### What about Assets as Entities? This Bevy Asset V2 proposal implementation initially stored Assets as ECS Entities. Instead of `AssetId<T>` + the `Assets<T>` resource it used `Entity` as the asset id and Asset values were just ECS components. There are plenty of compelling reasons to do this: 1. Easier to inline assets in Bevy Scenes (as they are "just" normal entities + components) 2. More flexible queries: use the power of the ECS to filter assets (ex: `Query<Mesh, With<Tree>>`). 3. Extensible. Users can add arbitrary component data to assets. 4. Things like "component visualization tools" work out of the box to visualize asset data. However Assets as Entities has a ton of caveats right now: * We need to be able to allocate entity ids without a direct World reference (aka rework id allocator in Entities ... i worked around this in my prototypes by just pre allocating big chunks of entities) * We want asset change events in addition to ECS change tracking ... how do we populate them when mutations can come from anywhere? Do we use Changed queries? This would require iterating over the change data for all assets every frame. Is this acceptable or should we implement a new "event based" component change detection option? * Reconciling manually created assets with asset-system managed assets has some nuance (ex: are they "loaded" / do they also have that component metadata?) * "how do we handle "static" / default entity handles" (ties in to the Entity Indices discussion: https://github.com/bevyengine/bevy/discussions/8319). This is necessary for things like "built in" assets and default handles in things like SpriteBundle. * Storing asset information as a component makes it easy to "invalidate" asset state by removing the component (or forcing modifications). Ideally we have ways to lock this down (some combination of Rust type privacy and ECS validation) In practice, how we store and identify assets is a reasonably superficial change (porting off of Assets as Entities and implementing dedicated storage + ids took less than a day). So once we sort out the remaining challenges the flip should be straightforward. Additionally, I do still have "Assets as Entities" in my commit history, so we can reuse that work. I personally think "assets as entities" is a good endgame, but it also doesn't provide _significant_ value at the moment and it certainly isn't ready yet with the current state of things. ### Why not Distill? [Distill](https://github.com/amethyst/distill) is a high quality fully featured asset system built in Rust. It is very natural to ask "why not just use Distill?". It is also worth calling out that for awhile, [we planned on adopting Distill / I signed off on it](https://github.com/bevyengine/bevy/issues/708). However I think Bevy has a number of constraints that make Distill adoption suboptimal: * **Architectural Simplicity:** * Distill's processor requires an in-memory database (lmdb) and RPC networked API (using Cap'n Proto). Each of these introduces API complexity that increases maintenance burden and "code grokability". Ignoring tests, documentation, and examples, Distill has 24,237 lines of Rust code (including generated code for RPC + database interactions). If you ignore generated code, it has 11,499 lines. * Bevy builds the AssetProcessor and AssetServer using pluggable AssetReader/AssetWriter Rust traits with simple io interfaces. They do not necessitate databases or RPC interfaces (although Readers/Writers could use them if that is desired). Bevy Asset V2 (at the time of writing this PR) is 5,384 lines of Rust code (ignoring tests, documentation, and examples). Grain of salt: Distill does have more features currently (ex: Asset Packing, GUIDS, remote-out-of-process asset processor). I do plan to implement these features in Bevy Asset V2 and I personally highly doubt they will meaningfully close the 6115 lines-of-code gap. * This complexity gap (which while illustrated by lines of code, is much bigger than just that) is noteworthy to me. Bevy should be hackable and there are pillars of Distill that are very hard to understand and extend. This is a matter of opinion (and Bevy Asset V2 also has complicated areas), but I think Bevy Asset V2 is much more approachable for the average developer. * Necessary disclaimer: counting lines of code is an extremely rough complexity metric. Read the code and form your own opinions. * **Optional Asset Processing:** Not all Bevy Apps (or Bevy App developers) need / want asset preprocessing. Processing increases the complexity of the development environment by introducing things like meta files, imported asset storage, running processors in the background, waiting for processing to finish, etc. Distill _requires_ preprocessing to work. With Bevy Asset V2 processing is fully opt-in. The AssetServer isn't directly aware of asset processors at all. AssetLoaders only care about converting bytes to runtime Assets ... they don't know or care if the bytes were pre-processed or not. Processing is "elegantly" (forgive my self-congratulatory phrasing) layered on top and builds on the existing Asset system primitives. * **Direct Filesystem Access to Processed Asset State:** Distill stores processed assets in a database. This makes debugging / inspecting the processed outputs harder (either requires special tooling to query the database or they need to be "deployed" to be inspected). Bevy Asset V2, on the other hand, stores processed assets in the filesystem (by default ... this is configurable). This makes interacting with the processed state more natural. Note that both Godot and Unity's new asset system store processed assets in the filesystem. * **Portability**: Because Distill's processor uses lmdb and RPC networking, it cannot be run on certain platforms (ex: lmdb is a non-rust dependency that cannot run on the web, some platforms don't support running network servers). Bevy should be able to process assets everywhere (ex: run the Bevy Editor on the web, compile + process shaders on mobile, etc). Distill does partially mitigate this problem by supporting "streaming" assets via the RPC protocol, but this is not a full solve from my perspective. And Bevy Asset V2 can (in theory) also stream assets (without requiring RPC, although this isn't implemented yet) Note that I _do_ still think Distill would be a solid asset system for Bevy. But I think the approach in this PR is a better solve for Bevy's specific "asset system requirements". ### Doesn't async-fs just shim requests to "sync" `std::fs`? What is the point? "True async file io" has limited / spotty platform support. async-fs (and the rust async ecosystem generally ... ex Tokio) currently use async wrappers over std::fs that offload blocking requests to separate threads. This may feel unsatisfying, but it _does_ still provide value because it prevents our task pools from blocking on file system operations (which would prevent progress when there are many tasks to do, but all threads in a pool are currently blocking on file system ops). Additionally, using async APIs for our AssetReaders and AssetWriters also provides value because we can later add support for "true async file io" for platforms that support it. _And_ we can implement other "true async io" asset backends (such as networked asset io). ## Draft TODO - [x] Fill in missing filesystem event APIs: file removed event (which is expressed as dangling RenameFrom events in some cases), file/folder renamed event - [x] Assets without loaders are not moved to the processed folder. This breaks things like referenced `.bin` files for GLTFs. This should be configurable per-non-asset-type. - [x] Initial implementation of Reflect and FromReflect for Handle. The "deserialization" parity bar is low here as this only worked with static UUIDs in the old impl ... this is a non-trivial problem. Either we add a Handle::AssetPath variant that gets "upgraded" to a strong handle on scene load or we use a separate AssetRef type for Bevy scenes (which is converted to a runtime Handle on load). This deserves its own discussion in a different pr. - [x] Populate read_asset_bytes hash when run by the processor (a bit of a special case .. when run by the processor the processed meta will contain the hash so we don't need to compute it on the spot, but we don't want/need to read the meta when run by the main AssetServer) - [x] Delay hot reloading: currently filesystem events are handled immediately, which creates timing issues in some cases. For example hot reloading images can sometimes break because the image isn't finished writing. We should add a delay, likely similar to the [implementation in this PR](https://github.com/bevyengine/bevy/pull/8503). - [x] Port old platform-specific AssetIo implementations to the new AssetReader interface (currently missing Android and web) - [x] Resolve on_loaded unsafety (either by removing the API entirely or removing the unsafe) - [x] Runtime loader setting overrides - [x] Remove remaining unwraps that should be error-handled. There are number of TODOs here - [x] Pretty AssetPath Display impl - [x] Document more APIs - [x] Resolve spurious "reloading because it has changed" events (to repro run load_gltf with `processed_dev()`) - [x] load_dependency hot reloading currently only works for processed assets. If processing is disabled, load_dependency changes are not hot reloaded. - [x] Replace AssetInfo dependency load/fail counters with `loading_dependencies: HashSet<UntypedAssetId>` to prevent reloads from (potentially) breaking counters. Storing this will also enable "dependency reloaded" events (see [Next Steps](#next-steps)) - [x] Re-add filesystem watcher cargo feature gate (currently it is not optional) - [ ] Migration Guide - [ ] Changelog ## Followup TODO - [ ] Replace "eager unchanged processed asset loading" behavior with "don't returned unchanged processed asset until dependencies have been checked". - [ ] Add true `Ignore` AssetAction that does not copy the asset to the imported_assets folder. - [ ] Finish "live asset unloading" (ex: free up CPU asset memory after uploading an image to the GPU), rethink RenderAssets, and port renderer features. The `Assets` collection uses `Option<T>` for asset storage to support its removal. (1) the Option might not actually be necessary ... might be able to just remove from the collection entirely (2) need to finalize removal apis - [ ] Try replacing the "channel based" asset id recycling with something a bit more efficient (ex: we might be able to use raw atomic ints with some cleverness) - [ ] Consider adding UUIDs to processed assets (scoped just to helping identify moved assets ... not exposed to load queries ... see [Next Steps](#next-steps)) - [ ] Store "last modified" source asset and meta timestamps in processed meta files to enable skipping expensive hashing when the file wasn't changed - [ ] Fix "slow loop" handle drop fix - [ ] Migrate to TypeName - [x] Handle "loader preregistration". See #9429 ## Next Steps * **Configurable per-type defaults for AssetMeta**: It should be possible to add configuration like "all png image meta should default to using nearest sampling" (currently this hard-coded per-loader/processor Settings::default() impls). Also see the "Folder Meta" bullet point. * **Avoid Reprocessing on Asset Renames / Moves**: See the "canonical asset ids" discussion in [Open Questions](#open-questions) and the relevant bullet point in [Draft TODO](#draft-todo). Even without canonical ids, folder renames could avoid reprocessing in some cases. * **Multiple Asset Sources**: Expand AssetPath to support "asset source names" and support multiple AssetReaders in the asset server (ex: `webserver://some_path/image.png` backed by an Http webserver AssetReader). The "default" asset reader would use normal `some_path/image.png` paths. Ideally this works in combination with multiple AssetWatchers for hot-reloading * **Stable Type Names**: this pr removes the TypeUuid requirement from assets in favor of `std::any::type_name`. This makes defining assets easier (no need to generate a new uuid / use weird proc macro syntax). It also makes reading meta files easier (because things have "friendly names"). We also use type names for components in scene files. If they are good enough for components, they are good enough for assets. And consistency across Bevy pillars is desirable. However, `std::any::type_name` is not guaranteed to be stable (although in practice it is). We've developed a [stable type path](https://github.com/bevyengine/bevy/pull/7184) to resolve this, which should be adopted when it is ready. * **Command Line Interface**: It should be possible to run the asset processor in a separate process from the command line. This will also require building a network-server-backed AssetReader to communicate between the app and the processor. We've been planning to build a "bevy cli" for awhile. This seems like a good excuse to build it. * **Asset Packing**: This is largely an additive feature, so it made sense to me to punt this until we've laid the foundations in this PR. * **Per-Platform Processed Assets**: It should be possible to generate assets for multiple platforms by supporting multiple "processor profiles" per asset (ex: compress with format X on PC and Y on iOS). I think there should probably be arbitrary "profiles" (which can be separate from actual platforms), which are then assigned to a given platform when generating the final asset distribution for that platform. Ex: maybe devs want a "Mobile" profile that is shared between iOS and Android. Or a "LowEnd" profile shared between web and mobile. * **Versioning and Migrations**: Assets, Loaders, Savers, and Processors need to have versions to determine if their schema is valid. If an asset / loader version is incompatible with the current version expected at runtime, the processor should be able to migrate them. I think we should try using Bevy Reflect for this, as it would allow us to load the old version as a dynamic Reflect type without actually having the old Rust type. It would also allow us to define "patches" to migrate between versions (Bevy Reflect devs are currently working on patching). The `.meta` file already has its own format version. Migrating that to new versions should also be possible. * **Real Copy-on-write AssetPaths**: Rust's actual Cow (clone-on-write type) currently used by AssetPath can still result in String clones that aren't actually necessary (cloning an Owned Cow clones the contents). Bevy's asset system requires cloning AssetPaths in a number of places, which result in actual clones of the internal Strings. This is not efficient. AssetPath internals should be reworked to exhibit truer cow-like-behavior that reduces String clones to the absolute minimum. * **Consider processor-less processing**: In theory the AssetServer could run processors "inline" even if the background AssetProcessor is disabled. If we decide this is actually desirable, we could add this. But I don't think its a priority in the short or medium term. * **Pre-emptive dependency loading**: We could encode dependencies in processed meta files, which could then be used by the Asset Server to kick of dependency loads as early as possible (prior to starting the actual asset load). Is this desirable? How much time would this save in practice? * **Optimize Processor With UntypedAssetIds**: The processor exclusively uses AssetPath to identify assets currently. It might be possible to swap these out for UntypedAssetIds in some places, which are smaller / cheaper to hash and compare. * **One to Many Asset Processing**: An asset source file that produces many assets currently must be processed into a single "processed" asset source. If labeled assets can be written separately they can each have their own configured savers _and_ they could be loaded more granularly. Definitely worth exploring! * **Automatically Track "Runtime-only" Asset Dependencies**: Right now, tracking "created at runtime" asset dependencies requires adding them via `asset_server.load_asset(StandardMaterial::default())`. I think with some cleverness we could also do this for `materials.add(StandardMaterial::default())`, making tracking work "everywhere". There are challenges here relating to change detection / ensuring the server is made aware of dependency changes. This could be expensive in some cases. * **"Dependency Changed" events**: Some assets have runtime artifacts that need to be re-generated when one of their dependencies change (ex: regenerate a material's bind group when a Texture needs to change). We are generating the dependency graph so we can definitely produce these events. Buuuuut generating these events will have a cost / they could be high frequency for some assets, so we might want this to be opt-in for specific cases. * **Investigate Storing More Information In Handles**: Handles can now store arbitrary information, which makes it cheaper and easier to access. How much should we move into them? Canonical asset load states (via atomics)? (`handle.is_loaded()` would be very cool). Should we store the entire asset and remove the `Assets<T>` collection? (`Arc<RwLock<Option<Image>>>`?) * **Support processing and loading files without extensions**: This is a pretty arbitrary restriction and could be supported with very minimal changes. * **Folder Meta**: It would be nice if we could define per folder processor configuration defaults (likely in a `.meta` or `.folder_meta` file). Things like "default to linear filtering for all Images in this folder". * **Replace async_broadcast with event-listener?** This might be approximately drop-in for some uses and it feels more light weight * **Support Running the AssetProcessor on the Web**: Most of the hard work is done here, but there are some easy straggling TODOs (make the transaction log an interface instead of a direct file writer so we can write a web storage backend, implement an AssetReader/AssetWriter that reads/writes to something like LocalStorage). * **Consider identifying and preventing circular dependencies**: This is especially important for "processor dependencies", as processing will silently never finish in these cases. * **Built-in/Inlined Asset Hot Reloading**: This PR regresses "built-in/inlined" asset hot reloading (previously provided by the DebugAssetServer). I'm intentionally punting this because I think it can be cleanly implemented with "multiple asset sources" by registering a "debug asset source" (ex: `debug://bevy_pbr/src/render/pbr.wgsl` asset paths) in combination with an AssetWatcher for that asset source and support for "manually loading pats with asset bytes instead of AssetReaders". The old DebugAssetServer was quite nasty and I'd love to avoid that hackery going forward. * **Investigate ways to remove double-parsing meta files**: Parsing meta files currently involves parsing once with "minimal" versions of the meta file to extract the type name of the loader/processor config, then parsing again to parse the "full" meta. This is suboptimal. We should be able to define custom deserializers that (1) assume the loader/processor type name comes first (2) dynamically looks up the loader/processor registrations to deserialize settings in-line (similar to components in the bevy scene format). Another alternative: deserialize as dynamic Reflect objects and then convert. * **More runtime loading configuration**: Support using the Handle type as a hint to select an asset loader (instead of relying on AssetPath extensions) * **More high level Processor trait implementations**: For example, it might be worth adding support for arbitrary chains of "asset transforms" that modify an in-memory asset representation between loading and saving. (ex: load a Mesh, run a `subdivide_mesh` transform, followed by a `flip_normals` transform, then save the mesh to an efficient compressed format). * **Bevy Scene Handle Deserialization**: (see the relevant [Draft TODO item](#draft-todo) for context) * **Explore High Level Load Interfaces**: See [this discussion](#discuss-on_loaded-high-level-interface) for one prototype. * **Asset Streaming**: It would be great if we could stream Assets (ex: stream a long video file piece by piece) * **ID Exchanging**: In this PR Asset Handles/AssetIds are bigger than they need to be because they have a Uuid enum variant. If we implement an "id exchanging" system that trades Uuids for "efficient runtime ids", we can cut down on the size of AssetIds, making them more efficient. This has some open design questions, such as how to spawn entities with "default" handle values (as these wouldn't have access to the exchange api in the current system). * **Asset Path Fixup Tooling**: Assets that inline asset paths inside them will break when an asset moves. The asset system provides the functionality to detect when paths break. We should build a framework that enables formats to define "path migrations". This is especially important for scene files. For editor-generated files, we should also consider using UUIDs (see other bullet point) to avoid the need to migrate in these cases. --------- Co-authored-by: BeastLe9enD <beastle9end@outlook.de> Co-authored-by: Mike <mike.hsu@gmail.com> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
2023-09-07 02:07:27 +00:00
[Custom Asset IO](../examples/asset/custom_asset_reader.rs) | Implements a custom AssetReader
[Embedded Asset](../examples/asset/embedded_asset.rs) | Embed an asset in the application binary and load it
[Extra asset source](../examples/asset/extra_source.rs) | Load an asset from a non-standard asset source
[Hot Reloading of Assets](../examples/asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk
[Mult-asset synchronization](../examples/asset/multi_asset_sync.rs) | Demonstrates how to wait for multiple assets to be loaded.
[Repeated texture configuration](../examples/asset/repeated_texture.rs) | How to configure the texture to repeat instead of the default clamp to edges
2020-08-17 23:02:59 +00:00
## Async Tasks
Example | Description
--- | ---
[Async Compute](../examples/async_tasks/async_compute.rs) | How to use `AsyncComputeTaskPool` to complete longer running tasks
[External Source of Data on an External Thread](../examples/async_tasks/external_source_external_thread.rs) | How to use an external thread to run an infinite task and communicate with a channel
2020-08-17 23:02:59 +00:00
## Audio
Example | Description
--- | ---
[Audio](../examples/audio/audio.rs) | Shows how to load and play an audio file
[Audio Control](../examples/audio/audio_control.rs) | Shows how to load and play an audio file, and control how it's played
[Decodable](../examples/audio/decodable.rs) | Shows how to create and register a custom audio source by implementing the `Decodable` type.
[Pitch](../examples/audio/pitch.rs) | Shows how to directly play a simple pitch
[Soundtrack](../examples/audio/soundtrack.rs) | Shows how to play different soundtracks based on game state
[Spatial Audio 2D](../examples/audio/spatial_audio_2d.rs) | Shows how to play spatial audio, and moving the emitter in 2D
[Spatial Audio 3D](../examples/audio/spatial_audio_3d.rs) | Shows how to play spatial audio, and moving the emitter in 3D
2020-08-17 23:02:59 +00:00
## Camera
Example | Description
--- | ---
[2D top-down camera](../examples/camera/2d_top_down_camera.rs) | A 2D top-down camera smoothly following player movements
[Camera Orbit](../examples/camera/camera_orbit.rs) | Shows how to orbit a static scene using pitch, yaw, and roll.
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>
2024-06-17 15:03:31 +00:00
[First person view model](../examples/camera/first_person_view_model.rs) | A first-person camera that uses a world model and a view model with different field of views (FOV)
[Projection Zoom](../examples/camera/projection_zoom.rs) | Shows how to zoom orthographic and perspective projection cameras.
[Screen Shake](../examples/camera/2d_screen_shake.rs) | A simple 2D screen shake effect
## Dev tools
Example | Description
--- | ---
[FPS overlay](../examples/dev_tools/fps_overlay.rs) | Demonstrates FPS overlay
2020-08-17 23:02:59 +00:00
## Diagnostics
Example | Description
--- | ---
[Custom Diagnostic](../examples/diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic
[Enabling/disabling diagnostic](../examples/diagnostics/enabling_disabling_diagnostic.rs) | Shows how to disable/re-enable a Diagnostic during runtime
[Log Diagnostics](../examples/diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics, like frames per second (FPS), to the console
2020-08-17 23:02:59 +00:00
## ECS (Entity Component System)
Example | Description
--- | ---
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>
2024-07-30 12:02:38 +00:00
[Change Detection](../examples/ecs/change_detection.rs) | Change detection on components and resources
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.
2024-03-01 14:59:22 +00:00
[Component Hooks](../examples/ecs/component_hooks.rs) | Define component hooks to manage component lifecycle events
[Custom Query Parameters](../examples/ecs/custom_query_param.rs) | Groups commonly used compound queries and query filters into a single type
[Custom Schedule](../examples/ecs/custom_schedule.rs) | Demonstrates how to add custom schedules
Dynamic queries and builder API (#9774) # Objective Expand the existing `Query` API to support more dynamic use cases i.e. scripting. ## Prior Art - #6390 - #8308 - #10037 ## Solution - Create a `QueryBuilder` with runtime methods to define the set of component accesses for a built query. - Create new `WorldQueryData` implementations `FilteredEntityMut` and `FilteredEntityRef` as variants of `EntityMut` and `EntityRef` that provide run time checked access to the components included in a given query. - Add new methods to `Query` to create "query lens" with a subset of the access of the initial query. ### Query Builder The `QueryBuilder` API allows you to define a query at runtime. At it's most basic use it will simply create a query with the corresponding type signature: ```rust let query = QueryBuilder::<Entity, With<A>>::new(&mut world).build(); // is equivalent to let query = QueryState::<Entity, With<A>>::new(&mut world); ``` Before calling `.build()` you also have the opportunity to add additional accesses and filters. Here is a simple example where we add additional filter terms: ```rust let entity_a = world.spawn((A(0), B(0))).id(); let entity_b = world.spawn((A(0), C(0))).id(); let mut query_a = QueryBuilder::<Entity>::new(&mut world) .with::<A>() .without::<C>() .build(); assert_eq!(entity_a, query_a.single(&world)); ``` This alone is useful in that allows you to decide which archetypes your query will match at runtime. However it is also very limited, consider a case like the following: ```rust let query_a = QueryBuilder::<&A>::new(&mut world) // Add an additional access .data::<&B>() .build(); ``` This will grant the query an additional read access to component B however we have no way of accessing the data while iterating as the type signature still only includes &A. For an even more concrete example of this consider dynamic components: ```rust let query_a = QueryBuilder::<Entity>::new(&mut world) // Adding a filter is easy since it doesn't need be read later .with_id(component_id_a) // How do I access the data of this component? .ref_id(component_id_b) .build(); ``` With this in mind the `QueryBuilder` API seems somewhat incomplete by itself, we need some way method of accessing the components dynamically. So here's one: ### Query Transmutation If the problem is not having the component in the type signature why not just add it? This PR also adds transmute methods to `QueryBuilder` and `QueryState`. Here's a simple example: ```rust world.spawn(A(0)); world.spawn((A(1), B(0))); let mut query = QueryBuilder::<()>::new(&mut world) .with::<B>() .transmute::<&A>() .build(); query.iter(&world).for_each(|a| assert_eq!(a.0, 1)); ``` The `QueryState` and `QueryBuilder` transmute methods look quite similar but are different in one respect. Transmuting a builder will always succeed as it will just add the additional accesses needed for the new terms if they weren't already included. Transmuting a `QueryState` will panic in the case that the new type signature would give it access it didn't already have, for example: ```rust let query = QueryState::<&A, Option<&B>>::new(&mut world); /// This is fine, the access for Option<&A> is less restrictive than &A query.transmute::<Option<&A>>(&world); /// Oh no, this would allow access to &B on entities that might not have it, so it panics query.transmute::<&B>(&world); /// This is right out query.transmute::<&C>(&world); ``` This is quite an appealing API to also have available on `Query` however it does pose one additional wrinkle: In order to to change the iterator we need to create a new `QueryState` to back it. `Query` doesn't own it's own state though, it just borrows it, so we need a place to borrow it from. This is why `QueryLens` exists, it is a place to store the new state so it can be borrowed when you call `.query()` leaving you with an API like this: ```rust fn function_that_takes_a_query(query: &Query<&A>) { // ... } fn system(query: Query<(&A, &B)>) { let lens = query.transmute_lens::<&A>(); let q = lens.query(); function_that_takes_a_query(&q); } ``` Now you may be thinking: Hey, wait a second, you introduced the problem with dynamic components and then described a solution that only works for static components! Ok, you got me, I guess we need a bit more: ### Filtered Entity References Currently the only way you can access dynamic components on entities through a query is with either `EntityMut` or `EntityRef`, however these can access all components and so conflict with all other accesses. This PR introduces `FilteredEntityMut` and `FilteredEntityRef` as alternatives that have additional runtime checking to prevent accessing components that you shouldn't. This way you can build a query with a `QueryBuilder` and actually access the components you asked for: ```rust let mut query = QueryBuilder::<FilteredEntityRef>::new(&mut world) .ref_id(component_id_a) .with(component_id_b) .build(); let entity_ref = query.single(&world); // Returns Some(Ptr) as we have that component and are allowed to read it let a = entity_ref.get_by_id(component_id_a); // Will return None even though the entity does have the component, as we are not allowed to read it let b = entity_ref.get_by_id(component_id_b); ``` For the most part these new structs have the exact same methods as their non-filtered equivalents. Putting all of this together we can do some truly dynamic ECS queries, check out the `dynamic` example to see it in action: ``` Commands: comp, c Create new components spawn, s Spawn entities query, q Query for entities Enter a command with no parameters for usage. > c A, B, C, Data 4 Component A created with id: 0 Component B created with id: 1 Component C created with id: 2 Component Data created with id: 3 > s A, B, Data 1 Entity spawned with id: 0v0 > s A, C, Data 0 Entity spawned with id: 1v0 > q &Data 0v0: Data: [1, 0, 0, 0] 1v0: Data: [0, 0, 0, 0] > q B, &mut Data 0v0: Data: [2, 1, 1, 1] > q B || C, &Data 0v0: Data: [2, 1, 1, 1] 1v0: Data: [0, 0, 0, 0] ``` ## Changelog - Add new `transmute_lens` methods to `Query`. - Add new types `QueryBuilder`, `FilteredEntityMut`, `FilteredEntityRef` and `QueryLens` - `update_archetype_component_access` has been removed, archetype component accesses are now determined by the accesses set in `update_component_access` - Added method `set_access` to `WorldQuery`, this is called before `update_component_access` for queries that have a restricted set of accesses, such as those built by `QueryBuilder` or `QueryLens`. This is primarily used by the `FilteredEntity*` variants and has an empty trait implementation. - Added method `get_state` to `WorldQuery` as a fallible version of `init_state` when you don't have `&mut World` access. ## Future Work Improve performance of `FilteredEntityMut` and `FilteredEntityRef`, currently they have to determine the accesses a query has in a given archetype during iteration which is far from ideal, especially since we already did the work when matching the archetype in the first place. To avoid making more internal API changes I have left it out of this PR. --------- Co-authored-by: Mike Hsu <mike.hsu@gmail.com>
2024-01-16 19:16:49 +00:00
[Dynamic ECS](../examples/ecs/dynamic.rs) | Dynamically create components, spawn entities with those components and query those components
[ECS Guide](../examples/ecs/ecs_guide.rs) | Full guide to Bevy's ECS
[Event](../examples/ecs/event.rs) | Illustrates event creation, activation, and reception
[Fallible System Parameters](../examples/ecs/fallible_params.rs) | Systems are skipped if their parameters cannot be acquired
Fallible systems (#16589) # Objective Error handling in bevy is hard. See for reference https://github.com/bevyengine/bevy/issues/11562, https://github.com/bevyengine/bevy/issues/10874 and https://github.com/bevyengine/bevy/issues/12660. The goal of this PR is to make it better, by allowing users to optionally return `Result` from systems as outlined by Cart in <https://github.com/bevyengine/bevy/issues/14275#issuecomment-2223708314>. ## Solution This PR introduces a new `ScheuleSystem` type to represent systems that can be added to schedules. Instances of this type contain either an infallible `BoxedSystem<(), ()>` or a fallible `BoxedSystem<(), Result>`. `ScheuleSystem` implements `System<In = (), Out = Result>` and replaces all uses of `BoxedSystem` in schedules. The async executor now receives a result after executing a system, which for infallible systems is always `Ok(())`. Currently it ignores this result, but more useful error handling could also be implemented. Aliases for `Error` and `Result` have been added to the `bevy_ecs` prelude, as well as const `OK` which new users may find more friendly than `Ok(())`. ## Testing - Currently there are not actual semantics changes that really require new tests, but I added a basic one just to make sure we don't break stuff in the future. - The behavior of existing systems is totally unchanged, including logging. - All of the existing systems tests pass, and I have not noticed anything strange while playing with the examples ## Showcase The following minimal example prints "hello world" once, then completes. ```rust use bevy::prelude::*; fn main() { App::new().add_systems(Update, hello_world_system).run(); } fn hello_world_system() -> Result { println!("hello world"); Err("string")?; println!("goodbye world"); OK } ``` ## Migration Guide This change should be pretty much non-breaking, except for users who have implemented their own custom executors. Those users should use `ScheduleSystem` in place of `BoxedSystem<(), ()>` and import the `System` trait where needed. They can choose to do whatever they wish with the result. ## Current Work + [x] Fix tests & doc comments + [x] Write more tests + [x] Add examples + [X] Draft release notes ## Draft Release Notes As of this release, systems can now return results. First a bit of background: Bevy has hisotrically expected systems to return the empty type `()`. While this makes sense in the context of the ecs, it's at odds with how error handling is typically done in rust: returning `Result::Error` to indicate failure, and using the short-circuiting `?` operator to propagate that error up the call stack to where it can be properly handled. Users of functional languages will tell you this is called "monadic error handling". Not being able to return `Results` from systems left bevy users with a quandry. They could add custom error handling logic to every system, or manually pipe every system into an error handler, or perhaps sidestep the issue with some combination of fallible assignents, logging, macros, and early returns. Often, users would just litter their systems with unwraps and possible panics. While any one of these approaches might be fine for a particular user, each of them has their own drawbacks, and none makes good use of the language. Serious issues could also arrise when two different crates used by the same project made different choices about error handling. Now, by returning results, systems can defer error handling to the application itself. It looks like this: ```rust // Previous, handling internally app.add_systems(my_system) fn my_system(window: Query<&Window>) { let Ok(window) = query.get_single() else { return; }; // ... do something to the window here } // Previous, handling externally app.add_systems(my_system.pipe(my_error_handler)) fn my_system(window: Query<&Window>) -> Result<(), impl Error> { let window = query.get_single()?; // ... do something to the window here Ok(()) } // Previous, panicking app.add_systems(my_system) fn my_system(window: Query<&Window>) { let window = query.single(); // ... do something to the window here } // Now app.add_systems(my_system) fn my_system(window: Query<&Window>) -> Result { let window = query.get_single()?; // ... do something to the window here Ok(()) } ``` There are currently some limitations. Systems must either return `()` or `Result<(), Box<dyn Error + Send + Sync + 'static>>`, with no in-between. Results are also ignored by default, and though implementing a custom handler is possible, it involves writing your own custom ecs executor (which is *not* recomended). Systems should return errors when they cannot perform their normal behavior. In turn, errors returned to the executor while running the schedule will (eventually) be treated as unexpected. Users and library authors should prefer to return errors for anything that disrupts the normal expected behavior of a system, and should only handle expected cases internally. We have big plans for improving error handling further: + Allowing users to change the error handling logic of the default executors. + Adding source tracking and optional backtraces to errors. + Possibly adding tracing-levels (Error/Warn/Info/Debug/Trace) to errors. + Generally making the default error logging more helpful and inteligent. + Adding monadic system combininators for fallible systems. + Possibly removing all panicking variants from our api. --------- Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2024-12-05 22:29:06 +00:00
[Fallible Systems](../examples/ecs/fallible_systems.rs) | Systems that return results to handle errors
[Fixed Timestep](../examples/ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick
[Generic System](../examples/ecs/generic_system.rs) | Shows how to create systems that can be reused with different types
[Hierarchy](../examples/ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities
Add Immutable `Component` Support (#16372) # Objective - Fixes #16208 ## Solution - Added an associated type to `Component`, `Mutability`, which flags whether a component is mutable, or immutable. If `Mutability= Mutable`, the component is mutable. If `Mutability= Immutable`, the component is immutable. - Updated `derive_component` to default to mutable unless an `#[component(immutable)]` attribute is added. - Updated `ReflectComponent` to check if a component is mutable and, if not, panic when attempting to mutate. ## Testing - CI - `immutable_components` example. --- ## Showcase Users can now mark a component as `#[component(immutable)]` to prevent safe mutation of a component while it is attached to an entity: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` This prevents creating an exclusive reference to the component while it is attached to an entity. This is particularly powerful when combined with component hooks, as you can now fully track a component's value, ensuring whatever invariants you desire are upheld. Before this would be done my making a component private, and manually creating a `QueryData` implementation which only permitted read access. <details> <summary>Using immutable components as an index</summary> ```rust /// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)] #[component( immutable, on_insert = on_insert_name, on_replace = on_replace_name, )] pub struct Name(pub &'static str); /// This index allows for O(1) lookups of an [`Entity`] by its [`Name`]. #[derive(Resource, Default)] struct NameIndex { name_to_entity: HashMap<Name, Entity>, } impl NameIndex { fn get_entity(&self, name: &'static str) -> Option<Entity> { self.name_to_entity.get(&Name(name)).copied() } } fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.insert(name, entity); } fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.remove(&name); } // Setup our name index world.init_resource::<NameIndex>(); // Spawn some entities! let alyssa = world.spawn(Name("Alyssa")).id(); let javier = world.spawn(Name("Javier")).id(); // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Alyssa"), Some(alyssa)); assert_eq!(index.get_entity("Javier"), Some(javier)); // Changing the name of an entity is also fully capture by our index world.entity_mut(javier).insert(Name("Steven")); // Javier changed their name to Steven let steven = javier; // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Javier"), None); assert_eq!(index.get_entity("Steven"), Some(steven)); ``` </details> Additionally, users can use `Component<Mutability = ...>` in trait bounds to enforce that a component _is_ mutable or _is_ immutable. When using `Component` as a trait bound without specifying `Mutability`, any component is applicable. However, methods which only work on mutable or immutable components are unavailable, since the compiler must be pessimistic about the type. ## Migration Guide - When implementing `Component` manually, you must now provide a type for `Mutability`. The type `Mutable` provides equivalent behaviour to earlier versions of `Component`: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` - When working with generic components, you may need to specify that your generic parameter implements `Component<Mutability = Mutable>` rather than `Component` if you require mutable access to said component. - The entity entry API has had to have some changes made to minimise friction when working with immutable components. Methods which previously returned a `Mut<T>` will now typically return an `OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get the `Mut<T>` item again. ## Draft Release Notes Components can now be made immutable while stored within the ECS. Components are the fundamental unit of data within an ECS, and Bevy provides a number of ways to work with them that align with Rust's rules around ownership and borrowing. One part of this is hooks, which allow for defining custom behavior at key points in a component's lifecycle, such as addition and removal. However, there is currently no way to respond to _mutation_ of a component using hooks. The reasons for this are quite technical, but to summarize, their addition poses a significant challenge to Bevy's core promises around performance. Without mutation hooks, it's relatively trivial to modify a component in such a way that breaks invariants it intends to uphold. For example, you can use `core::mem::swap` to swap the components of two entities, bypassing the insertion and removal hooks. This means the only way to react to this modification is via change detection in a system, which then begs the question of what happens _between_ that alteration and the next run of that system? Alternatively, you could make your component private to prevent mutation, but now you need to provide commands and a custom `QueryData` implementation to allow users to interact with your component at all. Immutable components solve this problem by preventing the creation of an exclusive reference to the component entirely. Without an exclusive reference, the only way to modify an immutable component is via removal or replacement, which is fully captured by component hooks. To make a component immutable, simply add `#[component(immutable)]`: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` When implementing `Component` manually, there is an associated type `Mutability` which controls this behavior: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` Note that this means when working with generic components, you may need to specify that a component is mutable to gain access to certain methods: ```rust // Before fn bar<C: Component>() { // ... } // After fn bar<C: Component<Mutability = Mutable>>() { // ... } ``` With this new tool, creating index components, or caching data on an entity should be more user friendly, allowing libraries to provide APIs relying on components and hooks to uphold their invariants. ## Notes - ~~I've done my best to implement this feature, but I'm not happy with how reflection has turned out. If any reflection SMEs know a way to improve this situation I'd greatly appreciate it.~~ There is an outstanding issue around the fallibility of mutable methods on `ReflectComponent`, but the DX is largely unchanged from `main` now. - I've attempted to prevent all safe mutable access to a component that does not implement `Component<Mutability = Mutable>`, but there may still be some methods I have missed. Please indicate so and I will address them, as they are bugs. - Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever you do with unsafe is between you and your compiler. - I am marking this PR as ready, but I suspect it will undergo fairly major revisions based on SME feedback. - I've marked this PR as _Uncontroversial_ based on the feature, not the implementation. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Nuutti Kotivuori <naked@iki.fi>
2024-12-05 14:27:48 +00:00
[Immutable Components](../examples/ecs/immutable_components.rs) | Demonstrates the creation and utility of immutable components
[Iter Combinations](../examples/ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results
[Nondeterministic System Order](../examples/ecs/nondeterministic_system_order.rs) | Systems run in parallel, but their order isn't always deterministic. Here's how to detect and fix this.
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>
2024-07-15 13:39:41 +00:00
[Observer Propagation](../examples/ecs/observer_propagation.rs) | Demonstrates event propagation with observers
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>
2024-06-15 01:33:26 +00:00
[Observers](../examples/ecs/observers.rs) | Demonstrates observers that react to events (both built-in life-cycle events and custom events)
One Shot Systems (#8963) I'm adopting this ~~child~~ PR. # Objective - Working with exclusive world access is not always easy: in many cases, a standard system or three is more ergonomic to write, and more modularly maintainable. - For small, one-off tasks (commonly handled with scripting), running an event-reader system incurs a small but flat overhead cost and muddies the schedule. - Certain forms of logic (e.g. turn-based games) want very fine-grained linear and/or branching control over logic. - SystemState is not automatically cached, and so performance can suffer and change detection breaks. - Fixes https://github.com/bevyengine/bevy/issues/2192. - Partial workaround for https://github.com/bevyengine/bevy/issues/279. ## Solution - Adds a SystemRegistry resource to the World, which stores initialized systems keyed by their SystemSet. - Allows users to call world.run_system(my_system) and commands.run_system(my_system), without re-initializing or losing state (essential for change detection). - Add a Callback type to enable convenient use of dynamic one shot systems and reduce the mental overhead of working with Box<dyn SystemSet>. - Allow users to run systems based on their SystemSet, enabling more complex user-made abstractions. ## Future work - Parameterized one-shot systems would improve reusability and bring them closer to events and commands. The API could be something like run_system_with_input(my_system, my_input) and use the In SystemParam. - We should evaluate the unification of commands and one-shot systems since they are two different ways to run logic on demand over a World. ### Prior attempts - https://github.com/bevyengine/bevy/pull/2234 - https://github.com/bevyengine/bevy/pull/2417 - https://github.com/bevyengine/bevy/pull/4090 - https://github.com/bevyengine/bevy/pull/7999 This PR continues the work done in https://github.com/bevyengine/bevy/pull/7999. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Federico Rinaldi <gisquerin@gmail.com> Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Alejandro Pascual Pozo <alejandro.pascual.pozo@gmail.com> Co-authored-by: Rob Parrett <robparrett@gmail.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: Dmytro Banin <banind@cs.washington.edu> Co-authored-by: James Liu <contact@jamessliu.com>
2023-09-19 20:17:05 +00:00
[One Shot Systems](../examples/ecs/one_shot_systems.rs) | Shows how to flexibly run systems without scheduling them
[Parallel Query](../examples/ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator`
Migrate engine to Schedule v3 (#7267) Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR. # Objective - Followup #6587. - Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45 ## Solution - [x] Remove old scheduling module - [x] Migrate new methods to no longer use extension methods - [x] Fix compiler errors - [x] Fix benchmarks - [x] Fix examples - [x] Fix docs - [x] Fix tests ## Changelog ### Added - a large number of methods on `App` to work with schedules ergonomically - the `CoreSchedule` enum - `App::add_extract_system` via the `RenderingAppExtension` trait extension method - the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms` ### Removed - stages, and all code that mentions stages - states have been dramatically simplified, and no longer use a stack - `RunCriteriaLabel` - `AsSystemLabel` trait - `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition) - systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world - `RunCriteriaLabel` - `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear. ### Changed - `System::default_labels` is now `System::default_system_sets`. - `App::add_default_labels` is now `App::add_default_sets` - `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet` - `App::add_system_set` was renamed to `App::add_systems` - The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum - `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)` - `SystemLabel` trait was replaced by `SystemSet` - `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>` - The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq` - Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria. - Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. - `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`. - `bevy_pbr::add_clusters` is no longer an exclusive system - the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling` - `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread. ## Migration Guide - Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)` - Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed. - The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved. - Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior. - Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you. - For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with - `add_system(my_system.in_set(CoreSet::PostUpdate)` - When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages - Run criteria have been renamed to run conditions. These can now be combined with each other and with states. - Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow. - For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label. - Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default. - Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually. - Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior. - the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity - `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl. - Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings. - `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds. - `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool. - States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set` ## TODO - [x] remove dead methods on App and World - [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule` - [x] avoid adding the default system set at inappropriate times - [x] remove any accidental cycles in the default plugins schedule - [x] migrate benchmarks - [x] expose explicit labels for the built-in command flush points - [x] migrate engine code - [x] remove all mentions of stages from the docs - [x] verify docs for States - [x] fix uses of exclusive systems that use .end / .at_start / .before_commands - [x] migrate RenderStage and AssetStage - [x] migrate examples - [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub) - [x] ensure that on_enter schedules are run at least once before the main app - [x] re-enable opt-in to execution order ambiguities - [x] revert change to `update_bounds` to ensure it runs in `PostUpdate` - [x] test all examples - [x] unbreak directional lights - [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples) - [x] game menu example shows loading screen and menu simultaneously - [x] display settings menu is a blank screen - [x] `without_winit` example panics - [x] ensure all tests pass - [x] SubApp doc test fails - [x] runs_spawn_local tasks fails - [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120) ## Points of Difficulty and Controversy **Reviewers, please give feedback on these and look closely** 1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup. 2. The outer schedule controls which schedule is run when `App::update` is called. 3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes. 4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset. 5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order 6. Implemetnation strategy for fixed timesteps 7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks. 8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements. ## Future Work (ideally before 0.10) - Rename schedule_v3 module to schedule or scheduling - Add a derive macro to states, and likely a `EnumIter` trait of some form - Figure out what exactly to do with the "systems added should basically work by default" problem - Improve ergonomics for working with fixed timesteps and states - Polish FixedTime API to match Time - Rebase and merge #7415 - Resolve all internal ambiguities (blocked on better tools, especially #7442) - Add "base sets" to replace the removed default sets.
2023-02-06 02:04:50 +00:00
[Removal Detection](../examples/ecs/removal_detection.rs) | Query for entities that had a specific component removed earlier in the current frame
[Run Conditions](../examples/ecs/run_conditions.rs) | Run systems only when one or multiple conditions are met
[Send and receive events](../examples/ecs/send_and_receive_events.rs) | Demonstrates how to send and receive events of the same type in a single system
[Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
[System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state
[System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully
[System Stepping](../examples/ecs/system_stepping.rs) | Demonstrate stepping through systems in order of execution.
2020-08-17 23:02:59 +00:00
## Games
Example | Description
--- | ---
[Alien Cake Addict](../examples/games/alien_cake_addict.rs) | Eat the cakes. Eat them all. An example 3D game
[Breakout](../examples/games/breakout.rs) | An implementation of the classic game "Breakout".
[Contributors](../examples/games/contributors.rs) | Displays each contributor as a bouncy bevy-ball!
[Desk Toy](../examples/games/desk_toy.rs) | Bevy logo as a desk toy using transparent windows! Now with Googly Eyes!
[Game Menu](../examples/games/game_menu.rs) | A simple game menu
[Loading Screen](../examples/games/loading_screen.rs) | Demonstrates how to create a loading screen that waits for all assets to be loaded and render pipelines to be compiled.
2020-08-17 23:02:59 +00:00
## Gizmos
Example | Description
--- | ---
[2D Gizmos](../examples/gizmos/2d_gizmos.rs) | A scene showcasing 2D gizmos
[3D Gizmos](../examples/gizmos/3d_gizmos.rs) | A scene showcasing 3D gizmos
[Axes](../examples/gizmos/axes.rs) | Demonstrates the function of axes gizmos
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: ![without-radius](https://github.com/bevyengine/bevy/assets/18357657/699d0154-f367-4727-9b09-8b458d96a0e2) With Radius: ![with-radius](https://github.com/bevyengine/bevy/assets/18357657/f3af003e-dbba-427a-a305-c5cc1676e340) 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>
2024-03-03 18:50:46 +00:00
[Light Gizmos](../examples/gizmos/light_gizmos.rs) | A scene showcasing light gizmos
2020-08-17 23:02:59 +00:00
## Input
Example | Description
--- | ---
[Char Input Events](../examples/input/char_input_events.rs) | Prints out all chars as they are inputted
[Gamepad Input](../examples/input/gamepad_input.rs) | Shows handling of gamepad input, connections, and disconnections
[Gamepad Input Events](../examples/input/gamepad_input_events.rs) | Iterates and prints gamepad input and connection events
[Gamepad Rumble](../examples/input/gamepad_rumble.rs) | Shows how to rumble a gamepad using force feedback
[Keyboard Input](../examples/input/keyboard_input.rs) | Demonstrates handling a key press/release
[Keyboard Input Events](../examples/input/keyboard_input_events.rs) | Prints out all keyboard events
[Keyboard Modifiers](../examples/input/keyboard_modifiers.rs) | Demonstrates using key modifiers (ctrl, shift)
[Mouse Grab](../examples/input/mouse_grab.rs) | Demonstrates how to grab the mouse, locking the cursor to the app's screen
[Mouse Input](../examples/input/mouse_input.rs) | Demonstrates handling a mouse button press/release
[Mouse Input Events](../examples/input/mouse_input_events.rs) | Prints out all mouse events (buttons, movement, etc.)
[Text Input](../examples/input/text_input.rs) | Simple text input with IME support
[Touch Input](../examples/input/touch_input.rs) | Displays touch presses, releases, and cancels
[Touch Input Events](../examples/input/touch_input_events.rs) | Prints out all touch inputs
2020-08-17 23:02:59 +00:00
## Math
Example | Description
--- | ---
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.
2024-07-17 13:02:31 +00:00
[Cubic Splines](../examples/math/cubic_splines.rs) | Exhibits different modes of constructing cubic curves using splines
[Custom Primitives](../examples/math/custom_primitives.rs) | Demonstrates how to add custom primitives and useful traits for them.
2024-05-27 13:46:11 +00:00
[Random Sampling](../examples/math/random_sampling.rs) | Demonstrates how to sample random points from mathematical primitives
[Rendering Primitives](../examples/math/render_primitives.rs) | Shows off rendering for all math primitives as both Meshes and Gizmos
[Sampling Primitives](../examples/math/sampling_primitives.rs) | Demonstrates all the primitives which can be sampled.
[Smooth Follow](../examples/movement/smooth_follow.rs) | Demonstrates how to make an entity smoothly follow another using interpolation
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.
2024-07-09 14:23:10 +00:00
## Movement
Example | Description
--- | ---
[Run physics in a fixed timestep](../examples/movement/physics_in_fixed_timestep.rs) | Handles input, physics, and rendering in an industry-standard way by using a fixed timestep
## Picking
Example | Description
--- | ---
Add mesh picking backend and `MeshRayCast` system parameter (#15800) # Objective Closes #15545. `bevy_picking` supports UI and sprite picking, but not mesh picking. Being able to pick meshes would be extremely useful for various games, tools, and our own examples, as well as scene editors and inspectors. So, we need a mesh picking backend! Luckily, [`bevy_mod_picking`](https://github.com/aevyrie/bevy_mod_picking) (which `bevy_picking` is based on) by @aevyrie already has a [backend for it](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) using [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast). As a side product of adding mesh picking, we also get support for performing ray casts on meshes! ## Solution Upstream a large chunk of the immediate-mode ray casting functionality from `bevy_mod_raycast`, and add a mesh picking backend based on `bevy_mod_picking`. Huge thanks to @aevyrie who did all the hard work on these incredible crates! All meshes are pickable by default. Picking can be disabled for individual entities by adding `PickingBehavior::IGNORE`, like normal. Or, if you want mesh picking to be entirely opt-in, you can set `MeshPickingBackendSettings::require_markers` to `true` and add a `RayCastPickable` component to the desired camera and target entities. You can also use the new `MeshRayCast` system parameter to cast rays into the world manually: ```rust fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); // Only ray cast against entities with the `Foo` component. let filter = |entity| foo_query.contains(entity); // Never early-exit. Note that you can change behavior per-entity. let early_exit_test = |_entity| false; // Ignore the visibility of entities. This allows ray casting hidden entities. let visibility = RayCastVisibility::Any; let settings = RayCastSettings::default() .with_filter(&filter) .with_early_exit_test(&early_exit_test) .with_visibility(visibility); // Cast the ray with the settings, returning a list of intersections. let hits = ray_cast.cast_ray(ray, &settings); } ``` This is largely a direct port, but I did make several changes to match our APIs better, remove things we don't need or that I think are unnecessary, and do some general improvements to code quality and documentation. ### Changes Relative to `bevy_mod_raycast` and `bevy_mod_picking` - Every `Raycast` and "raycast" has been renamed to `RayCast` and "ray cast" (similar reasoning as the "Naming" section in #15724) - `Raycast` system param has been renamed to `MeshRayCast` to avoid naming conflicts and to be explicit that it is not for colliders - `RaycastBackend` has been renamed to `MeshPickingBackend` - `RayCastVisibility` variants are now `Any`, `Visible`, and `VisibleInView` instead of `Ignore`, `MustBeVisible`, and `MustBeVisibleAndInView` - `NoBackfaceCulling` has been renamed to `RayCastBackfaces`, to avoid implying that it affects the rendering of backfaces for meshes (it doesn't) - `SimplifiedMesh` and `RayCastBackfaces` live near other ray casting API types, not in their own 10 LoC module - All intersection logic and types are in the same `intersections` module, not split across several modules - Some intersection types have been renamed to be clearer and more consistent - `IntersectionData` -> `RayMeshHit` - `RayHit` -> `RayTriangleHit` - General documentation and code quality improvements ### Removed / Not Ported - Removed unused ray helpers and types, like `PrimitiveIntersection` - Removed getters on intersection types, and made their properties public - There is no `2d` feature, and `Raycast::mesh_query` and `Raycast::mesh2d_query` have been merged into `MeshRayCast::mesh_query`, which handles both 2D and 3D - I assume this existed previously because `Mesh2dHandle` used to be in `bevy_sprite`. Now both the 2D and 3D mesh are in `bevy_render`. - There is no `debug` feature or ray debug rendering - There is no deferred API (`RaycastSource`) - There is no `CursorRayPlugin` (the picking backend handles this) ### Note for Reviewers In case it's helpful, the [first commit](https://github.com/bevyengine/bevy/commit/281638ef10ebd25f783bee2533509fdf756f7ab4) here is essentially a one-to-one port. The rest of the commits are primarily refactoring and cleaning things up in the ways listed earlier, as well as changes to the module structure. It may also be useful to compare the original [picking backend](https://github.com/aevyrie/bevy_mod_picking/blob/74f0c3c0fbc8048632ba46fd8f14e26aaea9c76c/backends/bevy_picking_raycast/src/lib.rs) and [`bevy_mod_raycast`](https://github.com/aevyrie/bevy_mod_raycast) to this PR. Feel free to mention if there are any changes that I should revert or something I should not include in this PR. ## Testing I tested mesh picking and relevant components in some examples, for both 2D and 3D meshes, and added a new `mesh_picking` example. I also ~~stole~~ ported over the [ray-mesh intersection benchmark](https://github.com/aevyrie/bevy_mod_raycast/blob/dbc5ef32fe48997a1a7eeec7434d9dd8b829e52e/benches/ray_mesh_intersection.rs) from `bevy_mod_raycast`. --- ## Showcase Below is a version of the `2d_shapes` example modified to demonstrate 2D mesh picking. This is not included in this PR. https://github.com/user-attachments/assets/7742528c-8630-4c00-bacd-81576ac432bf And below is the new `mesh_picking` example: https://github.com/user-attachments/assets/b65c7a5a-fa3a-4c2d-8bbd-e7a2c772986e There is also a really cool new `mesh_ray_cast` example ported over from `bevy_mod_raycast`: https://github.com/user-attachments/assets/3c5eb6c0-bd94-4fb0-bec6-8a85668a06c9 --------- Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Trent <2771466+tbillington@users.noreply.github.com> Co-authored-by: François Mockers <mockersf@gmail.com>
2024-10-13 17:24:19 +00:00
[Mesh Picking](../examples/picking/mesh_picking.rs) | Demonstrates picking meshes
[Showcases simple picking events and usage](../examples/picking/simple_picking.rs) | Demonstrates how to use picking events to spawn simple objects
[Sprite Picking](../examples/picking/sprite_picking.rs) | Demonstrates picking sprites and sprite atlases
2020-12-02 22:35:27 +00:00
## Reflection
Example | Description
--- | ---
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")]`)
2024-05-20 19:30:21 +00:00
[Custom Attributes](../examples/reflection/custom_attributes.rs) | Registering and accessing custom attributes on reflected types
[Dynamic Types](../examples/reflection/dynamic_types.rs) | How dynamic types are used with reflection
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: https://github.com/bevyengine/bevy/blob/ba727898f2adff817838fc4cdb49871bbce37356/examples/reflection/function_reflection.rs#L1-L157 #### Details Added the following items: - `ArgError` enum - `ArgId` enum - `ArgInfo` struct - `ArgList` struct - `Arg` enum - `DynamicFunction` struct - `FromArg` trait (derived with `derive(Reflect)`) - `FunctionError` enum - `FunctionInfo` struct - `FunctionResult` alias - `GetOwnership` trait (derived with `derive(Reflect)`) - `IntoFunction` trait (with blanket implementation) - `IntoReturn` trait (derived with `derive(Reflect)`) - `Ownership` enum - `ReturnInfo` struct - `Return` enum --------- Co-authored-by: Periwink <charlesbour@gmail.com>
2024-07-01 13:49:08 +00:00
[Function Reflection](../examples/reflection/function_reflection.rs) | Demonstrates how functions can be called dynamically using reflection
[Generic Reflection](../examples/reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection
[Reflection](../examples/reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types
[Reflection Types](../examples/reflection/reflection_types.rs) | Illustrates the various reflection types available
[Type Data](../examples/reflection/type_data.rs) | Demonstrates how to create and use type data
2020-12-02 22:35:27 +00:00
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::cool::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>
2024-09-23 18:36:16 +00:00
## Remote Protocol
Example | Description
--- | ---
[client](../examples/remote/client.rs) | A simple command line client that can control Bevy apps via the BRP
[server](../examples/remote/server.rs) | A Bevy app that you can connect to with the BRP and edit
2020-08-17 23:02:59 +00:00
## Scene
Example | Description
--- | ---
[Scene](../examples/scene/scene.rs) | Demonstrates loading from and saving scenes to files
2020-08-17 23:02:59 +00:00
## Shaders
These examples demonstrate how to implement different shaders in user code.
A shader in its most common usage is a small program that is run by the GPU per-vertex in a mesh (a vertex shader) or per-affected-screen-fragment (a fragment shader.) The GPU executes these programs in a highly parallel way.
There are also compute shaders which are used for more general processing leveraging the GPU's parallelism.
Example | Description
--- | ---
[Animated](../examples/shader/animate_shader.rs) | A shader that uses dynamic data like the time since startup
[Array Texture](../examples/shader/array_texture.rs) | A shader that shows how to reuse the core bevy PBR shading functionality in a custom material that obtains the base color from an array texture.
[Compute - Game of Life](../examples/shader/compute_shader_game_of_life.rs) | A compute shader that simulates Conway's Game of Life
[Custom Vertex Attribute](../examples/shader/custom_vertex_attribute.rs) | A shader that reads a mesh's custom vertex attribute
2024-06-27 16:13:03 +00:00
[Custom phase item](../examples/shader/custom_phase_item.rs) | Demonstrates how to enqueue custom draw commands in a render phase
[Extended Material](../examples/shader/extended_material.rs) | A custom shader that builds on the standard material
[GPU readback](../examples/shader/gpu_readback.rs) | A very simple compute shader that writes to a buffer that is read by the cpu
[Instancing](../examples/shader/custom_shader_instancing.rs) | A shader that renders a mesh multiple times in one draw call using low level rendering api
[Instancing](../examples/shader/automatic_instancing.rs) | Shows that multiple instances of a cube are automatically instanced in one draw call
[Material](../examples/shader/shader_material.rs) | A shader and a material that uses it
[Material](../examples/shader/shader_material_2d.rs) | A shader and a material that uses it on a 2d mesh
Add a bindless mode to `AsBindGroup`. (#16368) This patch adds the infrastructure necessary for Bevy to support *bindless resources*, by adding a new `#[bindless]` attribute to `AsBindGroup`. Classically, only a single texture (or sampler, or buffer) can be attached to each shader binding. This means that switching materials requires breaking a batch and issuing a new drawcall, even if the mesh is otherwise identical. This adds significant overhead not only in the driver but also in `wgpu`, as switching bind groups increases the amount of validation work that `wgpu` must do. *Bindless resources* are the typical solution to this problem. Instead of switching bindings between each texture, the renderer instead supplies a large *array* of all textures in the scene up front, and the material contains an index into that array. This pattern is repeated for buffers and samplers as well. The renderer now no longer needs to switch binding descriptor sets while drawing the scene. Unfortunately, as things currently stand, this approach won't quite work for Bevy. Two aspects of `wgpu` conspire to make this ideal approach unacceptably slow: 1. In the DX12 backend, all binding arrays (bindless resources) must have a constant size declared in the shader, and all textures in an array must be bound to actual textures. Changing the size requires a recompile. 2. Changing even one texture incurs revalidation of all textures, a process that takes time that's linear in the total size of the binding array. This means that declaring a large array of textures big enough to encompass the entire scene is presently unacceptably slow. For example, if you declare 4096 textures, then `wgpu` will have to revalidate all 4096 textures if even a single one changes. This process can take multiple frames. To work around this problem, this PR groups bindless resources into small *slabs* and maintains a free list for each. The size of each slab for the bindless arrays associated with a material is specified via the `#[bindless(N)]` attribute. For instance, consider the following declaration: ```rust #[derive(AsBindGroup)] #[bindless(16)] struct MyMaterial { #[buffer(0)] color: Vec4, #[texture(1)] #[sampler(2)] diffuse: Handle<Image>, } ``` The `#[bindless(N)]` attribute specifies that, if bindless arrays are supported on the current platform, each resource becomes a binding array of N instances of that resource. So, for `MyMaterial` above, the `color` attribute is exposed to the shader as `binding_array<vec4<f32>, 16>`, the `diffuse` texture is exposed to the shader as `binding_array<texture_2d<f32>, 16>`, and the `diffuse` sampler is exposed to the shader as `binding_array<sampler, 16>`. Inside the material's vertex and fragment shaders, the applicable index is available via the `material_bind_group_slot` field of the `Mesh` structure. So, for instance, you can access the current color like so: ```wgsl // `uniform` binding arrays are a non-sequitur, so `uniform` is automatically promoted // to `storage` in bindless mode. @group(2) @binding(0) var<storage> material_color: binding_array<Color, 4>; ... @fragment fn fragment(in: VertexOutput) -> @location(0) vec4<f32> { let color = material_color[mesh[in.instance_index].material_bind_group_slot]; ... } ``` Note that portable shader code can't guarantee that the current platform supports bindless textures. Indeed, bindless mode is only available in Vulkan and DX12. The `BINDLESS` shader definition is available for your use to determine whether you're on a bindless platform or not. Thus a portable version of the shader above would look like: ```wgsl #ifdef BINDLESS @group(2) @binding(0) var<storage> material_color: binding_array<Color, 4>; #else // BINDLESS @group(2) @binding(0) var<uniform> material_color: Color; #endif // BINDLESS ... @fragment fn fragment(in: VertexOutput) -> @location(0) vec4<f32> { #ifdef BINDLESS let color = material_color[mesh[in.instance_index].material_bind_group_slot]; #else // BINDLESS let color = material_color; #endif // BINDLESS ... } ``` Importantly, this PR *doesn't* update `StandardMaterial` to be bindless. So, for example, `scene_viewer` will currently not run any faster. I intend to update `StandardMaterial` to use bindless mode in a follow-up patch. A new example, `shaders/shader_material_bindless`, has been added to demonstrate how to use this new feature. Here's a Tracy profile of `submit_graph_commands` of this patch and an additional patch (not submitted yet) that makes `StandardMaterial` use bindless. Red is those patches; yellow is `main`. The scene was Bistro Exterior with a hack that forces all textures to opaque. You can see a 1.47x mean speedup. ![Screenshot 2024-11-12 161713](https://github.com/user-attachments/assets/4334b362-42c8-4d64-9cfb-6835f019b95c) ## Migration Guide * `RenderAssets::prepare_asset` now takes an `AssetId` parameter. * Bin keys now have Bevy-specific material bind group indices instead of `wgpu` material bind group IDs, as part of the bindless change. Use the new `MaterialBindGroupAllocator` to map from bind group index to bind group ID.
2024-12-03 18:00:34 +00:00
[Material - Bindless](../examples/shader/shader_material_bindless.rs) | Demonstrates how to make materials that use bindless textures
[Material - GLSL](../examples/shader/shader_material_glsl.rs) | A shader that uses the GLSL shading language
[Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates
[Material Prepass](../examples/shader/shader_prepass.rs) | A shader that uses the various textures generated by the prepass
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 ![Screenshot 2024-06-04 180304](https://github.com/bevyengine/bevy/assets/157897/3631c64f-a615-44fe-91ca-7f04df0a54b2) ![Screenshot 2024-06-04 180743](https://github.com/bevyengine/bevy/assets/157897/ee055cbf-4314-49c5-8bfa-8d8a17bd52bb) ## Changelog ### Added * Chromatic aberration is now available as a built-in postprocessing effect. To use it, add `ChromaticAberration` to your camera.
2024-07-15 13:59:02 +00:00
[Post Processing - Custom Render Pass](../examples/shader/custom_post_processing.rs) | A custom post processing effect, using a custom render pass that runs after the main pass
[Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader)
[Specialized Mesh Pipeline](../examples/shader/specialized_mesh_pipeline.rs) | Demonstrates how to write a specialized mesh pipeline
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>
2024-09-02 16:46:34 +00:00
[Storage Buffer](../examples/shader/storage_buffer.rs) | A shader that shows how to bind a storage buffer using a custom material.
[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures).
2020-08-17 23:02:59 +00:00
## State
Example | Description
--- | ---
[Computed States](../examples/state/computed_states.rs) | Advanced state patterns using Computed States.
[Custom State Transition Behavior](../examples/state/custom_transitions.rs) | Creating and working with custom state transition schedules.
[States](../examples/state/states.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state.
[Sub States](../examples/state/sub_states.rs) | Using Sub States for hierarchical state handling.
## Stress Tests
These examples are used to test the performance and stability of various parts of the engine in an isolated way.
Due to the focus on performance it's recommended to run the stress tests in release mode:
```sh
cargo run --release --example <example name>
```
Example | Description
--- | ---
[Bevymark](../examples/stress_tests/bevymark.rs) | A heavy sprite rendering workload to benchmark your system with Bevy
[Many Animated Sprites](../examples/stress_tests/many_animated_sprites.rs) | Displays many animated sprites in a grid arrangement with slight offsets to their animation timers. Used for performance testing.
[Many Buttons](../examples/stress_tests/many_buttons.rs) | Test rendering of many UI elements
Do not re-check visibility or re-render shadow maps for point and spot lights for each view (#15156) # Objective _If I understand it correctly_, we were checking mesh visibility, as well as re-rendering point and spot light shadow maps for each view. This makes it so that M views and N lights produce M x N complexity. This PR aims to fix that, as well as introduce a stress test for this specific scenario. ## Solution - Keep track of what lights have already had mesh visibility calculated and do not calculate it again; - Reuse shadow depth textures and attachments across all views, and only render shadow maps for the _first_ time a light is encountered on a view; - Directional lights remain unaltered, since their shadow map cascades are view-dependent; - Add a new `many_cameras_lights` stress test example to verify the solution ## Showcase 110% speed up on the stress test 83% reduction of memory usage in stress test ### Before (5.35 FPS on stress test) <img width="1392" alt="Screenshot 2024-09-11 at 12 25 57" src="https://github.com/user-attachments/assets/136b0785-e9a4-44df-9a22-f99cc465e126"> ### After (11.34 FPS on stress test) <img width="1392" alt="Screenshot 2024-09-11 at 12 24 35" src="https://github.com/user-attachments/assets/b8dd858f-5e19-467f-8344-2b46ca039630"> ## Testing - Did you test these changes? If so, how? - On my game project where I have two cameras, and many shadow casting lights I managed to get pretty much double the FPS. - Also included a stress test, see the comparison above - Are there any parts that need more testing? - Yes, I would like help verifying that this fix is indeed correct, and that we were really re-rendering the shadow maps by mistake and it's indeed okay to not do that - How can other people (reviewers) test your changes? Is there anything specific they need to know? - Run the `many_cameras_lights` example - On the `main` branch, cherry pick the commit with the example (`git cherry-pick --no-commit 1ed4ace01`) and run it - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - macOS --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
2024-11-11 18:49:09 +00:00
[Many Cameras & Lights](../examples/stress_tests/many_cameras_lights.rs) | Test rendering of many cameras and lights
[Many Cubes](../examples/stress_tests/many_cubes.rs) | Simple benchmark to test per-entity draw overhead. Run with the `sphere` argument to test frustum culling
[Many Foxes](../examples/stress_tests/many_foxes.rs) | Loads an animated fox model and spawns lots of them. Good for testing skinned mesh performance. Takes an unsigned integer argument for the number of foxes to spawn. Defaults to 1000
Immediate Mode Line/Gizmo Drawing (#6529) # Objective Add a convenient immediate mode drawing API for visual debugging. Fixes #5619 Alternative to #1625 Partial alternative to #5734 Based off https://github.com/Toqozz/bevy_debug_lines with some changes: * Simultaneous support for 2D and 3D. * Methods for basic shapes; circles, spheres, rectangles, boxes, etc. * 2D methods. * Removed durations. Seemed niche, and can be handled by users. <details> <summary>Performance</summary> Stress tested using Bevy's recommended optimization settings for the dev profile with the following command. ```bash cargo run --example many_debug_lines \ --config "profile.dev.package.\"*\".opt-level=3" \ --config "profile.dev.opt-level=1" ``` I dipped to 65-70 FPS at 300,000 lines CPU: 3700x RAM Speed: 3200 Mhz GPU: 2070 super - probably not very relevant, mostly cpu/memory bound </details> <details> <summary>Fancy bloom screenshot</summary> ![Screenshot_20230207_155033](https://user-images.githubusercontent.com/29694403/217291980-f1e0500e-7a14-4131-8c96-eaaaf52596ae.png) </details> ## Changelog * Added `GizmoPlugin` * Added `Gizmos` system parameter for drawing lines and wireshapes. ### TODO - [ ] Update changelog - [x] Update performance numbers - [x] Add credit to PR description ### Future work - Cache rendering primitives instead of constructing them out of line segments each frame. - Support for drawing solid meshes - Interactions. (See [bevy_mod_gizmos](https://github.com/LiamGallagher737/bevy_mod_gizmos)) - Fancier line drawing. (See [bevy_polyline](https://github.com/ForesightMiningSoftwareCorporation/bevy_polyline)) - Support for `RenderLayers` - Display gizmos for a certain duration. Currently everything displays for one frame (ie. immediate mode) - Changing settings per drawn item like drawing on top or drawing to different `RenderLayers` Co-Authored By: @lassade <felipe.jorge.pereira@gmail.com> Co-Authored By: @The5-1 <agaku@hotmail.de> Co-Authored By: @Toqozz <toqoz@hotmail.com> Co-Authored By: @nicopap <nico@nicopap.ch> --------- Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: IceSentry <c.giguere42@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-03-20 20:57:54 +00:00
[Many Gizmos](../examples/stress_tests/many_gizmos.rs) | Test rendering of many gizmos
[Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering.
[Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights
[Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites.
[Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark
[Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance
## Time
Example | Description
--- | ---
[Time handling](../examples/time/time.rs) | Explains how Time is handled in ECS
[Timers](../examples/time/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state
[Virtual time](../examples/time/virtual_time.rs) | Shows how `Time<Virtual>` can be used to pause, resume, slow down and speed up a game.
## Tools
Example | Description
--- | ---
[Gamepad Viewer](../examples/tools/gamepad_viewer.rs) | Shows a visualization of gamepad buttons, sticks, and triggers
[Scene Viewer](../examples/tools/scene_viewer/main.rs) | A simple way to view glTF models with Bevy. Just run `cargo run --release --example scene_viewer /path/to/model.gltf#Scene0`, replacing the path as appropriate. With no arguments it will load the FieldHelmet glTF model from the repository assets subdirectory
## Transforms
Example | Description
--- | ---
[3D Rotation](../examples/transforms/3d_rotation.rs) | Illustrates how to (constantly) rotate an object around an axis
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`).
2024-03-14 14:55:55 +00:00
[Alignment](../examples/transforms/align.rs) | A demonstration of Transform's axis-alignment feature
[Scale](../examples/transforms/scale.rs) | Illustrates how to scale an object in each direction
[Transform](../examples/transforms/transform.rs) | Shows multiple transformations of objects
[Translation](../examples/transforms/translation.rs) | Illustrates how to move an object along an axis
2020-08-17 23:02:59 +00:00
## UI (User Interface)
Example | Description
--- | ---
[Borders](../examples/ui/borders.rs) | Demonstrates how to create a node with a border
box shadow (#15204) # Objective UI box shadow support Adds a new component `BoxShadow`: ```rust pub struct BoxShadow { /// The shadow's color pub color: Color, /// Horizontal offset pub x_offset: Val, /// Vertical offset pub y_offset: Val, /// Horizontal difference in size from the occluding uninode pub spread_radius: Val, /// Blurriness of the shadow pub blur_radius: Val, } ``` To use `BoxShadow`, add the component to any Bevy UI node and a shadow will be drawn beneath that node. Also adds a resource `BoxShadowSamples` that can be used to adjust the shadow quality. #### Notes * I'm not super happy with the field names. Maybe we need a `struct Size { width: Val, height: Val }` type or something. * The shader isn't very optimised but I don't see that it's too important for now as the number of shadows being rendered is not going to be massive most of the time. I think it's more important to get the API and geometry correct with this PR. * I didn't implement an inset property, it's not essential and can easily be added in a follow up. * Shadows are only rendered for uinodes, not for images or text. * Batching isn't supported, it would need out-of-the-scope-of-this-pr changes to the way the UI handles z-ordering for it to be effective. # Showcase ```cargo run --example box_shadow -- --samples 4``` <img width="391" alt="br" src="https://github.com/user-attachments/assets/4e8add96-dc93-46e0-9e35-d995eb0943ad"> ```cargo run --example box_shadow -- --samples 10``` <img width="391" alt="s10" src="https://github.com/user-attachments/assets/ecb384c9-4012-4cd6-9dea-5180904bf28e">
2024-10-08 16:26:17 +00:00
[Box Shadow](../examples/ui/box_shadow.rs) | Demonstrates how to create a node with a shadow
[Button](../examples/ui/button.rs) | Illustrates creating and updating a button
Add CSS Grid support to `bevy_ui` (#8026) # Objective An easy way to create 2D grid layouts ## Solution Enable the `grid` feature in Taffy and add new style types for defining grids. ## Notes - ~I'm having a bit of trouble getting `#[derive(Reflect)]` to work properly. Help with that would be appreciated (EDIT: got it to compile by ignoring the problematic fields, but this presumably can't be merged).~ This is now fixed - ~The alignment types now have a `Normal` variant because I couldn't get reflect to work with `Option`.~ I've decided to stick with the flattened variant, as it saves a level of wrapping when authoring styles. But I've renamed the variants from `Normal` to `Default`. - ~This currently exposes a simplified API on top of grid. In particular the following is not currently supported:~ - ~Negative grid indices~ Now supported. - ~Custom `end` values for grid placement (you can only use `start` and `span`)~ Now supported - ~`minmax()` track sizing functions~ minmax is now support through a `GridTrack::minmax()` constructor - ~`repeat()`~ repeat is now implemented as `RepeatedGridTrack` - ~Documentation still needs to be improved.~ An initial pass over the documentation has been completed. ## Screenshot <img width="846" alt="Screenshot 2023-03-10 at 17 56 21" src="https://user-images.githubusercontent.com/1007307/224435332-69aa9eac-123d-4856-b75d-5449d3f1d426.png"> --- ## Changelog - Support for CSS Grid layout added to `bevy_ui` --------- Co-authored-by: Rob Parrett <robparrett@gmail.com> Co-authored-by: Andreas Weibye <13300393+Weibye@users.noreply.github.com>
2023-04-17 16:21:38 +00:00
[CSS Grid](../examples/ui/grid.rs) | An example for CSS Grid layout
[Display and Visibility](../examples/ui/display_and_visibility.rs) | Demonstrates how Display and Visibility work in the UI.
[Flex Layout](../examples/ui/flex_layout.rs) | Demonstrates how the AlignItems and JustifyContent properties can be composed to layout nodes and position text
[Font Atlas Debug](../examples/ui/font_atlas_debug.rs) | Illustrates how FontAtlases are populated (used to optimize text rendering internally)
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>
2024-10-02 00:24:28 +00:00
[Ghost Nodes](../examples/ui/ghost_nodes.rs) | Demonstrates the use of Ghost Nodes to skip entities in the UI layout hierarchy
Split UI `Overflow` by axis (#8095) # Objective Split the UI overflow enum so that overflow can be set for each axis separately. ## Solution Change `Overflow` from an enum to a struct with `x` and `y` `OverflowAxis` fields, where `OverflowAxis` is an enum with `Clip` and `Visible` variants. Modify `update_clipping` to calculate clipping for each axis separately. If only one axis is clipped, the other axis is given infinite bounds. <img width="642" alt="overflow" src="https://user-images.githubusercontent.com/27962798/227592983-568cf76f-7e40-48c4-a511-43c886f5e431.PNG"> --- ## Changelog * Split the UI overflow implementation so overflow can be set for each axis separately. * Added the enum `OverflowAxis` with `Clip` and `Visible` variants. * Changed `Overflow` to a struct with `x` and `y` fields of type `OverflowAxis`. * `Overflow` has new methods `visible()` and `hidden()` that replace its previous `Clip` and `Visible` variants. * Added `Overflow` helper methods `clip_x()` and `clip_y()` that return a new `Overflow` value with the given axis clipped. * Modified `update_clipping` so it calculates clipping for each axis separately. If a node is only clipped on a single axis, the other axis is given `-f32::INFINITY` to `f32::INFINITY` clipping bounds. ## Migration Guide The `Style` property `Overflow` is now a struct with `x` and `y` fields, that allow for per-axis overflow control. Use these helper functions to replace the variants of `Overflow`: * Replace `Overflow::Visible` with `Overflow::visible()` * Replace `Overflow::Hidden` with `Overflow::clip()`
2023-04-17 22:23:52 +00:00
[Overflow](../examples/ui/overflow.rs) | Simple example demonstrating overflow behavior
Overflow clip margin (#15561) # Objective Limited implementation of the CSS property `overflow-clip-margin` https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-margin Allows you to control the visible area for clipped content when using overfllow-clip, -hidden, or -scroll and expand it with a margin. Based on #15442 Fixes #15468 ## Solution Adds a new field to Style: `overflow_clip_margin: OverflowClipMargin`. The field is ignored unless overflow-clip, -hidden or -scroll is set on at least one axis. `OverflowClipMargin` has these associated constructor functions: ``` pub const fn content_box() -> Self; pub const fn padding_box() -> Self; pub const fn border_box() -> Self; ``` You can also use the method `with_margin` to increases the size of the visible area: ``` commands .spawn(NodeBundle { style: Style { width: Val::Px(100.), height: Val::Px(100.), padding: UiRect::all(Val::Px(20.)), border: UiRect::all(Val::Px(5.)), overflow: Overflow::clip(), overflow_clip_margin: OverflowClipMargin::border_box().with_margin(25.), ..Default::default() }, border_color: Color::BLACK.into(), background_color: GRAY.into(), ..Default::default() }) ``` `with_margin` expects a length in logical pixels, negative values are clamped to zero. ## Notes * To keep this PR as simple as possible I omitted responsive margin values support. This could be added in a follow up if we want it. * CSS also supports a `margin-box` option but we don't have access to the margin values in `Node` so it's probably not feasible to implement atm. ## Testing ```cargo run --example overflow_clip_margin``` <img width="396" alt="overflow-clip-margin" src="https://github.com/user-attachments/assets/07b51cd6-a565-4451-87a0-fa079429b04b"> ## Migration Guide Style has a new field `OverflowClipMargin`. It allows users to set the visible area for clipped content when using overflow-clip, -hidden, or -scroll and expand it with a margin. There are three associated constructor functions `content_box`, `padding_box` and `border_box`: * `content_box`: elements painted outside of the content box area (the innermost part of the node excluding the padding and border) of the node are clipped. This is the new default behaviour. * `padding_box`: elements painted outside outside of the padding area of the node are clipped. * `border_box`: elements painted outside of the bounds of the node are clipped. This matches the behaviour from Bevy 0.14. There is also a `with_margin` method that increases the size of the visible area by the given number in logical pixels, negative margin values are clamped to zero. `OverflowClipMargin` is ignored unless overflow-clip, -hidden or -scroll is also set on at least one axis of the UI node. --------- Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
2024-10-16 13:17:49 +00:00
[Overflow Clip Margin](../examples/ui/overflow_clip_margin.rs) | Simple example demonstrating the OverflowClipMargin style property
[Overflow and Clipping Debug](../examples/ui/overflow_debug.rs) | An example to debug overflow and clipping behavior
[Relative Cursor Position](../examples/ui/relative_cursor_position.rs) | Showcases the RelativeCursorPosition component
Camera-driven UI (#10559) # Objective Add support for presenting each UI tree on a specific window and viewport, while making as few breaking changes as possible. This PR is meant to resolve the following issues at once, since they're all related. - Fixes #5622 - Fixes #5570 - Fixes #5621 Adopted #5892 , but started over since the current codebase diverged significantly from the original PR branch. Also, I made a decision to propagate component to children instead of recursively iterating over nodes in search for the root. ## Solution Add a new optional component that can be inserted to UI root nodes and propagate to children to specify which camera it should render onto. This is then used to get the render target and the viewport for that UI tree. Since this component is optional, the default behavior should be to render onto the single camera (if only one exist) and warn of ambiguity if multiple cameras exist. This reduces the complexity for users with just one camera, while giving control in contexts where it matters. ## Changelog - Adds `TargetCamera(Entity)` component to specify which camera should a node tree be rendered into. If only one camera exists, this component is optional. - Adds an example of rendering UI to a texture and using it as a material in a 3D world. - Fixes recalculation of physical viewport size when target scale factor changes. This can happen when the window is moved between displays with different DPI. - Changes examples to demonstrate assigning UI to different viewports and windows and make interactions in an offset viewport testable. - Removes `UiCameraConfig`. UI visibility now can be controlled via combination of explicit `TargetCamera` and `Visibility` on the root nodes. --------- Co-authored-by: davier <bricedavier@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
2024-01-16 00:39:10 +00:00
[Render UI to Texture](../examples/ui/render_ui_to_texture.rs) | An example of rendering UI as a part of a 3D world
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 ![scroll](https://github.com/user-attachments/assets/1331138f-93aa-4a8f-959c-6be18a04ff03) ## Alternatives - `bevy_ui` doesn't support scrolling. - `bevy_ui` implements scrolling with a one-frame delay on reactions to layout changes.
2024-09-23 17:17:58 +00:00
[Scroll](../examples/ui/scroll.rs) | Demonstrates scrolling UI containers
[Size Constraints](../examples/ui/size_constraints.rs) | Demonstrates how the to use the size constraints to control the size of a UI node.
[Text](../examples/ui/text.rs) | Illustrates creating and updating text
[Text Debug](../examples/ui/text_debug.rs) | An example for debugging text layout
[Text Wrap Debug](../examples/ui/text_wrap_debug.rs) | Demonstrates text wrapping
[Transparency UI](../examples/ui/transparency_ui.rs) | Demonstrates transparency for UI
[UI Material](../examples/ui/ui_material.rs) | Demonstrates creating and using custom Ui materials
[UI Scaling](../examples/ui/ui_scaling.rs) | Illustrates how to scale the UI
[UI Texture Atlas](../examples/ui/ui_texture_atlas.rs) | Illustrates how to use TextureAtlases in UI
[UI Texture Atlas Slice](../examples/ui/ui_texture_atlas_slice.rs) | Illustrates how to use 9 Slicing for TextureAtlases in UI
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
2024-02-07 20:07:53 +00:00
[UI Texture Slice](../examples/ui/ui_texture_slice.rs) | Illustrates how to use 9 Slicing in UI
[UI Texture Slice Flipping and Tiling](../examples/ui/ui_texture_slice_flip_and_tile.rs) | Illustrates how to flip and tile images with 9 Slicing in UI
Add z-index support with a predictable UI stack (#5877) # Objective Add consistent UI rendering and interaction where deep nodes inside two different hierarchies will never render on top of one-another by default and offer an escape hatch (z-index) for nodes to change their depth. ## The problem with current implementation The current implementation of UI rendering is broken in that regard, mainly because [it sets the Z value of the `Transform` component based on a "global Z" space](https://github.com/bevyengine/bevy/blob/main/crates/bevy_ui/src/update.rs#L43) shared by all nodes in the UI. This doesn't account for the fact that each node's final `GlobalTransform` value will be relative to its parent. This effectively makes the depth unpredictable when two deep trees are rendered on top of one-another. At the moment, it's also up to each part of the UI code to sort all of the UI nodes. The solution that's offered here does the full sorting of UI node entities once and offers the result through a resource so that all systems can use it. ## Solution ### New ZIndex component This adds a new optional `ZIndex` enum component for nodes which offers two mechanism: - `ZIndex::Local(i32)`: Overrides the depth of the node relative to its siblings. - `ZIndex::Global(i32)`: Overrides the depth of the node relative to the UI root. This basically allows any node in the tree to "escape" the parent and be ordered relative to the entire UI. Note that in the current implementation, omitting `ZIndex` on a node has the same result as adding `ZIndex::Local(0)`. Additionally, the "global" stacking context is essentially a way to add your node to the root stacking context, so using `ZIndex::Local(n)` on a root node (one without parent) will share that space with all nodes using `Index::Global(n)`. ### New UiStack resource This adds a new `UiStack` resource which is calculated from both hierarchy and `ZIndex` during UI update and contains a vector of all node entities in the UI, ordered by depth (from farthest from camera to closest). This is exposed publicly by the bevy_ui crate with the hope that it can be used for consistent ordering and to reduce the amount of sorting that needs to be done by UI systems (i.e. instead of sorting everything by `global_transform.z` in every system, this array can be iterated over). ### New z_index example This also adds a new z_index example that showcases the new `ZIndex` component. It's also a good general demo of the new UI stack system, because making this kind of UI was very broken with the old system (e.g. nodes would render on top of each other, not respecting hierarchy or insert order at all). ![image](https://user-images.githubusercontent.com/1060971/189015985-8ea8f989-0e9d-4601-a7e0-4a27a43a53f9.png) --- ## Changelog - Added the `ZIndex` component to bevy_ui. - Added the `UiStack` resource to bevy_ui, and added implementation in a new `stack.rs` module. - Removed the previous Z updating system from bevy_ui, because it was replaced with the above. - Changed bevy_ui rendering to use UiStack instead of z ordering. - Changed bevy_ui focus/interaction system to use UiStack instead of z ordering. - Added a new z_index example. ## ZIndex demo Here's a demo I wrote to test these features https://user-images.githubusercontent.com/1060971/188329295-d7beebd6-9aee-43ab-821e-d437df5dbe8a.mp4 Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-11-02 22:06:04 +00:00
[UI Z-Index](../examples/ui/z_index.rs) | Demonstrates how to control the relative depth (z-position) of UI elements
[Viewport Debug](../examples/ui/viewport_debug.rs) | An example for debugging viewport coordinates
[Window Fallthrough](../examples/ui/window_fallthrough.rs) | Illustrates how to access `winit::window::Window`'s `hittest` functionality.
2020-08-17 23:02:59 +00:00
## Window
Example | Description
--- | ---
[Clear Color](../examples/window/clear_color.rs) | Creates a solid color window
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>
2024-06-03 13:06:48 +00:00
[Custom User Event](../examples/window/custom_user_event.rs) | Handles custom user events within the event loop
[Low Power](../examples/window/low_power.rs) | Demonstrates settings to reduce power use for bevy applications
[Monitor info](../examples/window/monitor_info.rs) | Displays information about available monitors (displays).
[Multiple Windows](../examples/window/multiple_windows.rs) | Demonstrates creating multiple windows, and rendering to them
[Scale Factor Override](../examples/window/scale_factor_override.rs) | Illustrates how to customize the default window settings
[Screenshot](../examples/window/screenshot.rs) | Shows how to save screenshots to disk
[Transparent Window](../examples/window/transparent_window.rs) | Illustrates making the window transparent and hiding the window decoration
2024-10-15 23:38:35 +00:00
[Window Drag Move](../examples/window/window_drag_move.rs) | Demonstrates drag move and drag resize without window decoration
[Window Resizing](../examples/window/window_resizing.rs) | Demonstrates resizing and responding to resizing a window
[Window Settings](../examples/window/window_settings.rs) | Demonstrates customizing default window settings
# Tests
Example | Description
--- | ---
[How to Test Apps](../tests/how_to_test_apps.rs) | How to test apps (simple integration testing)
[How to Test Systems](../tests/how_to_test_systems.rs) | How to test systems with commands, queries or resources
# Platform-Specific Examples
## Android
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Setup
```sh
rustup target add aarch64-linux-android
cargo install cargo-ndk
```
The Android SDK must be installed, and the environment variable `ANDROID_SDK_ROOT` set to the root Android `sdk` folder.
When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` must also be set to one of the NDKs in `sdk\ndk\[NDK number]`.
Alternatively, you can install Android Studio.
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Build & Run
To build an Android app, you first need to build shared object files for the target architecture with `cargo-ndk`:
```sh
cargo ndk -t <target_name> -o <project_name>/app/src/main/jniLibs build
```
For example, to compile to a 64-bit ARM platform:
```sh
cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build
```
Setting the output path ensures the shared object files can be found in target-specific directories under `jniLibs` where the JNI can find them.
See the `cargo-ndk` [README](https://crates.io/crates/cargo-ndk) for other options.
After this you can build it with `gradlew`:
```sh
./gradlew build
```
Or build it with Android Studio.
Then you can test it in your Android project.
#### About `libc++_shared.so`
Bevy may require `libc++_shared.so` to run on Android, as it is needed by the `oboe` crate, but typically `cargo-ndk` does not copy this file automatically.
To include it, you can manually obtain it from NDK source or use a `build.rs` script for automation, as described in the `cargo-ndk` [README](https://github.com/bbqsrc/cargo-ndk?tab=readme-ov-file#linking-against-and-copying-libc_sharedso-into-the-relevant-places-in-the-output-directory).
Alternatively, you can modify project files to include it when building an APK. To understand the specific steps taken in this project, please refer to the comments within the project files for detailed instructions(`app/CMakeList.txt`, `app/build.gradle`, `app/src/main/cpp/dummy.cpp`).
### Debugging
You can view the logs with the following command:
```sh
adb logcat | grep 'RustStdoutStderr\|bevy\|wgpu'
```
In case of an error getting a GPU or setting it up, you can try settings logs of `wgpu_hal` to `DEBUG` to get more information.
Sometimes, running the app complains about an unknown activity. This may be fixed by uninstalling the application:
```sh
adb uninstall org.bevyengine.example
```
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Old phones
In its examples, Bevy targets the minimum Android API that Play Store <!-- markdown-link-check-disable -->
[requires](https://developer.android.com/distribute/best-practices/develop/target-sdk) to upload and update apps. <!-- markdown-link-check-enable -->
Users of older phones may want to use an older API when testing. By default, Bevy uses [`GameAvtivity`](https://developer.android.com/games/agdk/game-activity), which only works for Android API level 31 and higher, so if you want to use older API, you need to switch to `NativeActivity`.
To use `NativeActivity`, you need to edit it in `cargo.toml` manually like this:
```toml
bevy = { version = "0.14", default-features = false, features = ["android-native-activity", ...] }
```
Then build it as the [Build & Run](#build--run) section stated above.
#### About `cargo-apk`
You can also build an APK with `cargo-apk`, a simpler and deprecated tool which doesn't support `GameActivity`. If you want to use this, there is a [folder](./mobile/android_basic) inside the mobile example with instructions.
Example | File | Description
--- | --- | ---
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## iOS
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Setup
You need to install the correct rust targets:
- `aarch64-apple-ios`: iOS devices
- `x86_64-apple-ios`: iOS simulator on x86 processors
- `aarch64-apple-ios-sim`: iOS simulator on Apple processors
```sh
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
```
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Build & Run
Using bash:
```sh
cd examples/mobile
make run
```
In an ideal world, this will boot up, install and run the app for the first
iOS simulator in your `xcrun simctl list devices`. If this fails, you can
specify the simulator device UUID via:
```sh
DEVICE_ID=${YOUR_DEVICE_ID} make run
```
If you'd like to see xcode do stuff, you can run
```sh
open bevy_mobile_example.xcodeproj/
```
which will open xcode. You then must push the zoom zoom play button and wait
for the magic.
Example | File | Description
--- | --- | ---
`ios` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## Wasm
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Setup
```sh
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
```
Cleanup of Markdown Files and add CI Checking (#1463) I have run the VSCode Extension [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) on all Markdown Files in the Repo. The provided Rules are documented here: https://github.com/DavidAnson/markdownlint/blob/v0.23.1/doc/Rules.md Rules I didn't follow/fix: * MD024/no-duplicate-heading * Changelog: Here Heading will always repeat. * Examples Readme: Platform-specific documentation should be symmetrical. * MD025/single-title * MD026/no-trailing-punctuation * Caused by the ! in "Hello, World!". * MD033/no-inline-html * The plugins_guidlines file does need HTML, so the shown badges aren't downscaled too much. * ~~MD036/no-emphasis-as-heading:~~ * ~~This Warning only Appears in the Github Issue Templates and can be ignored.~~ * ~~MD041/first-line-heading~~ * ~~Only appears in the Readme for the AlienCake example Assets, which is unimportant.~~ --- I also sorted the Examples in the Readme and Cargo.toml in this order/Priority: * Topic/Folder * Introductionary Examples * Alphabetical Order The explanation for each case, where it isn't Alphabetical : * Diagnostics * log_diagnostics: The usage of inbuild Diagnostics is more important than creating your own. * ECS (Entity Component System) * ecs_guide: The guide should be read, before diving into other Features. * Reflection * reflection: Basic Explanation should be read, before more advanced Topics. * WASM Examples * hello_wasm: It's "Hello, World!".
2021-02-22 04:50:05 +00:00
### Build & Run
Following is an example for `lighting`. For other examples, change the `lighting` in the
following commands.
```sh
cargo build --release --example lighting --target wasm32-unknown-unknown
wasm-bindgen --out-name wasm_example \
--out-dir examples/wasm/target \
--target web target/wasm32-unknown-unknown/release/examples/lighting.wasm
```
The first command will build the example for the wasm target, creating a binary. Then,
[wasm-bindgen-cli](https://rustwasm.github.io/wasm-bindgen/reference/cli.html) is used to create
javascript bindings to this wasm file in the output file `examples/wasm/target/wasm_example.js`, which can be loaded using this
[example HTML file](./wasm/index.html).
Then serve `examples/wasm` directory to browser. i.e.
```sh
# cargo install basic-http-server
basic-http-server examples/wasm
# with python
python3 -m http.server --directory examples/wasm
# with ruby
ruby -run -ehttpd examples/wasm
```
Webgpu support (#8336) # Objective - Support WebGPU - alternative to #5027 that doesn't need any async / await - fixes #8315 - Surprise fix #7318 ## Solution ### For async renderer initialisation - Update the plugin lifecycle: - app builds the plugin - calls `plugin.build` - registers the plugin - app starts the event loop - event loop waits for `ready` of all registered plugins in the same order - returns `true` by default - then call all `finish` then all `cleanup` in the same order as registered - then execute the schedule In the case of the renderer, to avoid anything async: - building the renderer plugin creates a detached task that will send back the initialised renderer through a mutex in a resource - `ready` will wait for the renderer to be present in the resource - `finish` will take that renderer and place it in the expected resources by other plugins - other plugins (that expect the renderer to be available) `finish` are called and they are able to set up their pipelines - `cleanup` is called, only custom one is still for pipeline rendering ### For WebGPU support - update the `build-wasm-example` script to support passing `--api webgpu` that will build the example with WebGPU support - feature for webgl2 was always enabled when building for wasm. it's now in the default feature list and enabled on all platforms, so check for this feature must also check that the target_arch is `wasm32` --- ## Migration Guide - `Plugin::setup` has been renamed `Plugin::cleanup` - `Plugin::finish` has been added, and plugins adding pipelines should do it in this function instead of `Plugin::build` ```rust // Before impl Plugin for MyPlugin { fn build(&self, app: &mut App) { app.insert_resource::<MyResource> .add_systems(Update, my_system); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<RenderResourceNeedingDevice>() .init_resource::<OtherRenderResource>(); } } // After impl Plugin for MyPlugin { fn build(&self, app: &mut App) { app.insert_resource::<MyResource> .add_systems(Update, my_system); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<OtherRenderResource>(); } fn finish(&self, app: &mut App) { let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<RenderResourceNeedingDevice>(); } } ```
2023-05-04 22:07:57 +00:00
#### WebGL2 and WebGPU
Bevy support for WebGPU is being worked on, but is currently experimental.
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>
2024-01-26 18:14:21 +00:00
To build for WebGPU, you'll need to enable the `webgpu` feature. This will override the `webgl2` feature, and builds with the `webgpu` feature enabled won't be able to run on browsers that don't support WebGPU.
Webgpu support (#8336) # Objective - Support WebGPU - alternative to #5027 that doesn't need any async / await - fixes #8315 - Surprise fix #7318 ## Solution ### For async renderer initialisation - Update the plugin lifecycle: - app builds the plugin - calls `plugin.build` - registers the plugin - app starts the event loop - event loop waits for `ready` of all registered plugins in the same order - returns `true` by default - then call all `finish` then all `cleanup` in the same order as registered - then execute the schedule In the case of the renderer, to avoid anything async: - building the renderer plugin creates a detached task that will send back the initialised renderer through a mutex in a resource - `ready` will wait for the renderer to be present in the resource - `finish` will take that renderer and place it in the expected resources by other plugins - other plugins (that expect the renderer to be available) `finish` are called and they are able to set up their pipelines - `cleanup` is called, only custom one is still for pipeline rendering ### For WebGPU support - update the `build-wasm-example` script to support passing `--api webgpu` that will build the example with WebGPU support - feature for webgl2 was always enabled when building for wasm. it's now in the default feature list and enabled on all platforms, so check for this feature must also check that the target_arch is `wasm32` --- ## Migration Guide - `Plugin::setup` has been renamed `Plugin::cleanup` - `Plugin::finish` has been added, and plugins adding pipelines should do it in this function instead of `Plugin::build` ```rust // Before impl Plugin for MyPlugin { fn build(&self, app: &mut App) { app.insert_resource::<MyResource> .add_systems(Update, my_system); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<RenderResourceNeedingDevice>() .init_resource::<OtherRenderResource>(); } } // After impl Plugin for MyPlugin { fn build(&self, app: &mut App) { app.insert_resource::<MyResource> .add_systems(Update, my_system); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<OtherRenderResource>(); } fn finish(&self, app: &mut App) { let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app .init_resource::<RenderResourceNeedingDevice>(); } } ```
2023-05-04 22:07:57 +00:00
Bevy has an helper to build its examples:
- Build for WebGL2: `cargo run -p build-wasm-example -- --api webgl2 load_gltf`
- Build for WebGPU: `cargo run -p build-wasm-example -- --api webgpu load_gltf`
This helper will log the command used to build the examples.
### Audio in the browsers
For the moment, everything is single threaded, this can lead to stuttering when playing audio in browsers. Not all browsers react the same way for all games, you will have to experiment for your game.
In browsers, audio is not authorized to start without being triggered by an user interaction. This is to avoid multiple tabs all starting to auto play some sounds. You can find more context and explanation for this on [Google Chrome blog](https://developer.chrome.com/blog/web-audio-autoplay/). This page also describes a JS workaround to resume audio as soon as the user interact with your game.
### Optimizing
On the web, it's useful to reduce the size of the files that are distributed.
With rust, there are many ways to improve your executable sizes, starting with
the steps described in [the quick-start guide](https://bevyengine.org/learn/quick-start/getting-started/setup/#compile-with-performance-optimizations).
Now, when building the executable, use `--profile wasm-release` instead of `--release`:
```sh
cargo build --profile wasm-release --example lighting --target wasm32-unknown-unknown
```
To apply `wasm-opt`, first locate the `.wasm` file generated in the `--out-dir` of the
earlier `wasm-bindgen-cli` command (the filename should end with `_bg.wasm`), then run:
```sh
wasm-opt -Oz --output optimized.wasm examples/wasm/target/lighting_bg.wasm
mv optimized.wasm examples/wasm/target/lighting_bg.wasm
```
Make sure your final executable size is actually smaller. Some optimizations
may not be worth keeping due to compilation time increases.
For a small project with a basic 3d model and two lights,
the generated file sizes are, as of July 2022, as follows:
|profile | wasm-opt | no wasm-opt |
|----------------------------------|----------|-------------|
|Default | 8.5M | 13.0M |
|opt-level = "z" | 6.1M | 12.7M |
|"z" + lto = "thin" | 5.9M | 12M |
|"z" + lto = "fat" | 5.1M | 9.4M |
|"z" + "thin" + codegen-units = 1 | 5.3M | 11M |
|"z" + "fat" + codegen-units = 1 | 4.8M | 8.5M |
### Loading Assets
To load assets, they need to be available in the folder examples/wasm/assets. Cloning this
repository will set it up as a symlink on Linux and macOS, but you will need to manually move
the assets on Windows.