Merge branch 'main' into proper-json-schema
This commit is contained in:
commit
8a918a8915
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -294,7 +294,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Check for typos
|
- name: Check for typos
|
||||||
uses: crate-ci/typos@v1.33.1
|
uses: crate-ci/typos@v1.34.0
|
||||||
- name: Typos info
|
- name: Typos info
|
||||||
if: failure()
|
if: failure()
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
29
Cargo.toml
29
Cargo.toml
@ -868,6 +868,7 @@ doc-scrape-examples = true
|
|||||||
name = "Texture Atlas"
|
name = "Texture Atlas"
|
||||||
description = "Generates a texture atlas (sprite sheet) from individual sprites"
|
description = "Generates a texture atlas (sprite sheet) from individual sprites"
|
||||||
category = "2D Rendering"
|
category = "2D Rendering"
|
||||||
|
# Loading asset folders is not supported in Wasm, but required to create the atlas.
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -945,6 +946,7 @@ doc-scrape-examples = true
|
|||||||
name = "2D Wireframe"
|
name = "2D Wireframe"
|
||||||
description = "Showcases wireframes for 2d meshes"
|
description = "Showcases wireframes for 2d meshes"
|
||||||
category = "2D Rendering"
|
category = "2D Rendering"
|
||||||
|
# PolygonMode::Line wireframes are not supported by WebGL
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
# 3D Rendering
|
# 3D Rendering
|
||||||
@ -1012,6 +1014,7 @@ doc-scrape-examples = true
|
|||||||
name = "Anti-aliasing"
|
name = "Anti-aliasing"
|
||||||
description = "Compares different anti-aliasing methods"
|
description = "Compares different anti-aliasing methods"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# TAA not supported by WebGL
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1056,6 +1059,7 @@ doc-scrape-examples = true
|
|||||||
name = "Auto Exposure"
|
name = "Auto Exposure"
|
||||||
description = "A scene showcasing auto exposure"
|
description = "A scene showcasing auto exposure"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# Requires compute shaders, which are not supported by WebGL.
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1123,6 +1127,7 @@ doc-scrape-examples = true
|
|||||||
name = "Screen Space Ambient Occlusion"
|
name = "Screen Space Ambient Occlusion"
|
||||||
description = "A scene showcasing screen space ambient occlusion"
|
description = "A scene showcasing screen space ambient occlusion"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# Requires compute shaders, which are not supported by WebGL.
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1222,6 +1227,7 @@ doc-scrape-examples = true
|
|||||||
name = "Order Independent Transparency"
|
name = "Order Independent Transparency"
|
||||||
description = "Demonstrates how to use OIT"
|
description = "Demonstrates how to use OIT"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# Not supported by WebGL
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1321,7 +1327,7 @@ doc-scrape-examples = true
|
|||||||
name = "Skybox"
|
name = "Skybox"
|
||||||
description = "Load a cubemap texture onto a cube like a skybox and cycle through different compressed texture formats."
|
description = "Load a cubemap texture onto a cube like a skybox and cycle through different compressed texture formats."
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = false
|
wasm = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "solari"
|
name = "solari"
|
||||||
@ -1432,6 +1438,7 @@ doc-scrape-examples = true
|
|||||||
name = "Wireframe"
|
name = "Wireframe"
|
||||||
description = "Showcases wireframe rendering"
|
description = "Showcases wireframe rendering"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# Not supported on WebGL
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1443,6 +1450,8 @@ doc-scrape-examples = true
|
|||||||
name = "Irradiance Volumes"
|
name = "Irradiance Volumes"
|
||||||
description = "Demonstrates irradiance volumes"
|
description = "Demonstrates irradiance volumes"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# On WebGL and WebGPU, the number of texture bindings is too low
|
||||||
|
# See <https://github.com/bevyengine/bevy/issues/11885>
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1455,6 +1464,7 @@ required-features = ["meshlet"]
|
|||||||
name = "Meshlet"
|
name = "Meshlet"
|
||||||
description = "Meshlet rendering for dense high-poly scenes (experimental)"
|
description = "Meshlet rendering for dense high-poly scenes (experimental)"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
|
# Requires compute shaders and WGPU extensions, not supported by WebGL nor WebGPU.
|
||||||
wasm = false
|
wasm = false
|
||||||
setup = [
|
setup = [
|
||||||
[
|
[
|
||||||
@ -1490,7 +1500,7 @@ doc-scrape-examples = true
|
|||||||
name = "Lightmaps"
|
name = "Lightmaps"
|
||||||
description = "Rendering a scene with baked lightmaps"
|
description = "Rendering a scene with baked lightmaps"
|
||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = false
|
wasm = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "no_prepass"
|
name = "no_prepass"
|
||||||
@ -1643,6 +1653,7 @@ doc-scrape-examples = true
|
|||||||
name = "Custom Loop"
|
name = "Custom Loop"
|
||||||
description = "Demonstrates how to create a custom runner (to update an app manually)"
|
description = "Demonstrates how to create a custom runner (to update an app manually)"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Doesn't render anything, doesn't create a canvas
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1654,6 +1665,7 @@ doc-scrape-examples = true
|
|||||||
name = "Drag and Drop"
|
name = "Drag and Drop"
|
||||||
description = "An example that shows how to handle drag and drop in an app"
|
description = "An example that shows how to handle drag and drop in an app"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Browser drag and drop is not supported
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1665,6 +1677,7 @@ doc-scrape-examples = true
|
|||||||
name = "Empty"
|
name = "Empty"
|
||||||
description = "An empty application (does nothing)"
|
description = "An empty application (does nothing)"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Doesn't render anything, doesn't create a canvas
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1688,6 +1701,7 @@ required-features = ["bevy_log"]
|
|||||||
name = "Headless"
|
name = "Headless"
|
||||||
description = "An application that runs without default plugins"
|
description = "An application that runs without default plugins"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Doesn't render anything, doesn't create a canvas
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1710,6 +1724,8 @@ doc-scrape-examples = true
|
|||||||
name = "Log layers"
|
name = "Log layers"
|
||||||
description = "Illustrate how to add custom log layers"
|
description = "Illustrate how to add custom log layers"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Accesses `time`, which is not available on the web
|
||||||
|
# Also doesn't render anything
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -1721,6 +1737,7 @@ doc-scrape-examples = true
|
|||||||
name = "Advanced log layers"
|
name = "Advanced log layers"
|
||||||
description = "Illustrate how to transfer data between log layers and Bevy's ECS"
|
description = "Illustrate how to transfer data between log layers and Bevy's ECS"
|
||||||
category = "Application"
|
category = "Application"
|
||||||
|
# Doesn't render anything, doesn't create a canvas
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -4325,6 +4342,14 @@ description = "Demonstrates specular tints and maps"
|
|||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "test_invalid_skinned_mesh"
|
||||||
|
path = "tests/3d/test_invalid_skinned_mesh.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.test_invalid_skinned_mesh]
|
||||||
|
hidden = true
|
||||||
|
|
||||||
[profile.wasm-release]
|
[profile.wasm-release]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// For 2d replace `bevy_pbr::mesh_functions` with `bevy_sprite::mesh2d_functions`
|
||||||
|
// and `mesh_position_local_to_clip` with `mesh2d_position_local_to_clip`.
|
||||||
#import bevy_pbr::mesh_functions::{get_world_from_local, mesh_position_local_to_clip}
|
#import bevy_pbr::mesh_functions::{get_world_from_local, mesh_position_local_to_clip}
|
||||||
|
|
||||||
struct CustomMaterial {
|
struct CustomMaterial {
|
||||||
|
|||||||
@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
struct MyExtendedMaterial {
|
struct MyExtendedMaterial {
|
||||||
quantize_steps: u32,
|
quantize_steps: u32,
|
||||||
|
#ifdef SIXTEEN_BYTE_ALIGNMENT
|
||||||
|
// Web examples WebGL2 support: structs must be 16 byte aligned.
|
||||||
|
_webgl2_padding_8b: u32,
|
||||||
|
_webgl2_padding_12b: u32,
|
||||||
|
_webgl2_padding_16b: u32,
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(3) @binding(100)
|
@group(3) @binding(100)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ impl<const SIZE: usize> Benchmark<SIZE> {
|
|||||||
let mut events = Events::default();
|
let mut events = Events::default();
|
||||||
|
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
events.send(BenchEvent([0u8; SIZE]));
|
events.write(BenchEvent([0u8; SIZE]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Self(events)
|
Self(events)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
mod iter;
|
mod iter;
|
||||||
mod send;
|
mod write;
|
||||||
|
|
||||||
use criterion::{criterion_group, Criterion};
|
use criterion::{criterion_group, Criterion};
|
||||||
|
|
||||||
@ -11,19 +11,19 @@ fn send(c: &mut Criterion) {
|
|||||||
group.measurement_time(core::time::Duration::from_secs(4));
|
group.measurement_time(core::time::Duration::from_secs(4));
|
||||||
for count in [100, 1_000, 10_000] {
|
for count in [100, 1_000, 10_000] {
|
||||||
group.bench_function(format!("size_4_events_{count}"), |b| {
|
group.bench_function(format!("size_4_events_{count}"), |b| {
|
||||||
let mut bench = send::Benchmark::<4>::new(count);
|
let mut bench = write::Benchmark::<4>::new(count);
|
||||||
b.iter(move || bench.run());
|
b.iter(move || bench.run());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for count in [100, 1_000, 10_000] {
|
for count in [100, 1_000, 10_000] {
|
||||||
group.bench_function(format!("size_16_events_{count}"), |b| {
|
group.bench_function(format!("size_16_events_{count}"), |b| {
|
||||||
let mut bench = send::Benchmark::<16>::new(count);
|
let mut bench = write::Benchmark::<16>::new(count);
|
||||||
b.iter(move || bench.run());
|
b.iter(move || bench.run());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for count in [100, 1_000, 10_000] {
|
for count in [100, 1_000, 10_000] {
|
||||||
group.bench_function(format!("size_512_events_{count}"), |b| {
|
group.bench_function(format!("size_512_events_{count}"), |b| {
|
||||||
let mut bench = send::Benchmark::<512>::new(count);
|
let mut bench = write::Benchmark::<512>::new(count);
|
||||||
b.iter(move || bench.run());
|
b.iter(move || bench.run());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ impl<const SIZE: usize> Benchmark<SIZE> {
|
|||||||
// Force both internal buffers to be allocated.
|
// Force both internal buffers to be allocated.
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
events.send(BenchEvent([0u8; SIZE]));
|
events.write(BenchEvent([0u8; SIZE]));
|
||||||
}
|
}
|
||||||
events.update();
|
events.update();
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ impl<const SIZE: usize> Benchmark<SIZE> {
|
|||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
for _ in 0..self.count {
|
for _ in 0..self.count {
|
||||||
self.events
|
self.events
|
||||||
.send(core::hint::black_box(BenchEvent([0u8; SIZE])));
|
.write(core::hint::black_box(BenchEvent([0u8; SIZE])));
|
||||||
}
|
}
|
||||||
self.events.update();
|
self.events.update();
|
||||||
}
|
}
|
||||||
@ -14,7 +14,6 @@ bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
|
|||||||
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
||||||
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
||||||
bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
|
|
||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||||
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
|
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
|
||||||
|
|||||||
@ -30,9 +30,7 @@
|
|||||||
//!
|
//!
|
||||||
//! [SMAA]: https://www.iryoku.com/smaa/
|
//! [SMAA]: https://www.iryoku.com/smaa/
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
#[cfg(feature = "smaa_luts")]
|
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
|
||||||
use bevy_asset::load_internal_binary_asset;
|
|
||||||
use bevy_asset::{embedded_asset, load_embedded_asset, uuid_handle, AssetServer, Handle};
|
|
||||||
#[cfg(not(feature = "smaa_luts"))]
|
#[cfg(not(feature = "smaa_luts"))]
|
||||||
use bevy_core_pipeline::tonemapping::lut_placeholder;
|
use bevy_core_pipeline::tonemapping::lut_placeholder;
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
@ -79,13 +77,6 @@ use bevy_render::{
|
|||||||
};
|
};
|
||||||
use bevy_utils::prelude::default;
|
use bevy_utils::prelude::default;
|
||||||
|
|
||||||
/// The handle of the area LUT, a KTX2 format texture that SMAA uses internally.
|
|
||||||
const SMAA_AREA_LUT_TEXTURE_HANDLE: Handle<Image> =
|
|
||||||
uuid_handle!("569c4d67-c7fa-4958-b1af-0836023603c0");
|
|
||||||
/// The handle of the search LUT, a KTX2 format texture that SMAA uses internally.
|
|
||||||
const SMAA_SEARCH_LUT_TEXTURE_HANDLE: Handle<Image> =
|
|
||||||
uuid_handle!("43b97515-252e-4c8a-b9af-f2fc528a1c27");
|
|
||||||
|
|
||||||
/// Adds support for subpixel morphological antialiasing, or SMAA.
|
/// Adds support for subpixel morphological antialiasing, or SMAA.
|
||||||
pub struct SmaaPlugin;
|
pub struct SmaaPlugin;
|
||||||
|
|
||||||
@ -125,6 +116,14 @@ pub enum SmaaPreset {
|
|||||||
Ultra,
|
Ultra,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct SmaaLuts {
|
||||||
|
/// The handle of the area LUT, a KTX2 format texture that SMAA uses internally.
|
||||||
|
area_lut: Handle<Image>,
|
||||||
|
/// The handle of the search LUT, a KTX2 format texture that SMAA uses internally.
|
||||||
|
search_lut: Handle<Image>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A render world resource that holds all render pipeline data needed for SMAA.
|
/// A render world resource that holds all render pipeline data needed for SMAA.
|
||||||
///
|
///
|
||||||
/// There are three separate passes, so we need three separate pipelines.
|
/// There are three separate passes, so we need three separate pipelines.
|
||||||
@ -292,49 +291,26 @@ impl Plugin for SmaaPlugin {
|
|||||||
// Load the shader.
|
// Load the shader.
|
||||||
embedded_asset!(app, "smaa.wgsl");
|
embedded_asset!(app, "smaa.wgsl");
|
||||||
|
|
||||||
// Load the two lookup textures. These are compressed textures in KTX2
|
|
||||||
// format.
|
|
||||||
#[cfg(feature = "smaa_luts")]
|
#[cfg(feature = "smaa_luts")]
|
||||||
load_internal_binary_asset!(
|
let smaa_luts = {
|
||||||
app,
|
// Load the two lookup textures. These are compressed textures in KTX2 format.
|
||||||
SMAA_AREA_LUT_TEXTURE_HANDLE,
|
embedded_asset!(app, "SMAAAreaLUT.ktx2");
|
||||||
"SMAAAreaLUT.ktx2",
|
embedded_asset!(app, "SMAASearchLUT.ktx2");
|
||||||
|bytes, _: String| Image::from_buffer(
|
|
||||||
bytes,
|
|
||||||
bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
|
|
||||||
bevy_image::CompressedImageFormats::NONE,
|
|
||||||
false,
|
|
||||||
bevy_image::ImageSampler::Default,
|
|
||||||
bevy_asset::RenderAssetUsages::RENDER_WORLD,
|
|
||||||
)
|
|
||||||
.expect("Failed to load SMAA area LUT")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(feature = "smaa_luts")]
|
|
||||||
load_internal_binary_asset!(
|
|
||||||
app,
|
|
||||||
SMAA_SEARCH_LUT_TEXTURE_HANDLE,
|
|
||||||
"SMAASearchLUT.ktx2",
|
|
||||||
|bytes, _: String| Image::from_buffer(
|
|
||||||
bytes,
|
|
||||||
bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
|
|
||||||
bevy_image::CompressedImageFormats::NONE,
|
|
||||||
false,
|
|
||||||
bevy_image::ImageSampler::Default,
|
|
||||||
bevy_asset::RenderAssetUsages::RENDER_WORLD,
|
|
||||||
)
|
|
||||||
.expect("Failed to load SMAA search LUT")
|
|
||||||
);
|
|
||||||
|
|
||||||
|
SmaaLuts {
|
||||||
|
area_lut: load_embedded_asset!(app, "SMAAAreaLUT.ktx2"),
|
||||||
|
search_lut: load_embedded_asset!(app, "SMAASearchLUT.ktx2"),
|
||||||
|
}
|
||||||
|
};
|
||||||
#[cfg(not(feature = "smaa_luts"))]
|
#[cfg(not(feature = "smaa_luts"))]
|
||||||
app.world_mut()
|
let smaa_luts = {
|
||||||
.resource_mut::<bevy_asset::Assets<Image>>()
|
let mut images = app.world_mut().resource_mut::<bevy_asset::Assets<Image>>();
|
||||||
.insert(SMAA_AREA_LUT_TEXTURE_HANDLE.id(), lut_placeholder());
|
let handle = images.add(lut_placeholder());
|
||||||
|
SmaaLuts {
|
||||||
#[cfg(not(feature = "smaa_luts"))]
|
area_lut: handle.clone(),
|
||||||
app.world_mut()
|
search_lut: handle.clone(),
|
||||||
.resource_mut::<bevy_asset::Assets<Image>>()
|
}
|
||||||
.insert(SMAA_SEARCH_LUT_TEXTURE_HANDLE.id(), lut_placeholder());
|
};
|
||||||
|
|
||||||
app.add_plugins(ExtractComponentPlugin::<Smaa>::default())
|
app.add_plugins(ExtractComponentPlugin::<Smaa>::default())
|
||||||
.register_type::<Smaa>();
|
.register_type::<Smaa>();
|
||||||
@ -344,6 +320,7 @@ impl Plugin for SmaaPlugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render_app
|
render_app
|
||||||
|
.insert_resource(smaa_luts)
|
||||||
.init_resource::<SmaaSpecializedRenderPipelines>()
|
.init_resource::<SmaaSpecializedRenderPipelines>()
|
||||||
.init_resource::<SmaaInfoUniformBuffer>()
|
.init_resource::<SmaaInfoUniformBuffer>()
|
||||||
.add_systems(RenderStartup, init_smaa_pipelines)
|
.add_systems(RenderStartup, init_smaa_pipelines)
|
||||||
@ -747,13 +724,14 @@ fn prepare_smaa_bind_groups(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
smaa_pipelines: Res<SmaaPipelines>,
|
smaa_pipelines: Res<SmaaPipelines>,
|
||||||
|
smaa_luts: Res<SmaaLuts>,
|
||||||
images: Res<RenderAssets<GpuImage>>,
|
images: Res<RenderAssets<GpuImage>>,
|
||||||
view_targets: Query<(Entity, &SmaaTextures), (With<ExtractedView>, With<Smaa>)>,
|
view_targets: Query<(Entity, &SmaaTextures), (With<ExtractedView>, With<Smaa>)>,
|
||||||
) {
|
) {
|
||||||
// Fetch the two lookup textures. These are bundled in this library.
|
// Fetch the two lookup textures. These are bundled in this library.
|
||||||
let (Some(search_texture), Some(area_texture)) = (
|
let (Some(search_texture), Some(area_texture)) = (
|
||||||
images.get(&SMAA_SEARCH_LUT_TEXTURE_HANDLE),
|
images.get(&smaa_luts.search_lut),
|
||||||
images.get(&SMAA_AREA_LUT_TEXTURE_HANDLE),
|
images.get(&smaa_luts.area_lut),
|
||||||
) else {
|
) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1864,7 +1864,7 @@ mod tests {
|
|||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
// Sending one event
|
// Sending one event
|
||||||
app.world_mut().send_event(TestEvent);
|
app.world_mut().write_event(TestEvent);
|
||||||
|
|
||||||
let test_events = app.world().resource::<Events<TestEvent>>();
|
let test_events = app.world().resource::<Events<TestEvent>>();
|
||||||
assert_eq!(test_events.len(), 1);
|
assert_eq!(test_events.len(), 1);
|
||||||
@ -1872,8 +1872,8 @@ mod tests {
|
|||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
// Sending two events on the next frame
|
// Sending two events on the next frame
|
||||||
app.world_mut().send_event(TestEvent);
|
app.world_mut().write_event(TestEvent);
|
||||||
app.world_mut().send_event(TestEvent);
|
app.world_mut().write_event(TestEvent);
|
||||||
|
|
||||||
let test_events = app.world().resource::<Events<TestEvent>>();
|
let test_events = app.world().resource::<Events<TestEvent>>();
|
||||||
assert_eq!(test_events.len(), 3); // Events are double-buffered, so we see 1 + 2 = 3
|
assert_eq!(test_events.len(), 3); // Events are double-buffered, so we see 1 + 2 = 3
|
||||||
|
|||||||
@ -492,8 +492,8 @@ impl<A: Asset> TryFrom<UntypedHandle> for Handle<A> {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_asset::{Handle, uuid_handle};
|
/// # use bevy_asset::{Handle, uuid_handle};
|
||||||
/// # type Shader = ();
|
/// # type Image = ();
|
||||||
/// const SHADER: Handle<Shader> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
|
/// const IMAGE: Handle<Image> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! uuid_handle {
|
macro_rules! uuid_handle {
|
||||||
|
|||||||
@ -167,7 +167,7 @@ impl AssetServer {
|
|||||||
fn sender<A: Asset>(world: &mut World, id: UntypedAssetId) {
|
fn sender<A: Asset>(world: &mut World, id: UntypedAssetId) {
|
||||||
world
|
world
|
||||||
.resource_mut::<Events<AssetEvent<A>>>()
|
.resource_mut::<Events<AssetEvent<A>>>()
|
||||||
.send(AssetEvent::LoadedWithDependencies { id: id.typed() });
|
.write(AssetEvent::LoadedWithDependencies { id: id.typed() });
|
||||||
}
|
}
|
||||||
fn failed_sender<A: Asset>(
|
fn failed_sender<A: Asset>(
|
||||||
world: &mut World,
|
world: &mut World,
|
||||||
@ -177,7 +177,7 @@ impl AssetServer {
|
|||||||
) {
|
) {
|
||||||
world
|
world
|
||||||
.resource_mut::<Events<AssetLoadFailedEvent<A>>>()
|
.resource_mut::<Events<AssetLoadFailedEvent<A>>>()
|
||||||
.send(AssetLoadFailedEvent {
|
.write(AssetLoadFailedEvent {
|
||||||
id: id.typed(),
|
id: id.typed(),
|
||||||
path,
|
path,
|
||||||
error,
|
error,
|
||||||
@ -1685,7 +1685,7 @@ pub fn handle_internal_asset_events(world: &mut World) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !untyped_failures.is_empty() {
|
if !untyped_failures.is_empty() {
|
||||||
world.send_event_batch(untyped_failures);
|
world.write_event_batch(untyped_failures);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_ancestors(
|
fn queue_ancestors(
|
||||||
|
|||||||
@ -16,7 +16,6 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
|||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
|
||||||
|
|
||||||
# other
|
# other
|
||||||
# TODO: Remove `coreaudio-sys` dep below when updating `cpal`.
|
# TODO: Remove `coreaudio-sys` dep below when updating `cpal`.
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use bevy_asset::{Asset, Handle};
|
|||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
|
use bevy_transform::components::Transform;
|
||||||
|
|
||||||
/// The way Bevy manages the sound playback.
|
/// The way Bevy manages the sound playback.
|
||||||
#[derive(Debug, Clone, Copy, Reflect)]
|
#[derive(Debug, Clone, Copy, Reflect)]
|
||||||
@ -10,10 +11,10 @@ use bevy_reflect::prelude::*;
|
|||||||
pub enum PlaybackMode {
|
pub enum PlaybackMode {
|
||||||
/// Play the sound once. Do nothing when it ends.
|
/// Play the sound once. Do nothing when it ends.
|
||||||
///
|
///
|
||||||
/// Note: It is not possible to reuse an `AudioPlayer` after it has finished playing and
|
/// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and
|
||||||
/// the underlying `AudioSink` or `SpatialAudioSink` has been drained.
|
/// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.
|
||||||
///
|
///
|
||||||
/// To replay a sound, the audio components provided by `AudioPlayer` must be removed and
|
/// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and
|
||||||
/// added again.
|
/// added again.
|
||||||
Once,
|
Once,
|
||||||
/// Repeat the sound forever.
|
/// Repeat the sound forever.
|
||||||
@ -27,7 +28,7 @@ pub enum PlaybackMode {
|
|||||||
/// Initial settings to be used when audio starts playing.
|
/// Initial settings to be used when audio starts playing.
|
||||||
///
|
///
|
||||||
/// If you would like to control the audio while it is playing, query for the
|
/// If you would like to control the audio while it is playing, query for the
|
||||||
/// [`AudioSink`][crate::AudioSink] or [`SpatialAudioSink`][crate::SpatialAudioSink]
|
/// [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink)
|
||||||
/// components. Changes to this component will *not* be applied to already-playing audio.
|
/// components. Changes to this component will *not* be applied to already-playing audio.
|
||||||
#[derive(Component, Clone, Copy, Debug, Reflect)]
|
#[derive(Component, Clone, Copy, Debug, Reflect)]
|
||||||
#[reflect(Clone, Default, Component, Debug)]
|
#[reflect(Clone, Default, Component, Debug)]
|
||||||
@ -78,10 +79,10 @@ impl Default for PlaybackSettings {
|
|||||||
impl PlaybackSettings {
|
impl PlaybackSettings {
|
||||||
/// Will play the associated audio source once.
|
/// Will play the associated audio source once.
|
||||||
///
|
///
|
||||||
/// Note: It is not possible to reuse an `AudioPlayer` after it has finished playing and
|
/// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and
|
||||||
/// the underlying `AudioSink` or `SpatialAudioSink` has been drained.
|
/// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.
|
||||||
///
|
///
|
||||||
/// To replay a sound, the audio components provided by `AudioPlayer` must be removed and
|
/// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and
|
||||||
/// added again.
|
/// added again.
|
||||||
pub const ONCE: PlaybackSettings = PlaybackSettings {
|
pub const ONCE: PlaybackSettings = PlaybackSettings {
|
||||||
mode: PlaybackMode::Once,
|
mode: PlaybackMode::Once,
|
||||||
@ -164,14 +165,15 @@ impl PlaybackSettings {
|
|||||||
|
|
||||||
/// Settings for the listener for spatial audio sources.
|
/// Settings for the listener for spatial audio sources.
|
||||||
///
|
///
|
||||||
/// This must be accompanied by `Transform` and `GlobalTransform`.
|
/// This is accompanied by [`Transform`] and [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
|
||||||
/// Only one entity with a `SpatialListener` should be present at any given time.
|
/// Only one entity with a [`SpatialListener`] should be present at any given time.
|
||||||
#[derive(Component, Clone, Debug, Reflect)]
|
#[derive(Component, Clone, Debug, Reflect)]
|
||||||
|
#[require(Transform)]
|
||||||
#[reflect(Clone, Default, Component, Debug)]
|
#[reflect(Clone, Default, Component, Debug)]
|
||||||
pub struct SpatialListener {
|
pub struct SpatialListener {
|
||||||
/// Left ear position relative to the `GlobalTransform`.
|
/// Left ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
|
||||||
pub left_ear_offset: Vec3,
|
pub left_ear_offset: Vec3,
|
||||||
/// Right ear position relative to the `GlobalTransform`.
|
/// Right ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
|
||||||
pub right_ear_offset: Vec3,
|
pub right_ear_offset: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +184,7 @@ impl Default for SpatialListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SpatialListener {
|
impl SpatialListener {
|
||||||
/// Creates a new `SpatialListener` component.
|
/// Creates a new [`SpatialListener`] component.
|
||||||
///
|
///
|
||||||
/// `gap` is the distance between the left and right "ears" of the listener. Ears are
|
/// `gap` is the distance between the left and right "ears" of the listener. Ears are
|
||||||
/// positioned on the x axis.
|
/// positioned on the x axis.
|
||||||
@ -203,12 +205,12 @@ impl SpatialListener {
|
|||||||
pub struct SpatialScale(pub Vec3);
|
pub struct SpatialScale(pub Vec3);
|
||||||
|
|
||||||
impl SpatialScale {
|
impl SpatialScale {
|
||||||
/// Create a new `SpatialScale` with the same value for all 3 dimensions.
|
/// Create a new [`SpatialScale`] with the same value for all 3 dimensions.
|
||||||
pub const fn new(scale: f32) -> Self {
|
pub const fn new(scale: f32) -> Self {
|
||||||
Self(Vec3::splat(scale))
|
Self(Vec3::splat(scale))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `SpatialScale` with the same value for `x` and `y`, and `0.0`
|
/// Create a new [`SpatialScale`] with the same value for `x` and `y`, and `0.0`
|
||||||
/// for `z`.
|
/// for `z`.
|
||||||
pub const fn new_2d(scale: f32) -> Self {
|
pub const fn new_2d(scale: f32) -> Self {
|
||||||
Self(Vec3::new(scale, scale, 0.0))
|
Self(Vec3::new(scale, scale, 0.0))
|
||||||
@ -238,11 +240,11 @@ pub struct DefaultSpatialScale(pub SpatialScale);
|
|||||||
/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
|
/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
|
||||||
/// the audio will not begin playing immediately. The audio will play when the asset is ready.
|
/// the audio will not begin playing immediately. The audio will play when the asset is ready.
|
||||||
///
|
///
|
||||||
/// When Bevy begins the audio playback, an [`AudioSink`][crate::AudioSink] component will be
|
/// When Bevy begins the audio playback, an [`AudioSink`](crate::AudioSink) component will be
|
||||||
/// added to the entity. You can use that component to control the audio settings during playback.
|
/// added to the entity. You can use that component to control the audio settings during playback.
|
||||||
///
|
///
|
||||||
/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the
|
/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the
|
||||||
/// `PlaybackSettings` component will *not* affect already-playing audio.
|
/// [`PlaybackSettings`] component will *not* affect already-playing audio.
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component, Clone)]
|
#[reflect(Component, Clone)]
|
||||||
#[require(PlaybackSettings)]
|
#[require(PlaybackSettings)]
|
||||||
|
|||||||
@ -103,7 +103,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||||||
Entity,
|
Entity,
|
||||||
&AudioPlayer<Source>,
|
&AudioPlayer<Source>,
|
||||||
&PlaybackSettings,
|
&PlaybackSettings,
|
||||||
Option<&GlobalTransform>,
|
&GlobalTransform,
|
||||||
),
|
),
|
||||||
(Without<AudioSink>, Without<SpatialAudioSink>),
|
(Without<AudioSink>, Without<SpatialAudioSink>),
|
||||||
>,
|
>,
|
||||||
@ -118,7 +118,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (entity, source_handle, settings, maybe_emitter_transform) in &query_nonplaying {
|
for (entity, source_handle, settings, emitter_transform) in &query_nonplaying {
|
||||||
let Some(audio_source) = audio_sources.get(&source_handle.0) else {
|
let Some(audio_source) = audio_sources.get(&source_handle.0) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -136,14 +136,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
|
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
|
||||||
|
let emitter_translation = (emitter_transform.translation() * scale).into();
|
||||||
let emitter_translation = if let Some(emitter_transform) = maybe_emitter_transform {
|
|
||||||
(emitter_transform.translation() * scale).into()
|
|
||||||
} else {
|
|
||||||
warn!("Spatial AudioPlayer with no GlobalTransform component. Using zero.");
|
|
||||||
Vec3::ZERO.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
let sink = match SpatialSink::try_new(
|
let sink = match SpatialSink::try_new(
|
||||||
stream_handle,
|
stream_handle,
|
||||||
emitter_translation,
|
emitter_translation,
|
||||||
|
|||||||
@ -24,7 +24,6 @@ bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
|
|||||||
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
||||||
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
||||||
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
|
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||||
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
|
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
|
||||||
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
|
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
|
||||||
@ -39,14 +38,12 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea
|
|||||||
"serialize",
|
"serialize",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
bitflags = "2.3"
|
bitflags = "2.3"
|
||||||
radsort = "0.1"
|
radsort = "0.1"
|
||||||
nonmax = "0.5"
|
nonmax = "0.5"
|
||||||
smallvec = { version = "1", default-features = false }
|
smallvec = { version = "1", default-features = false }
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
bytemuck = { version = "1" }
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use crate::core_3d::{
|
|||||||
prepare_core_3d_depth_textures,
|
prepare_core_3d_depth_textures,
|
||||||
};
|
};
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{load_internal_asset, uuid_handle, Handle};
|
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
|
||||||
use bevy_derive::{Deref, DerefMut};
|
use bevy_derive::{Deref, DerefMut};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
@ -51,8 +51,8 @@ use bitflags::bitflags;
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
/// Identifies the `downsample_depth.wgsl` shader.
|
/// Identifies the `downsample_depth.wgsl` shader.
|
||||||
pub const DOWNSAMPLE_DEPTH_SHADER_HANDLE: Handle<Shader> =
|
#[derive(Resource, Deref)]
|
||||||
uuid_handle!("a09a149e-5922-4fa4-9170-3c1a13065364");
|
pub struct DownsampleDepthShader(Handle<Shader>);
|
||||||
|
|
||||||
/// The maximum number of mip levels that we can produce.
|
/// The maximum number of mip levels that we can produce.
|
||||||
///
|
///
|
||||||
@ -69,18 +69,16 @@ pub struct MipGenerationPlugin;
|
|||||||
|
|
||||||
impl Plugin for MipGenerationPlugin {
|
impl Plugin for MipGenerationPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
load_internal_asset!(
|
embedded_asset!(app, "downsample_depth.wgsl");
|
||||||
app,
|
|
||||||
DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
let downsample_depth_shader = load_embedded_asset!(app, "downsample_depth.wgsl");
|
||||||
"downsample_depth.wgsl",
|
|
||||||
Shader::from_wgsl
|
|
||||||
);
|
|
||||||
|
|
||||||
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
render_app
|
render_app
|
||||||
|
.insert_resource(DownsampleDepthShader(downsample_depth_shader))
|
||||||
.init_resource::<SpecializedComputePipelines<DownsampleDepthPipeline>>()
|
.init_resource::<SpecializedComputePipelines<DownsampleDepthPipeline>>()
|
||||||
.add_render_graph_node::<DownsampleDepthNode>(Core3d, Node3d::EarlyDownsampleDepth)
|
.add_render_graph_node::<DownsampleDepthNode>(Core3d, Node3d::EarlyDownsampleDepth)
|
||||||
.add_render_graph_node::<DownsampleDepthNode>(Core3d, Node3d::LateDownsampleDepth)
|
.add_render_graph_node::<DownsampleDepthNode>(Core3d, Node3d::LateDownsampleDepth)
|
||||||
@ -294,17 +292,21 @@ pub struct DownsampleDepthPipeline {
|
|||||||
bind_group_layout: BindGroupLayout,
|
bind_group_layout: BindGroupLayout,
|
||||||
/// A handle that identifies the compiled shader.
|
/// A handle that identifies the compiled shader.
|
||||||
pipeline_id: Option<CachedComputePipelineId>,
|
pipeline_id: Option<CachedComputePipelineId>,
|
||||||
|
/// The shader asset handle.
|
||||||
|
shader: Handle<Shader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownsampleDepthPipeline {
|
impl DownsampleDepthPipeline {
|
||||||
/// Creates a new [`DownsampleDepthPipeline`] from a bind group layout.
|
/// Creates a new [`DownsampleDepthPipeline`] from a bind group layout and the downsample
|
||||||
|
/// shader.
|
||||||
///
|
///
|
||||||
/// This doesn't actually specialize the pipeline; that must be done
|
/// This doesn't actually specialize the pipeline; that must be done
|
||||||
/// afterward.
|
/// afterward.
|
||||||
fn new(bind_group_layout: BindGroupLayout) -> DownsampleDepthPipeline {
|
fn new(bind_group_layout: BindGroupLayout, shader: Handle<Shader>) -> DownsampleDepthPipeline {
|
||||||
DownsampleDepthPipeline {
|
DownsampleDepthPipeline {
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
pipeline_id: None,
|
pipeline_id: None,
|
||||||
|
shader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,6 +337,7 @@ fn create_downsample_depth_pipelines(
|
|||||||
pipeline_cache: Res<PipelineCache>,
|
pipeline_cache: Res<PipelineCache>,
|
||||||
mut specialized_compute_pipelines: ResMut<SpecializedComputePipelines<DownsampleDepthPipeline>>,
|
mut specialized_compute_pipelines: ResMut<SpecializedComputePipelines<DownsampleDepthPipeline>>,
|
||||||
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
|
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
|
||||||
|
downsample_depth_shader: Res<DownsampleDepthShader>,
|
||||||
mut has_run: Local<bool>,
|
mut has_run: Local<bool>,
|
||||||
) {
|
) {
|
||||||
// Only run once.
|
// Only run once.
|
||||||
@ -368,10 +371,22 @@ fn create_downsample_depth_pipelines(
|
|||||||
|
|
||||||
// Initialize the pipelines.
|
// Initialize the pipelines.
|
||||||
let mut downsample_depth_pipelines = DownsampleDepthPipelines {
|
let mut downsample_depth_pipelines = DownsampleDepthPipelines {
|
||||||
first: DownsampleDepthPipeline::new(standard_bind_group_layout.clone()),
|
first: DownsampleDepthPipeline::new(
|
||||||
second: DownsampleDepthPipeline::new(standard_bind_group_layout.clone()),
|
standard_bind_group_layout.clone(),
|
||||||
first_multisample: DownsampleDepthPipeline::new(multisampled_bind_group_layout.clone()),
|
downsample_depth_shader.0.clone(),
|
||||||
second_multisample: DownsampleDepthPipeline::new(multisampled_bind_group_layout.clone()),
|
),
|
||||||
|
second: DownsampleDepthPipeline::new(
|
||||||
|
standard_bind_group_layout.clone(),
|
||||||
|
downsample_depth_shader.0.clone(),
|
||||||
|
),
|
||||||
|
first_multisample: DownsampleDepthPipeline::new(
|
||||||
|
multisampled_bind_group_layout.clone(),
|
||||||
|
downsample_depth_shader.0.clone(),
|
||||||
|
),
|
||||||
|
second_multisample: DownsampleDepthPipeline::new(
|
||||||
|
multisampled_bind_group_layout.clone(),
|
||||||
|
downsample_depth_shader.0.clone(),
|
||||||
|
),
|
||||||
sampler,
|
sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -491,7 +506,7 @@ impl SpecializedComputePipeline for DownsampleDepthPipeline {
|
|||||||
stages: ShaderStages::COMPUTE,
|
stages: ShaderStages::COMPUTE,
|
||||||
range: 0..4,
|
range: 0..4,
|
||||||
}],
|
}],
|
||||||
shader: DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
shader: self.shader.clone(),
|
||||||
shader_defs,
|
shader_defs,
|
||||||
entry_point: Some(if key.contains(DownsampleDepthPipelineKey::SECOND_PHASE) {
|
entry_point: Some(if key.contains(DownsampleDepthPipelineKey::SECOND_PHASE) {
|
||||||
"downsample_depth_second".into()
|
"downsample_depth_second".into()
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
//! Currently, this consists only of chromatic aberration.
|
//! Currently, this consists only of chromatic aberration.
|
||||||
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{embedded_asset, load_embedded_asset, uuid_handle, Assets, Handle};
|
use bevy_asset::{embedded_asset, load_embedded_asset, Assets, Handle};
|
||||||
use bevy_derive::{Deref, DerefMut};
|
use bevy_derive::{Deref, DerefMut};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
@ -47,13 +47,6 @@ use crate::{
|
|||||||
FullscreenShader,
|
FullscreenShader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The handle to the default chromatic aberration lookup texture.
|
|
||||||
///
|
|
||||||
/// This is just a 3x1 image consisting of one red pixel, one green pixel, and
|
|
||||||
/// one blue pixel, in that order.
|
|
||||||
const DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE: Handle<Image> =
|
|
||||||
uuid_handle!("dc3e3307-40a1-49bb-be6d-e0634e8836b2");
|
|
||||||
|
|
||||||
/// The default chromatic aberration intensity amount, in a fraction of the
|
/// The default chromatic aberration intensity amount, in a fraction of the
|
||||||
/// window size.
|
/// window size.
|
||||||
const DEFAULT_CHROMATIC_ABERRATION_INTENSITY: f32 = 0.02;
|
const DEFAULT_CHROMATIC_ABERRATION_INTENSITY: f32 = 0.02;
|
||||||
@ -68,6 +61,9 @@ const DEFAULT_CHROMATIC_ABERRATION_MAX_SAMPLES: u32 = 8;
|
|||||||
static DEFAULT_CHROMATIC_ABERRATION_LUT_DATA: [u8; 12] =
|
static DEFAULT_CHROMATIC_ABERRATION_LUT_DATA: [u8; 12] =
|
||||||
[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255];
|
[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255];
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct DefaultChromaticAberrationLut(Handle<Image>);
|
||||||
|
|
||||||
/// A plugin that implements a built-in postprocessing stack with some common
|
/// A plugin that implements a built-in postprocessing stack with some common
|
||||||
/// effects.
|
/// effects.
|
||||||
///
|
///
|
||||||
@ -96,14 +92,14 @@ pub struct PostProcessingPlugin;
|
|||||||
pub struct ChromaticAberration {
|
pub struct ChromaticAberration {
|
||||||
/// The lookup texture that determines the color gradient.
|
/// The lookup texture that determines the color gradient.
|
||||||
///
|
///
|
||||||
/// By default, this is a 3×1 texel texture consisting of one red pixel, one
|
/// By default (if None), this is a 3×1 texel texture consisting of one red
|
||||||
/// green pixel, and one blue texel, in that order. This recreates the most
|
/// pixel, one green pixel, and one blue texel, in that order. This
|
||||||
/// typical chromatic aberration pattern. However, you can change it to
|
/// recreates the most typical chromatic aberration pattern. However, you
|
||||||
/// achieve different artistic effects.
|
/// can change it to achieve different artistic effects.
|
||||||
///
|
///
|
||||||
/// The texture is always sampled in its vertical center, so it should
|
/// The texture is always sampled in its vertical center, so it should
|
||||||
/// ordinarily have a height of 1 texel.
|
/// ordinarily have a height of 1 texel.
|
||||||
pub color_lut: Handle<Image>,
|
pub color_lut: Option<Handle<Image>>,
|
||||||
|
|
||||||
/// The size of the streaks around the edges of objects, as a fraction of
|
/// The size of the streaks around the edges of objects, as a fraction of
|
||||||
/// the window size.
|
/// the window size.
|
||||||
@ -192,9 +188,7 @@ impl Plugin for PostProcessingPlugin {
|
|||||||
|
|
||||||
// Load the default chromatic aberration LUT.
|
// Load the default chromatic aberration LUT.
|
||||||
let mut assets = app.world_mut().resource_mut::<Assets<_>>();
|
let mut assets = app.world_mut().resource_mut::<Assets<_>>();
|
||||||
assets.insert(
|
let default_lut = assets.add(Image::new(
|
||||||
DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE.id(),
|
|
||||||
Image::new(
|
|
||||||
Extent3d {
|
Extent3d {
|
||||||
width: 3,
|
width: 3,
|
||||||
height: 1,
|
height: 1,
|
||||||
@ -204,8 +198,7 @@ impl Plugin for PostProcessingPlugin {
|
|||||||
DEFAULT_CHROMATIC_ABERRATION_LUT_DATA.to_vec(),
|
DEFAULT_CHROMATIC_ABERRATION_LUT_DATA.to_vec(),
|
||||||
TextureFormat::Rgba8UnormSrgb,
|
TextureFormat::Rgba8UnormSrgb,
|
||||||
RenderAssetUsages::RENDER_WORLD,
|
RenderAssetUsages::RENDER_WORLD,
|
||||||
),
|
));
|
||||||
);
|
|
||||||
|
|
||||||
app.register_type::<ChromaticAberration>();
|
app.register_type::<ChromaticAberration>();
|
||||||
app.add_plugins(ExtractComponentPlugin::<ChromaticAberration>::default());
|
app.add_plugins(ExtractComponentPlugin::<ChromaticAberration>::default());
|
||||||
@ -215,6 +208,7 @@ impl Plugin for PostProcessingPlugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render_app
|
render_app
|
||||||
|
.insert_resource(DefaultChromaticAberrationLut(default_lut))
|
||||||
.init_resource::<SpecializedRenderPipelines<PostProcessingPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<PostProcessingPipeline>>()
|
||||||
.init_resource::<PostProcessingUniformBuffers>()
|
.init_resource::<PostProcessingUniformBuffers>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@ -258,7 +252,7 @@ impl Plugin for PostProcessingPlugin {
|
|||||||
impl Default for ChromaticAberration {
|
impl Default for ChromaticAberration {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color_lut: DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE,
|
color_lut: None,
|
||||||
intensity: DEFAULT_CHROMATIC_ABERRATION_INTENSITY,
|
intensity: DEFAULT_CHROMATIC_ABERRATION_INTENSITY,
|
||||||
max_samples: DEFAULT_CHROMATIC_ABERRATION_MAX_SAMPLES,
|
max_samples: DEFAULT_CHROMATIC_ABERRATION_MAX_SAMPLES,
|
||||||
}
|
}
|
||||||
@ -357,6 +351,7 @@ impl ViewNode for PostProcessingNode {
|
|||||||
let post_processing_pipeline = world.resource::<PostProcessingPipeline>();
|
let post_processing_pipeline = world.resource::<PostProcessingPipeline>();
|
||||||
let post_processing_uniform_buffers = world.resource::<PostProcessingUniformBuffers>();
|
let post_processing_uniform_buffers = world.resource::<PostProcessingUniformBuffers>();
|
||||||
let gpu_image_assets = world.resource::<RenderAssets<GpuImage>>();
|
let gpu_image_assets = world.resource::<RenderAssets<GpuImage>>();
|
||||||
|
let default_lut = world.resource::<DefaultChromaticAberrationLut>();
|
||||||
|
|
||||||
// We need a render pipeline to be prepared.
|
// We need a render pipeline to be prepared.
|
||||||
let Some(pipeline) = pipeline_cache.get_render_pipeline(**pipeline_id) else {
|
let Some(pipeline) = pipeline_cache.get_render_pipeline(**pipeline_id) else {
|
||||||
@ -364,8 +359,12 @@ impl ViewNode for PostProcessingNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// We need the chromatic aberration LUT to be present.
|
// We need the chromatic aberration LUT to be present.
|
||||||
let Some(chromatic_aberration_lut) = gpu_image_assets.get(&chromatic_aberration.color_lut)
|
let Some(chromatic_aberration_lut) = gpu_image_assets.get(
|
||||||
else {
|
chromatic_aberration
|
||||||
|
.color_lut
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&default_lut.0),
|
||||||
|
) else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -18,8 +18,6 @@ bevy_input_focus = { path = "../bevy_input_focus", version = "0.17.0-dev" }
|
|||||||
bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
|
bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||||
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
|
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
|
||||||
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
|
||||||
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev", features = [
|
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev", features = [
|
||||||
"bevy_ui_picking_backend",
|
"bevy_ui_picking_backend",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@ -176,7 +176,7 @@ impl Default for SliderRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the amount by which to increment or decrement the slider value when using keyboard
|
/// Defines the amount by which to increment or decrement the slider value when using keyboard
|
||||||
/// shorctuts. Defaults to 1.0.
|
/// shortcuts. Defaults to 1.0.
|
||||||
#[derive(Component, Debug, PartialEq, Clone)]
|
#[derive(Component, Debug, PartialEq, Clone)]
|
||||||
#[component(immutable)]
|
#[component(immutable)]
|
||||||
pub struct SliderStep(pub f32);
|
pub struct SliderStep(pub f32);
|
||||||
|
|||||||
@ -18,14 +18,12 @@ bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
|||||||
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
||||||
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
|
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||||
bevy_input = { path = "../bevy_input", version = "0.17.0-dev" }
|
|
||||||
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
|
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
|
||||||
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
||||||
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
||||||
bevy_text = { path = "../bevy_text", version = "0.17.0-dev" }
|
bevy_text = { path = "../bevy_text", version = "0.17.0-dev" }
|
||||||
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev" }
|
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
||||||
bevy_state = { path = "../bevy_state", version = "0.17.0-dev" }
|
bevy_state = { path = "../bevy_state", version = "0.17.0-dev" }
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local<u32>) {
|
|||||||
debug!("Handling event: {:?}", event);
|
debug!("Handling event: {:?}", event);
|
||||||
match event {
|
match event {
|
||||||
CiTestingEvent::AppExit => {
|
CiTestingEvent::AppExit => {
|
||||||
world.send_event(AppExit::Success);
|
world.write_event(AppExit::Success);
|
||||||
info!("Exiting after {} frames. Test successful!", *current_frame);
|
info!("Exiting after {} frames. Test successful!", *current_frame);
|
||||||
}
|
}
|
||||||
CiTestingEvent::ScreenshotAndExit => {
|
CiTestingEvent::ScreenshotAndExit => {
|
||||||
@ -53,7 +53,7 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local<u32>) {
|
|||||||
}
|
}
|
||||||
// Custom events are forwarded to the world.
|
// Custom events are forwarded to the world.
|
||||||
CiTestingEvent::Custom(event_string) => {
|
CiTestingEvent::Custom(event_string) => {
|
||||||
world.send_event(CiTestingCustomEvent(event_string));
|
world.write_event(CiTestingCustomEvent(event_string));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,6 @@ critical-section = [
|
|||||||
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
|
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_time = { path = "../bevy_time", version = "0.17.0-dev", default-features = false }
|
bevy_time = { path = "../bevy_time", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
|
|
||||||
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false }
|
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"alloc",
|
"alloc",
|
||||||
|
|||||||
@ -630,7 +630,7 @@ impl Archetype {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> u32 {
|
pub fn len(&self) -> u32 {
|
||||||
// No entity may have more than one archetype row, so there are no duplicates,
|
// No entity may have more than one archetype row, so there are no duplicates,
|
||||||
// and there may only ever be u32::MAX entities, so the length never exceeds u32's cappacity.
|
// and there may only ever be u32::MAX entities, so the length never exceeds u32's capacity.
|
||||||
self.entities.len() as u32
|
self.entities.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1584,7 +1584,7 @@ impl<'w> BundleRemover<'w> {
|
|||||||
// Handle sparse set removes
|
// Handle sparse set removes
|
||||||
for component_id in self.bundle_info.as_ref().iter_explicit_components() {
|
for component_id in self.bundle_info.as_ref().iter_explicit_components() {
|
||||||
if self.old_archetype.as_ref().contains(component_id) {
|
if self.old_archetype.as_ref().contains(component_id) {
|
||||||
world.removed_components.send(component_id, entity);
|
world.removed_components.write(component_id, entity);
|
||||||
|
|
||||||
// Make sure to drop components stored in sparse sets.
|
// Make sure to drop components stored in sparse sets.
|
||||||
// Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
|
// Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
|
||||||
|
|||||||
@ -795,47 +795,34 @@ impl<'w> EntityClonerBuilder<'w, OptOut> {
|
|||||||
/// this behavior.
|
/// this behavior.
|
||||||
pub fn deny<T: Bundle>(&mut self) -> &mut Self {
|
pub fn deny<T: Bundle>(&mut self) -> &mut Self {
|
||||||
let bundle_id = self.world.register_bundle::<T>().id();
|
let bundle_id = self.world.register_bundle::<T>().id();
|
||||||
self.deny_by_bundle_id(bundle_id)
|
self.deny_by_ids(bundle_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disallows all components of the bundle ID from being cloned.
|
/// Extends the list of components that shouldn't be cloned.
|
||||||
|
/// Supports filtering by [`TypeId`], [`ComponentId`], [`BundleId`], and [`IntoIterator`] yielding one of these.
|
||||||
///
|
///
|
||||||
/// If component `A` is denied here and component `B` requires `A`, then `A`
|
/// If component `A` is denied here and component `B` requires `A`, then `A`
|
||||||
/// is denied as well. See [`Self::without_required_by_components`] to alter
|
/// is denied as well. See [`Self::without_required_by_components`] to alter
|
||||||
/// this behavior.
|
/// this behavior.
|
||||||
pub fn deny_by_bundle_id(&mut self, bundle_id: BundleId) -> &mut Self {
|
pub fn deny_by_ids<M: Marker>(&mut self, ids: impl FilterableIds<M>) -> &mut Self {
|
||||||
|
ids.filter_ids(&mut |ids| match ids {
|
||||||
|
FilterableId::Type(type_id) => {
|
||||||
|
if let Some(id) = self.world.components().get_valid_id(type_id) {
|
||||||
|
self.filter.filter_deny(id, self.world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FilterableId::Component(component_id) => {
|
||||||
|
self.filter.filter_deny(component_id, self.world);
|
||||||
|
}
|
||||||
|
FilterableId::Bundle(bundle_id) => {
|
||||||
if let Some(bundle) = self.world.bundles().get(bundle_id) {
|
if let Some(bundle) = self.world.bundles().get(bundle_id) {
|
||||||
let ids = bundle.explicit_components().iter();
|
let ids = bundle.explicit_components().iter();
|
||||||
for &id in ids {
|
for &id in ids {
|
||||||
self.filter.filter_deny(id, self.world);
|
self.filter.filter_deny(id, self.world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extends the list of components that shouldn't be cloned.
|
|
||||||
///
|
|
||||||
/// If component `A` is denied here and component `B` requires `A`, then `A`
|
|
||||||
/// is denied as well. See [`Self::without_required_by_components`] to alter
|
|
||||||
/// this behavior.
|
|
||||||
pub fn deny_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
|
|
||||||
for id in ids {
|
|
||||||
self.filter.filter_deny(id, self.world);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extends the list of components that shouldn't be cloned by type ids.
|
|
||||||
///
|
|
||||||
/// If component `A` is denied here and component `B` requires `A`, then `A`
|
|
||||||
/// is denied as well. See [`Self::without_required_by_components`] to alter
|
|
||||||
/// this behavior.
|
|
||||||
pub fn deny_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
|
|
||||||
for type_id in ids {
|
|
||||||
if let Some(id) = self.world.components().get_valid_id(type_id) {
|
|
||||||
self.filter.filter_deny(id, self.world);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -865,7 +852,7 @@ impl<'w> EntityClonerBuilder<'w, OptIn> {
|
|||||||
/// to alter this behavior.
|
/// to alter this behavior.
|
||||||
pub fn allow<T: Bundle>(&mut self) -> &mut Self {
|
pub fn allow<T: Bundle>(&mut self) -> &mut Self {
|
||||||
let bundle_id = self.world.register_bundle::<T>().id();
|
let bundle_id = self.world.register_bundle::<T>().id();
|
||||||
self.allow_by_bundle_id(bundle_id)
|
self.allow_by_ids(bundle_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds all components of the bundle to the list of components to clone if
|
/// Adds all components of the bundle to the list of components to clone if
|
||||||
@ -876,94 +863,55 @@ impl<'w> EntityClonerBuilder<'w, OptIn> {
|
|||||||
/// to alter this behavior.
|
/// to alter this behavior.
|
||||||
pub fn allow_if_new<T: Bundle>(&mut self) -> &mut Self {
|
pub fn allow_if_new<T: Bundle>(&mut self) -> &mut Self {
|
||||||
let bundle_id = self.world.register_bundle::<T>().id();
|
let bundle_id = self.world.register_bundle::<T>().id();
|
||||||
self.allow_by_bundle_id_if_new(bundle_id)
|
self.allow_by_ids_if_new(bundle_id)
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds all components of the bundle ID to the list of components to clone.
|
|
||||||
///
|
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
|
||||||
/// to alter this behavior.
|
|
||||||
pub fn allow_by_bundle_id(&mut self, bundle_id: BundleId) -> &mut Self {
|
|
||||||
if let Some(bundle) = self.world.bundles().get(bundle_id) {
|
|
||||||
let ids = bundle.explicit_components().iter();
|
|
||||||
for &id in ids {
|
|
||||||
self.filter
|
|
||||||
.filter_allow(id, self.world, InsertMode::Replace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds all components of the bundle ID to the list of components to clone
|
|
||||||
/// if the target does not contain them.
|
|
||||||
///
|
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
|
||||||
/// to alter this behavior.
|
|
||||||
pub fn allow_by_bundle_id_if_new(&mut self, bundle_id: BundleId) -> &mut Self {
|
|
||||||
if let Some(bundle) = self.world.bundles().get(bundle_id) {
|
|
||||||
let ids = bundle.explicit_components().iter();
|
|
||||||
for &id in ids {
|
|
||||||
self.filter.filter_allow(id, self.world, InsertMode::Keep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the list of components to clone.
|
/// Extends the list of components to clone.
|
||||||
|
/// Supports filtering by [`TypeId`], [`ComponentId`], [`BundleId`], and [`IntoIterator`] yielding one of these.
|
||||||
///
|
///
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
/// If component `A` is allowed here and requires component `B`, then `B`
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
/// is allowed as well. See [`Self::without_required_components`]
|
||||||
/// to alter this behavior.
|
/// to alter this behavior.
|
||||||
pub fn allow_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
|
pub fn allow_by_ids<M: Marker>(&mut self, ids: impl FilterableIds<M>) -> &mut Self {
|
||||||
for id in ids {
|
self.allow_by_ids_inner(ids, InsertMode::Replace);
|
||||||
self.filter
|
|
||||||
.filter_allow(id, self.world, InsertMode::Replace);
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the list of components to clone if the target does not contain them.
|
/// Extends the list of components to clone if the target does not contain them.
|
||||||
|
/// Supports filtering by [`TypeId`], [`ComponentId`], [`BundleId`], and [`IntoIterator`] yielding one of these.
|
||||||
///
|
///
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
/// If component `A` is allowed here and requires component `B`, then `B`
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
/// is allowed as well. See [`Self::without_required_components`]
|
||||||
/// to alter this behavior.
|
/// to alter this behavior.
|
||||||
pub fn allow_by_ids_if_new(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
|
pub fn allow_by_ids_if_new<M: Marker>(&mut self, ids: impl FilterableIds<M>) -> &mut Self {
|
||||||
for id in ids {
|
self.allow_by_ids_inner(ids, InsertMode::Keep);
|
||||||
self.filter.filter_allow(id, self.world, InsertMode::Keep);
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the list of components to clone using [`TypeId`]s.
|
fn allow_by_ids_inner<M: Marker>(
|
||||||
///
|
&mut self,
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
ids: impl FilterableIds<M>,
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
insert_mode: InsertMode,
|
||||||
/// to alter this behavior.
|
) {
|
||||||
pub fn allow_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
|
ids.filter_ids(&mut |id| match id {
|
||||||
for type_id in ids {
|
FilterableId::Type(type_id) => {
|
||||||
if let Some(id) = self.world.components().get_valid_id(type_id) {
|
if let Some(id) = self.world.components().get_valid_id(type_id) {
|
||||||
|
self.filter.filter_allow(id, self.world, insert_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FilterableId::Component(component_id) => {
|
||||||
self.filter
|
self.filter
|
||||||
.filter_allow(id, self.world, InsertMode::Replace);
|
.filter_allow(component_id, self.world, insert_mode);
|
||||||
|
}
|
||||||
|
FilterableId::Bundle(bundle_id) => {
|
||||||
|
if let Some(bundle) = self.world.bundles().get(bundle_id) {
|
||||||
|
let ids = bundle.explicit_components().iter();
|
||||||
|
for &id in ids {
|
||||||
|
self.filter.filter_allow(id, self.world, insert_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
/// Extends the list of components to clone using [`TypeId`]s if the target
|
|
||||||
/// does not contain them.
|
|
||||||
///
|
|
||||||
/// If component `A` is allowed here and requires component `B`, then `B`
|
|
||||||
/// is allowed as well. See [`Self::without_required_components`]
|
|
||||||
/// to alter this behavior.
|
|
||||||
pub fn allow_by_type_ids_if_new(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
|
|
||||||
for type_id in ids {
|
|
||||||
if let Some(id) = self.world.components().get_valid_id(type_id) {
|
|
||||||
self.filter.filter_allow(id, self.world, InsertMode::Keep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,6 +1257,77 @@ impl Required {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Marker trait to allow multiple blanket implementations for [`FilterableIds`].
|
||||||
|
pub trait Marker {}
|
||||||
|
/// Marker struct for [`FilterableIds`] implementation for single-value types.
|
||||||
|
pub struct ScalarType {}
|
||||||
|
impl Marker for ScalarType {}
|
||||||
|
/// Marker struct for [`FilterableIds`] implementation for [`IntoIterator`] types.
|
||||||
|
pub struct VectorType {}
|
||||||
|
impl Marker for VectorType {}
|
||||||
|
|
||||||
|
/// Defines types of ids that [`EntityClonerBuilder`] can filter components by.
|
||||||
|
#[derive(From)]
|
||||||
|
pub enum FilterableId {
|
||||||
|
Type(TypeId),
|
||||||
|
Component(ComponentId),
|
||||||
|
Bundle(BundleId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<&'a T> for FilterableId
|
||||||
|
where
|
||||||
|
T: Into<FilterableId> + Copy,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn from(value: &'a T) -> Self {
|
||||||
|
(*value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait to allow [`EntityClonerBuilder`] filter by any supported id type and their iterators,
|
||||||
|
/// reducing the number of method permutations required for all id types.
|
||||||
|
///
|
||||||
|
/// The supported id types that can be used to filter components are defined by [`FilterableId`], which allows following types: [`TypeId`], [`ComponentId`] and [`BundleId`].
|
||||||
|
///
|
||||||
|
/// `M` is a generic marker to allow multiple blanket implementations of this trait.
|
||||||
|
/// This works because `FilterableId<M1>` is a different trait from `FilterableId<M2>`, so multiple blanket implementations for different `M` are allowed.
|
||||||
|
/// The reason this is required is because supporting `IntoIterator` requires blanket implementation, but that will conflict with implementation for `TypeId`
|
||||||
|
/// since `IntoIterator` can technically be implemented for `TypeId` in the future.
|
||||||
|
/// Functions like `allow_by_ids` rely on type inference to automatically select proper type for `M` at call site.
|
||||||
|
pub trait FilterableIds<M: Marker> {
|
||||||
|
/// Takes in a function that processes all types of [`FilterableId`] one-by-one.
|
||||||
|
fn filter_ids(self, ids: &mut impl FnMut(FilterableId));
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, T> FilterableIds<VectorType> for I
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
T: Into<FilterableId>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn filter_ids(self, ids: &mut impl FnMut(FilterableId)) {
|
||||||
|
for id in self.into_iter() {
|
||||||
|
ids(id.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FilterableIds<ScalarType> for T
|
||||||
|
where
|
||||||
|
T: Into<FilterableId>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn filter_ids(self, ids: &mut impl FnMut(FilterableId)) {
|
||||||
|
ids(self.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use private::{FilterableId, FilterableIds, Marker};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -54,8 +54,8 @@ use {
|
|||||||
/// // run this once per update/frame
|
/// // run this once per update/frame
|
||||||
/// events.update();
|
/// events.update();
|
||||||
///
|
///
|
||||||
/// // somewhere else: send an event
|
/// // somewhere else: write an event
|
||||||
/// events.send(MyEvent { value: 1 });
|
/// events.write(MyEvent { value: 1 });
|
||||||
///
|
///
|
||||||
/// // somewhere else: read the events
|
/// // somewhere else: read the events
|
||||||
/// for event in cursor.read(&events) {
|
/// for event in cursor.read(&events) {
|
||||||
@ -118,22 +118,22 @@ impl<E: BufferedEvent> Events<E> {
|
|||||||
self.events_a.start_event_count
|
self.events_a.start_event_count
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "Sends" an `event` by writing it to the current event buffer.
|
/// Writes an `event` to the current event buffer.
|
||||||
/// [`EventReader`](super::EventReader)s can then read the event.
|
/// [`EventReader`](super::EventReader)s can then read the event.
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
/// This method returns the [ID](`EventId`) of the written `event`.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn send(&mut self, event: E) -> EventId<E> {
|
pub fn write(&mut self, event: E) -> EventId<E> {
|
||||||
self.send_with_caller(event, MaybeLocation::caller())
|
self.write_with_caller(event, MaybeLocation::caller())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_with_caller(&mut self, event: E, caller: MaybeLocation) -> EventId<E> {
|
pub(crate) fn write_with_caller(&mut self, event: E, caller: MaybeLocation) -> EventId<E> {
|
||||||
let event_id = EventId {
|
let event_id = EventId {
|
||||||
id: self.event_count,
|
id: self.event_count,
|
||||||
caller,
|
caller,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "detailed_trace")]
|
#[cfg(feature = "detailed_trace")]
|
||||||
tracing::trace!("Events::send() -> id: {}", event_id);
|
tracing::trace!("Events::write() -> id: {}", event_id);
|
||||||
|
|
||||||
let event_instance = EventInstance { event_id, event };
|
let event_instance = EventInstance { event_id, event };
|
||||||
|
|
||||||
@ -143,30 +143,59 @@ impl<E: BufferedEvent> Events<E> {
|
|||||||
event_id
|
event_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
|
/// Writes a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
|
||||||
/// This is more efficient than sending each event individually.
|
/// This is more efficient than writing each event individually.
|
||||||
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
/// This method returns the [IDs](`EventId`) of the written `events`.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
pub fn write_batch(&mut self, events: impl IntoIterator<Item = E>) -> WriteBatchIds<E> {
|
||||||
let last_count = self.event_count;
|
let last_count = self.event_count;
|
||||||
|
|
||||||
self.extend(events);
|
self.extend(events);
|
||||||
|
|
||||||
SendBatchIds {
|
WriteBatchIds {
|
||||||
last_count,
|
last_count,
|
||||||
event_count: self.event_count,
|
event_count: self.event_count,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes the default value of the event. Useful when the event is an empty struct.
|
||||||
|
/// This method returns the [ID](`EventId`) of the written `event`.
|
||||||
|
#[track_caller]
|
||||||
|
pub fn write_default(&mut self) -> EventId<E>
|
||||||
|
where
|
||||||
|
E: Default,
|
||||||
|
{
|
||||||
|
self.write(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "Sends" an `event` by writing it to the current event buffer.
|
||||||
|
/// [`EventReader`](super::EventReader)s can then read the event.
|
||||||
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `Events<E>::write` instead.")]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn send(&mut self, event: E) -> EventId<E> {
|
||||||
|
self.write(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
|
||||||
|
/// This is more efficient than sending each event individually.
|
||||||
|
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `Events<E>::write_batch` instead.")]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> WriteBatchIds<E> {
|
||||||
|
self.write_batch(events)
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends the default value of the event. Useful when the event is an empty struct.
|
/// Sends the default value of the event. Useful when the event is an empty struct.
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `Events<E>::write_default` instead.")]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn send_default(&mut self) -> EventId<E>
|
pub fn send_default(&mut self) -> EventId<E>
|
||||||
where
|
where
|
||||||
E: Default,
|
E: Default,
|
||||||
{
|
{
|
||||||
self.send(Default::default())
|
self.write_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a new [`EventCursor`]. This will include all events already in the event buffers.
|
/// Gets a new [`EventCursor`]. This will include all events already in the event buffers.
|
||||||
@ -351,14 +380,18 @@ impl<E: BufferedEvent> DerefMut for EventSequence<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`Iterator`] over sent [`EventIds`](`EventId`) from a batch.
|
/// [`Iterator`] over written [`EventIds`](`EventId`) from a batch.
|
||||||
pub struct SendBatchIds<E> {
|
pub struct WriteBatchIds<E> {
|
||||||
last_count: usize,
|
last_count: usize,
|
||||||
event_count: usize,
|
event_count: usize,
|
||||||
_marker: PhantomData<E>,
|
_marker: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: BufferedEvent> Iterator for SendBatchIds<E> {
|
/// [`Iterator`] over sent [`EventIds`](`EventId`) from a batch.
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `WriteBatchIds` instead.")]
|
||||||
|
pub type SendBatchIds<E> = WriteBatchIds<E>;
|
||||||
|
|
||||||
|
impl<E: BufferedEvent> Iterator for WriteBatchIds<E> {
|
||||||
type Item = EventId<E>;
|
type Item = EventId<E>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@ -378,7 +411,7 @@ impl<E: BufferedEvent> Iterator for SendBatchIds<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: BufferedEvent> ExactSizeIterator for SendBatchIds<E> {
|
impl<E: BufferedEvent> ExactSizeIterator for WriteBatchIds<E> {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.event_count.saturating_sub(self.last_count)
|
self.event_count.saturating_sub(self.last_count)
|
||||||
}
|
}
|
||||||
@ -400,22 +433,22 @@ mod tests {
|
|||||||
assert_eq!(test_events.iter_current_update_events().count(), 0);
|
assert_eq!(test_events.iter_current_update_events().count(), 0);
|
||||||
test_events.update();
|
test_events.update();
|
||||||
|
|
||||||
// Sending one event
|
// Writing one event
|
||||||
test_events.send(TestEvent);
|
test_events.write(TestEvent);
|
||||||
|
|
||||||
assert_eq!(test_events.len(), 1);
|
assert_eq!(test_events.len(), 1);
|
||||||
assert_eq!(test_events.iter_current_update_events().count(), 1);
|
assert_eq!(test_events.iter_current_update_events().count(), 1);
|
||||||
test_events.update();
|
test_events.update();
|
||||||
|
|
||||||
// Sending two events on the next frame
|
// Writing two events on the next frame
|
||||||
test_events.send(TestEvent);
|
test_events.write(TestEvent);
|
||||||
test_events.send(TestEvent);
|
test_events.write(TestEvent);
|
||||||
|
|
||||||
assert_eq!(test_events.len(), 3); // Events are double-buffered, so we see 1 + 2 = 3
|
assert_eq!(test_events.len(), 3); // Events are double-buffered, so we see 1 + 2 = 3
|
||||||
assert_eq!(test_events.iter_current_update_events().count(), 2);
|
assert_eq!(test_events.iter_current_update_events().count(), 2);
|
||||||
test_events.update();
|
test_events.update();
|
||||||
|
|
||||||
// Sending zero events
|
// Writing zero events
|
||||||
assert_eq!(test_events.len(), 2); // Events are double-buffered, so we see 2 + 0 = 2
|
assert_eq!(test_events.len(), 2); // Events are double-buffered, so we see 2 + 0 = 2
|
||||||
assert_eq!(test_events.iter_current_update_events().count(), 0);
|
assert_eq!(test_events.iter_current_update_events().count(), 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ use core::marker::PhantomData;
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// for event in events_to_resend {
|
/// for event in events_to_resend {
|
||||||
/// events.send(MyEvent);
|
/// events.write(MyEvent);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|||||||
@ -13,7 +13,8 @@ mod writer;
|
|||||||
pub(crate) use base::EventInstance;
|
pub(crate) use base::EventInstance;
|
||||||
pub use base::{BufferedEvent, EntityEvent, Event, EventId, EventKey};
|
pub use base::{BufferedEvent, EntityEvent, Event, EventId, EventKey};
|
||||||
pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event};
|
pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event};
|
||||||
pub use collections::{Events, SendBatchIds};
|
#[expect(deprecated, reason = "`SendBatchIds` was renamed to `WriteBatchIds`.")]
|
||||||
|
pub use collections::{Events, SendBatchIds, WriteBatchIds};
|
||||||
pub use event_cursor::EventCursor;
|
pub use event_cursor::EventCursor;
|
||||||
#[cfg(feature = "multi_threaded")]
|
#[cfg(feature = "multi_threaded")]
|
||||||
pub use iterators::EventParIter;
|
pub use iterators::EventParIter;
|
||||||
@ -68,7 +69,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut reader_a: EventCursor<TestEvent> = events.get_cursor();
|
let mut reader_a: EventCursor<TestEvent> = events.get_cursor();
|
||||||
|
|
||||||
events.send(event_0);
|
events.write(event_0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_events(&events, &mut reader_a),
|
get_events(&events, &mut reader_a),
|
||||||
@ -94,7 +95,7 @@ mod tests {
|
|||||||
"second iteration of reader_b created after event results in zero events"
|
"second iteration of reader_b created after event results in zero events"
|
||||||
);
|
);
|
||||||
|
|
||||||
events.send(event_1);
|
events.write(event_1);
|
||||||
|
|
||||||
let mut reader_c = events.get_cursor();
|
let mut reader_c = events.get_cursor();
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut reader_d = events.get_cursor();
|
let mut reader_d = events.get_cursor();
|
||||||
|
|
||||||
events.send(event_2);
|
events.write(event_2);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_events(&events, &mut reader_a),
|
get_events(&events, &mut reader_a),
|
||||||
@ -153,17 +154,17 @@ mod tests {
|
|||||||
|
|
||||||
assert!(reader.read(&events).next().is_none());
|
assert!(reader.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert_eq!(*reader.read(&events).next().unwrap(), TestEvent { i: 0 });
|
assert_eq!(*reader.read(&events).next().unwrap(), TestEvent { i: 0 });
|
||||||
assert_eq!(reader.read(&events).next(), None);
|
assert_eq!(reader.read(&events).next(), None);
|
||||||
|
|
||||||
events.send(TestEvent { i: 1 });
|
events.write(TestEvent { i: 1 });
|
||||||
clear_func(&mut events);
|
clear_func(&mut events);
|
||||||
assert!(reader.read(&events).next().is_none());
|
assert!(reader.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 2 });
|
events.write(TestEvent { i: 2 });
|
||||||
events.update();
|
events.update();
|
||||||
events.send(TestEvent { i: 3 });
|
events.write(TestEvent { i: 3 });
|
||||||
|
|
||||||
assert!(reader
|
assert!(reader
|
||||||
.read(&events)
|
.read(&events)
|
||||||
@ -185,22 +186,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_events_send_default() {
|
fn test_events_write_default() {
|
||||||
let mut events = Events::<EmptyTestEvent>::default();
|
let mut events = Events::<EmptyTestEvent>::default();
|
||||||
events.send_default();
|
events.write_default();
|
||||||
|
|
||||||
let mut reader = events.get_cursor();
|
let mut reader = events.get_cursor();
|
||||||
assert_eq!(get_events(&events, &mut reader), vec![EmptyTestEvent]);
|
assert_eq!(get_events(&events, &mut reader), vec![EmptyTestEvent]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_send_events_ids() {
|
fn test_write_events_ids() {
|
||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
let event_0 = TestEvent { i: 0 };
|
let event_0 = TestEvent { i: 0 };
|
||||||
let event_1 = TestEvent { i: 1 };
|
let event_1 = TestEvent { i: 1 };
|
||||||
let event_2 = TestEvent { i: 2 };
|
let event_2 = TestEvent { i: 2 };
|
||||||
|
|
||||||
let event_0_id = events.send(event_0);
|
let event_0_id = events.write(event_0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
events.get_event(event_0_id.id),
|
events.get_event(event_0_id.id),
|
||||||
@ -208,7 +209,7 @@ mod tests {
|
|||||||
"Getting a sent event by ID should return the original event"
|
"Getting a sent event by ID should return the original event"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut event_ids = events.send_batch([event_1, event_2]);
|
let mut event_ids = events.write_batch([event_1, event_2]);
|
||||||
|
|
||||||
let event_id = event_ids.next().expect("Event 1 must have been sent");
|
let event_id = event_ids.next().expect("Event 1 must have been sent");
|
||||||
|
|
||||||
@ -253,14 +254,14 @@ mod tests {
|
|||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
let mut reader = events.get_cursor();
|
let mut reader = events.get_cursor();
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
events.send(TestEvent { i: 1 });
|
events.write(TestEvent { i: 1 });
|
||||||
assert_eq!(reader.read(&events).count(), 2);
|
assert_eq!(reader.read(&events).count(), 2);
|
||||||
|
|
||||||
let mut old_events = Vec::from_iter(events.update_drain());
|
let mut old_events = Vec::from_iter(events.update_drain());
|
||||||
assert!(old_events.is_empty());
|
assert!(old_events.is_empty());
|
||||||
|
|
||||||
events.send(TestEvent { i: 2 });
|
events.write(TestEvent { i: 2 });
|
||||||
assert_eq!(reader.read(&events).count(), 1);
|
assert_eq!(reader.read(&events).count(), 1);
|
||||||
|
|
||||||
old_events.extend(events.update_drain());
|
old_events.extend(events.update_drain());
|
||||||
@ -278,7 +279,7 @@ mod tests {
|
|||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
assert!(events.is_empty());
|
assert!(events.is_empty());
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert!(!events.is_empty());
|
assert!(!events.is_empty());
|
||||||
|
|
||||||
events.update();
|
events.update();
|
||||||
@ -308,12 +309,12 @@ mod tests {
|
|||||||
let mut cursor = events.get_cursor();
|
let mut cursor = events.get_cursor();
|
||||||
assert!(cursor.read(&events).next().is_none());
|
assert!(cursor.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
let sent_event = cursor.read(&events).next().unwrap();
|
let sent_event = cursor.read(&events).next().unwrap();
|
||||||
assert_eq!(sent_event, &TestEvent { i: 0 });
|
assert_eq!(sent_event, &TestEvent { i: 0 });
|
||||||
assert!(cursor.read(&events).next().is_none());
|
assert!(cursor.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 2 });
|
events.write(TestEvent { i: 2 });
|
||||||
let sent_event = cursor.read(&events).next().unwrap();
|
let sent_event = cursor.read(&events).next().unwrap();
|
||||||
assert_eq!(sent_event, &TestEvent { i: 2 });
|
assert_eq!(sent_event, &TestEvent { i: 2 });
|
||||||
assert!(cursor.read(&events).next().is_none());
|
assert!(cursor.read(&events).next().is_none());
|
||||||
@ -330,7 +331,7 @@ mod tests {
|
|||||||
assert!(write_cursor.read_mut(&mut events).next().is_none());
|
assert!(write_cursor.read_mut(&mut events).next().is_none());
|
||||||
assert!(read_cursor.read(&events).next().is_none());
|
assert!(read_cursor.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
let sent_event = write_cursor.read_mut(&mut events).next().unwrap();
|
let sent_event = write_cursor.read_mut(&mut events).next().unwrap();
|
||||||
assert_eq!(sent_event, &mut TestEvent { i: 0 });
|
assert_eq!(sent_event, &mut TestEvent { i: 0 });
|
||||||
*sent_event = TestEvent { i: 1 }; // Mutate whole event
|
*sent_event = TestEvent { i: 1 }; // Mutate whole event
|
||||||
@ -340,7 +341,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(read_cursor.read(&events).next().is_none());
|
assert!(read_cursor.read(&events).next().is_none());
|
||||||
|
|
||||||
events.send(TestEvent { i: 2 });
|
events.write(TestEvent { i: 2 });
|
||||||
let sent_event = write_cursor.read_mut(&mut events).next().unwrap();
|
let sent_event = write_cursor.read_mut(&mut events).next().unwrap();
|
||||||
assert_eq!(sent_event, &mut TestEvent { i: 2 });
|
assert_eq!(sent_event, &mut TestEvent { i: 2 });
|
||||||
sent_event.i = 3; // Mutate sub value
|
sent_event.i = 3; // Mutate sub value
|
||||||
@ -360,7 +361,7 @@ mod tests {
|
|||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
let mut reader = events.get_cursor();
|
let mut reader = events.get_cursor();
|
||||||
|
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert_eq!(reader.len(&events), 1);
|
assert_eq!(reader.len(&events), 1);
|
||||||
reader.clear(&events);
|
reader.clear(&events);
|
||||||
assert_eq!(reader.len(&events), 0);
|
assert_eq!(reader.len(&events), 0);
|
||||||
@ -369,12 +370,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_event_cursor_len_update() {
|
fn test_event_cursor_len_update() {
|
||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
let reader = events.get_cursor();
|
let reader = events.get_cursor();
|
||||||
assert_eq!(reader.len(&events), 2);
|
assert_eq!(reader.len(&events), 2);
|
||||||
events.update();
|
events.update();
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert_eq!(reader.len(&events), 3);
|
assert_eq!(reader.len(&events), 3);
|
||||||
events.update();
|
events.update();
|
||||||
assert_eq!(reader.len(&events), 1);
|
assert_eq!(reader.len(&events), 1);
|
||||||
@ -385,10 +386,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_event_cursor_len_current() {
|
fn test_event_cursor_len_current() {
|
||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
let reader = events.get_cursor_current();
|
let reader = events.get_cursor_current();
|
||||||
assert!(reader.is_empty(&events));
|
assert!(reader.is_empty(&events));
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert_eq!(reader.len(&events), 1);
|
assert_eq!(reader.len(&events), 1);
|
||||||
assert!(!reader.is_empty(&events));
|
assert!(!reader.is_empty(&events));
|
||||||
}
|
}
|
||||||
@ -396,9 +397,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_event_cursor_iter_len_updated() {
|
fn test_event_cursor_iter_len_updated() {
|
||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
events.send(TestEvent { i: 1 });
|
events.write(TestEvent { i: 1 });
|
||||||
events.send(TestEvent { i: 2 });
|
events.write(TestEvent { i: 2 });
|
||||||
let mut reader = events.get_cursor();
|
let mut reader = events.get_cursor();
|
||||||
let mut iter = reader.read(&events);
|
let mut iter = reader.read(&events);
|
||||||
assert_eq!(iter.len(), 3);
|
assert_eq!(iter.len(), 3);
|
||||||
@ -420,7 +421,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_event_cursor_len_filled() {
|
fn test_event_cursor_len_filled() {
|
||||||
let mut events = Events::<TestEvent>::default();
|
let mut events = Events::<TestEvent>::default();
|
||||||
events.send(TestEvent { i: 0 });
|
events.write(TestEvent { i: 0 });
|
||||||
assert_eq!(events.get_cursor().len(&events), 1);
|
assert_eq!(events.get_cursor().len(&events), 1);
|
||||||
assert!(!events.get_cursor().is_empty(&events));
|
assert!(!events.get_cursor().is_empty(&events));
|
||||||
}
|
}
|
||||||
@ -437,7 +438,7 @@ mod tests {
|
|||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.init_resource::<Events<TestEvent>>();
|
world.init_resource::<Events<TestEvent>>();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
@ -479,7 +480,7 @@ mod tests {
|
|||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.init_resource::<Events<TestEvent>>();
|
world.init_resource::<Events<TestEvent>>();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
}
|
}
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_systems(
|
schedule.add_systems(
|
||||||
@ -531,13 +532,13 @@ mod tests {
|
|||||||
let last = reader.run((), &mut world).unwrap();
|
let last = reader.run((), &mut world).unwrap();
|
||||||
assert!(last.is_none(), "EventReader should be empty");
|
assert!(last.is_none(), "EventReader should be empty");
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 0 });
|
world.write_event(TestEvent { i: 0 });
|
||||||
let last = reader.run((), &mut world).unwrap();
|
let last = reader.run((), &mut world).unwrap();
|
||||||
assert_eq!(last, Some(TestEvent { i: 0 }));
|
assert_eq!(last, Some(TestEvent { i: 0 }));
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
world.send_event(TestEvent { i: 2 });
|
world.write_event(TestEvent { i: 2 });
|
||||||
world.send_event(TestEvent { i: 3 });
|
world.write_event(TestEvent { i: 3 });
|
||||||
let last = reader.run((), &mut world).unwrap();
|
let last = reader.run((), &mut world).unwrap();
|
||||||
assert_eq!(last, Some(TestEvent { i: 3 }));
|
assert_eq!(last, Some(TestEvent { i: 3 }));
|
||||||
|
|
||||||
@ -561,13 +562,13 @@ mod tests {
|
|||||||
let last = mutator.run((), &mut world).unwrap();
|
let last = mutator.run((), &mut world).unwrap();
|
||||||
assert!(last.is_none(), "EventMutator should be empty");
|
assert!(last.is_none(), "EventMutator should be empty");
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 0 });
|
world.write_event(TestEvent { i: 0 });
|
||||||
let last = mutator.run((), &mut world).unwrap();
|
let last = mutator.run((), &mut world).unwrap();
|
||||||
assert_eq!(last, Some(TestEvent { i: 0 }));
|
assert_eq!(last, Some(TestEvent { i: 0 }));
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
world.send_event(TestEvent { i: 2 });
|
world.write_event(TestEvent { i: 2 });
|
||||||
world.send_event(TestEvent { i: 3 });
|
world.write_event(TestEvent { i: 3 });
|
||||||
let last = mutator.run((), &mut world).unwrap();
|
let last = mutator.run((), &mut world).unwrap();
|
||||||
assert_eq!(last, Some(TestEvent { i: 3 }));
|
assert_eq!(last, Some(TestEvent { i: 3 }));
|
||||||
|
|
||||||
@ -582,11 +583,11 @@ mod tests {
|
|||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.init_resource::<Events<TestEvent>>();
|
world.init_resource::<Events<TestEvent>>();
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 0 });
|
world.write_event(TestEvent { i: 0 });
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
world.send_event(TestEvent { i: 2 });
|
world.write_event(TestEvent { i: 2 });
|
||||||
world.send_event(TestEvent { i: 3 });
|
world.write_event(TestEvent { i: 3 });
|
||||||
world.send_event(TestEvent { i: 4 });
|
world.write_event(TestEvent { i: 4 });
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_systems(|mut events: EventReader<TestEvent>| {
|
schedule.add_systems(|mut events: EventReader<TestEvent>| {
|
||||||
@ -608,11 +609,11 @@ mod tests {
|
|||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.init_resource::<Events<TestEvent>>();
|
world.init_resource::<Events<TestEvent>>();
|
||||||
|
|
||||||
world.send_event(TestEvent { i: 0 });
|
world.write_event(TestEvent { i: 0 });
|
||||||
world.send_event(TestEvent { i: 1 });
|
world.write_event(TestEvent { i: 1 });
|
||||||
world.send_event(TestEvent { i: 2 });
|
world.write_event(TestEvent { i: 2 });
|
||||||
world.send_event(TestEvent { i: 3 });
|
world.write_event(TestEvent { i: 3 });
|
||||||
world.send_event(TestEvent { i: 4 });
|
world.write_event(TestEvent { i: 4 });
|
||||||
|
|
||||||
let mut schedule = Schedule::default();
|
let mut schedule = Schedule::default();
|
||||||
schedule.add_systems(|mut events: EventReader<TestEvent>| {
|
schedule.add_systems(|mut events: EventReader<TestEvent>| {
|
||||||
|
|||||||
@ -89,7 +89,7 @@ impl<'w, 's, E: BufferedEvent> EventMutator<'w, 's, E> {
|
|||||||
/// });
|
/// });
|
||||||
/// });
|
/// });
|
||||||
/// for value in 0..100 {
|
/// for value in 0..100 {
|
||||||
/// world.send_event(MyEvent { value });
|
/// world.write_event(MyEvent { value });
|
||||||
/// }
|
/// }
|
||||||
/// schedule.run(&mut world);
|
/// schedule.run(&mut world);
|
||||||
/// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
|
/// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
|
||||||
|
|||||||
@ -61,7 +61,7 @@ impl<'w, 's, E: BufferedEvent> EventReader<'w, 's, E> {
|
|||||||
/// });
|
/// });
|
||||||
/// });
|
/// });
|
||||||
/// for value in 0..100 {
|
/// for value in 0..100 {
|
||||||
/// world.send_event(MyEvent { value });
|
/// world.write_event(MyEvent { value });
|
||||||
/// }
|
/// }
|
||||||
/// schedule.run(&mut world);
|
/// schedule.run(&mut world);
|
||||||
/// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
|
/// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
event::{BufferedEvent, EventId, Events, SendBatchIds},
|
event::{BufferedEvent, EventId, Events, WriteBatchIds},
|
||||||
system::{ResMut, SystemParam},
|
system::{ResMut, SystemParam},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sends [`BufferedEvent`]s of type `T`.
|
/// Writes [`BufferedEvent`]s of type `T`.
|
||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
@ -34,14 +34,14 @@ use bevy_ecs::{
|
|||||||
///
|
///
|
||||||
/// `EventWriter` can only write events of one specific type, which must be known at compile-time.
|
/// `EventWriter` can only write events of one specific type, which must be known at compile-time.
|
||||||
/// This is not a problem most of the time, but you may find a situation where you cannot know
|
/// This is not a problem most of the time, but you may find a situation where you cannot know
|
||||||
/// ahead of time every kind of event you'll need to send. In this case, you can use the "type-erased event" pattern.
|
/// ahead of time every kind of event you'll need to write. In this case, you can use the "type-erased event" pattern.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_ecs::{prelude::*, event::Events};
|
/// # use bevy_ecs::{prelude::*, event::Events};
|
||||||
/// # #[derive(Event, BufferedEvent)]
|
/// # #[derive(Event, BufferedEvent)]
|
||||||
/// # pub struct MyEvent;
|
/// # pub struct MyEvent;
|
||||||
/// fn send_untyped(mut commands: Commands) {
|
/// fn write_untyped(mut commands: Commands) {
|
||||||
/// // Send an event of a specific type without having to declare that
|
/// // Write an event of a specific type without having to declare that
|
||||||
/// // type as a SystemParam.
|
/// // type as a SystemParam.
|
||||||
/// //
|
/// //
|
||||||
/// // Effectively, we're just moving the type parameter from the /type/ to the /method/,
|
/// // Effectively, we're just moving the type parameter from the /type/ to the /method/,
|
||||||
@ -51,7 +51,7 @@ use bevy_ecs::{
|
|||||||
/// // NOTE: the event won't actually be sent until commands get applied during
|
/// // NOTE: the event won't actually be sent until commands get applied during
|
||||||
/// // apply_deferred.
|
/// // apply_deferred.
|
||||||
/// commands.queue(|w: &mut World| {
|
/// commands.queue(|w: &mut World| {
|
||||||
/// w.send_event(MyEvent);
|
/// w.write_event(MyEvent);
|
||||||
/// });
|
/// });
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@ -72,18 +72,18 @@ impl<'w, E: BufferedEvent> EventWriter<'w, E> {
|
|||||||
#[doc(alias = "send")]
|
#[doc(alias = "send")]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn write(&mut self, event: E) -> EventId<E> {
|
pub fn write(&mut self, event: E) -> EventId<E> {
|
||||||
self.events.send(event)
|
self.events.write(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
|
/// Writes a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
|
||||||
/// This is more efficient than sending each event individually.
|
/// This is more efficient than writing each event individually.
|
||||||
/// This method returns the [IDs](`EventId`) of the written `events`.
|
/// This method returns the [IDs](`EventId`) of the written `events`.
|
||||||
///
|
///
|
||||||
/// See [`Events`] for details.
|
/// See [`Events`] for details.
|
||||||
#[doc(alias = "send_batch")]
|
#[doc(alias = "send_batch")]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn write_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
pub fn write_batch(&mut self, events: impl IntoIterator<Item = E>) -> WriteBatchIds<E> {
|
||||||
self.events.send_batch(events)
|
self.events.write_batch(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the default value of the event. Useful when the event is an empty struct.
|
/// Writes the default value of the event. Useful when the event is an empty struct.
|
||||||
@ -96,6 +96,6 @@ impl<'w, E: BufferedEvent> EventWriter<'w, E> {
|
|||||||
where
|
where
|
||||||
E: Default,
|
E: Default,
|
||||||
{
|
{
|
||||||
self.events.send_default()
|
self.events.write_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -455,15 +455,13 @@ pub fn validate_parent_has_component<C: Component>(
|
|||||||
let Some(child_of) = entity_ref.get::<ChildOf>() else {
|
let Some(child_of) = entity_ref.get::<ChildOf>() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !world
|
let parent = child_of.parent();
|
||||||
.get_entity(child_of.parent())
|
if !world.get_entity(parent).is_ok_and(|e| e.contains::<C>()) {
|
||||||
.is_ok_and(|e| e.contains::<C>())
|
|
||||||
{
|
|
||||||
// TODO: print name here once Name lives in bevy_ecs
|
// TODO: print name here once Name lives in bevy_ecs
|
||||||
let name: Option<String> = None;
|
let name: Option<String> = None;
|
||||||
let debug_name = DebugName::type_name::<C>();
|
let debug_name = DebugName::type_name::<C>();
|
||||||
warn!(
|
warn!(
|
||||||
"warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\
|
"warning[B0004]: {}{name} with the {ty_name} component has a parent ({parent}) without {ty_name}.\n\
|
||||||
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004",
|
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004",
|
||||||
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
|
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
|
||||||
ty_name = debug_name.shortname(),
|
ty_name = debug_name.shortname(),
|
||||||
|
|||||||
@ -465,10 +465,16 @@ impl RemovedComponentEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a removal event for the specified component.
|
/// Sends a removal event for the specified component.
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `RemovedComponentEvents:write` instead.")]
|
||||||
pub fn send(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
|
pub fn send(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
|
||||||
|
self.write(component_id, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a removal event for the specified component.
|
||||||
|
pub fn write(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
|
||||||
self.event_sets
|
self.event_sets
|
||||||
.get_or_insert_with(component_id.into(), Default::default)
|
.get_or_insert_with(component_id.into(), Default::default)
|
||||||
.send(RemovedComponentEntity(entity));
|
.write(RemovedComponentEntity(entity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -133,7 +133,7 @@ pub trait Relationship: Component + Sized {
|
|||||||
.and_modify(move |mut relationship_target| {
|
.and_modify(move |mut relationship_target| {
|
||||||
relationship_target.collection_mut_risky().add(entity);
|
relationship_target.collection_mut_risky().add(entity);
|
||||||
})
|
})
|
||||||
.or_insert_with(|| {
|
.or_insert_with(move || {
|
||||||
let mut target = Self::RelationshipTarget::with_capacity(1);
|
let mut target = Self::RelationshipTarget::with_capacity(1);
|
||||||
target.collection_mut_risky().add(entity);
|
target.collection_mut_risky().add(entity);
|
||||||
target
|
target
|
||||||
|
|||||||
@ -875,7 +875,7 @@ pub mod common_conditions {
|
|||||||
/// app.run(&mut world);
|
/// app.run(&mut world);
|
||||||
/// assert_eq!(world.resource::<Counter>().0, 0);
|
/// assert_eq!(world.resource::<Counter>().0, 0);
|
||||||
///
|
///
|
||||||
/// world.resource_mut::<Events<MyEvent>>().send(MyEvent);
|
/// world.resource_mut::<Events<MyEvent>>().write(MyEvent);
|
||||||
///
|
///
|
||||||
/// // A `MyEvent` event has been pushed so `my_system` will run
|
/// // A `MyEvent` event has been pushed so `my_system` will run
|
||||||
/// app.run(&mut world);
|
/// app.run(&mut world);
|
||||||
|
|||||||
@ -597,7 +597,7 @@ impl Table {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn entity_count(&self) -> u32 {
|
pub fn entity_count(&self) -> u32 {
|
||||||
// No entity may have more than one table row, so there are no duplicates,
|
// No entity may have more than one table row, so there are no duplicates,
|
||||||
// and there may only ever be u32::MAX entities, so the length never exceeds u32's cappacity.
|
// and there may only ever be u32::MAX entities, so the length never exceeds u32's capacity.
|
||||||
self.entities.len() as u32
|
self.entities.len() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -229,12 +229,19 @@ pub fn trigger_targets(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`Command`] that sends an arbitrary [`BufferedEvent`].
|
/// A [`Command`] that writes an arbitrary [`BufferedEvent`].
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn send_event<E: BufferedEvent>(event: E) -> impl Command {
|
pub fn write_event<E: BufferedEvent>(event: E) -> impl Command {
|
||||||
let caller = MaybeLocation::caller();
|
let caller = MaybeLocation::caller();
|
||||||
move |world: &mut World| {
|
move |world: &mut World| {
|
||||||
let mut events = world.resource_mut::<Events<E>>();
|
let mut events = world.resource_mut::<Events<E>>();
|
||||||
events.send_with_caller(event, caller);
|
events.write_with_caller(event, caller);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Command`] that writes an arbitrary [`BufferedEvent`].
|
||||||
|
#[track_caller]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `write_event` instead.")]
|
||||||
|
pub fn send_event<E: BufferedEvent>(event: E) -> impl Command {
|
||||||
|
write_event(event)
|
||||||
|
}
|
||||||
|
|||||||
@ -143,14 +143,40 @@ pub unsafe fn insert_by_id<T: Send + 'static>(
|
|||||||
|
|
||||||
/// An [`EntityCommand`] that adds a component to an entity using
|
/// An [`EntityCommand`] that adds a component to an entity using
|
||||||
/// the component's [`FromWorld`] implementation.
|
/// the component's [`FromWorld`] implementation.
|
||||||
|
///
|
||||||
|
/// `T::from_world` will only be invoked if the component will actually be inserted.
|
||||||
|
/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]
|
||||||
|
/// and the entity already has the component.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
|
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
|
||||||
let caller = MaybeLocation::caller();
|
let caller = MaybeLocation::caller();
|
||||||
move |mut entity: EntityWorldMut| {
|
move |mut entity: EntityWorldMut| {
|
||||||
|
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
|
||||||
let value = entity.world_scope(|world| T::from_world(world));
|
let value = entity.world_scope(|world| T::from_world(world));
|
||||||
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
|
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [`EntityCommand`] that adds a component to an entity using
|
||||||
|
/// some function that returns the component.
|
||||||
|
///
|
||||||
|
/// The function will only be invoked if the component will actually be inserted.
|
||||||
|
/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]
|
||||||
|
/// and the entity already has the component.
|
||||||
|
#[track_caller]
|
||||||
|
pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T + Send + 'static,
|
||||||
|
{
|
||||||
|
let caller = MaybeLocation::caller();
|
||||||
|
move |mut entity: EntityWorldMut| {
|
||||||
|
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
|
||||||
|
let value = component_fn();
|
||||||
|
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
|
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|||||||
@ -1124,9 +1124,9 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
self.spawn(Observer::new(observer))
|
self.spawn(Observer::new(observer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends an arbitrary [`BufferedEvent`].
|
/// Writes an arbitrary [`BufferedEvent`].
|
||||||
///
|
///
|
||||||
/// This is a convenience method for sending events
|
/// This is a convenience method for writing events
|
||||||
/// without requiring an [`EventWriter`](crate::event::EventWriter).
|
/// without requiring an [`EventWriter`](crate::event::EventWriter).
|
||||||
///
|
///
|
||||||
/// # Performance
|
/// # Performance
|
||||||
@ -1137,11 +1137,29 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// If these events are performance-critical or very frequently sent,
|
/// If these events are performance-critical or very frequently sent,
|
||||||
/// consider using a typed [`EventWriter`](crate::event::EventWriter) instead.
|
/// consider using a typed [`EventWriter`](crate::event::EventWriter) instead.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> &mut Self {
|
pub fn write_event<E: BufferedEvent>(&mut self, event: E) -> &mut Self {
|
||||||
self.queue(command::send_event(event));
|
self.queue(command::write_event(event));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes an arbitrary [`BufferedEvent`].
|
||||||
|
///
|
||||||
|
/// This is a convenience method for writing events
|
||||||
|
/// without requiring an [`EventWriter`](crate::event::EventWriter).
|
||||||
|
///
|
||||||
|
/// # Performance
|
||||||
|
///
|
||||||
|
/// Since this is a command, exclusive world access is used, which means that it will not profit from
|
||||||
|
/// system-level parallelism on supported platforms.
|
||||||
|
///
|
||||||
|
/// If these events are performance-critical or very frequently sent,
|
||||||
|
/// consider using a typed [`EventWriter`](crate::event::EventWriter) instead.
|
||||||
|
#[track_caller]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `Commands::write_event` instead.")]
|
||||||
|
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> &mut Self {
|
||||||
|
self.write_event(event)
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs the schedule corresponding to the given [`ScheduleLabel`].
|
/// Runs the schedule corresponding to the given [`ScheduleLabel`].
|
||||||
///
|
///
|
||||||
/// Calls [`World::try_run_schedule`](World::try_run_schedule).
|
/// Calls [`World::try_run_schedule`](World::try_run_schedule).
|
||||||
@ -2273,35 +2291,53 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
|
|||||||
|
|
||||||
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity,
|
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity,
|
||||||
/// if `T` is not already present.
|
/// if `T` is not already present.
|
||||||
|
///
|
||||||
|
/// `default` will only be invoked if the component will actually be inserted.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or_insert_with(&mut self, default: impl Fn() -> T) -> &mut Self {
|
pub fn or_insert_with<F>(&mut self, default: F) -> &mut Self
|
||||||
self.or_insert(default())
|
where
|
||||||
|
F: FnOnce() -> T + Send + 'static,
|
||||||
|
{
|
||||||
|
self.entity_commands
|
||||||
|
.queue(entity_command::insert_with(default, InsertMode::Keep));
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity,
|
/// [Insert](EntityCommands::insert) the value returned from `default` into this entity,
|
||||||
/// if `T` is not already present.
|
/// if `T` is not already present.
|
||||||
///
|
///
|
||||||
|
/// `default` will only be invoked if the component will actually be inserted.
|
||||||
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// If the entity does not exist when this command is executed,
|
/// If the entity does not exist when this command is executed,
|
||||||
/// the resulting error will be ignored.
|
/// the resulting error will be ignored.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or_try_insert_with(&mut self, default: impl Fn() -> T) -> &mut Self {
|
pub fn or_try_insert_with<F>(&mut self, default: F) -> &mut Self
|
||||||
self.or_try_insert(default())
|
where
|
||||||
|
F: FnOnce() -> T + Send + 'static,
|
||||||
|
{
|
||||||
|
self.entity_commands
|
||||||
|
.queue_silenced(entity_command::insert_with(default, InsertMode::Keep));
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Insert](EntityCommands::insert) `T::default` into this entity,
|
/// [Insert](EntityCommands::insert) `T::default` into this entity,
|
||||||
/// if `T` is not already present.
|
/// if `T` is not already present.
|
||||||
|
///
|
||||||
|
/// `T::default` will only be invoked if the component will actually be inserted.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or_default(&mut self) -> &mut Self
|
pub fn or_default(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Default,
|
T: Default,
|
||||||
{
|
{
|
||||||
self.or_insert(T::default())
|
self.or_insert_with(T::default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Insert](EntityCommands::insert) `T::from_world` into this entity,
|
/// [Insert](EntityCommands::insert) `T::from_world` into this entity,
|
||||||
/// if `T` is not already present.
|
/// if `T` is not already present.
|
||||||
|
///
|
||||||
|
/// `T::from_world` will only be invoked if the component will actually be inserted.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn or_from_world(&mut self) -> &mut Self
|
pub fn or_from_world(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
@ -2396,6 +2432,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for W<u8> {
|
||||||
|
fn default() -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn entity_commands_entry() {
|
fn entity_commands_entry() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
@ -2435,6 +2477,17 @@ mod tests {
|
|||||||
let id = commands.entity(entity).entry::<W<u64>>().entity().id();
|
let id = commands.entity(entity).entry::<W<u64>>().entity().id();
|
||||||
queue.apply(&mut world);
|
queue.apply(&mut world);
|
||||||
assert_eq!(id, entity);
|
assert_eq!(id, entity);
|
||||||
|
let mut commands = Commands::new(&mut queue, &world);
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.entry::<W<u8>>()
|
||||||
|
.or_insert_with(|| W(5))
|
||||||
|
.or_insert_with(|| unreachable!())
|
||||||
|
.or_try_insert_with(|| unreachable!())
|
||||||
|
.or_default()
|
||||||
|
.or_from_world();
|
||||||
|
queue.apply(&mut world);
|
||||||
|
assert_eq!(5, world.get::<W<u8>>(entity).unwrap().0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use crate::{
|
|||||||
change_detection::{MaybeLocation, MutUntyped},
|
change_detection::{MaybeLocation, MutUntyped},
|
||||||
component::{ComponentId, Mutable},
|
component::{ComponentId, Mutable},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::{BufferedEvent, EntityEvent, Event, EventId, EventKey, Events, SendBatchIds},
|
event::{BufferedEvent, EntityEvent, Event, EventId, EventKey, Events, WriteBatchIds},
|
||||||
lifecycle::{HookContext, INSERT, REPLACE},
|
lifecycle::{HookContext, INSERT, REPLACE},
|
||||||
observer::{Observers, TriggerTargets},
|
observer::{Observers, TriggerTargets},
|
||||||
prelude::{Component, QueryState},
|
prelude::{Component, QueryState},
|
||||||
@ -507,30 +507,51 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
unsafe { self.world.get_non_send_resource_mut() }
|
unsafe { self.world.get_non_send_resource_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a [`BufferedEvent`].
|
/// Writes a [`BufferedEvent`].
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn write_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
||||||
|
self.write_event_batch(core::iter::once(event))?.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a [`BufferedEvent`].
|
||||||
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `DeferredWorld::write_event` instead.")]
|
||||||
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
||||||
self.send_event_batch(core::iter::once(event))?.next()
|
self.write_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the default value of the [`BufferedEvent`] of type `E`.
|
/// Writes the default value of the [`BufferedEvent`] of type `E`.
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn write_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
||||||
|
self.write_event(E::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the default value of the [`BufferedEvent`] of type `E`.
|
||||||
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.17.0",
|
||||||
|
note = "Use `DeferredWorld::write_event_default` instead."
|
||||||
|
)]
|
||||||
pub fn send_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
pub fn send_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
||||||
self.send_event(E::default())
|
self.write_event_default::<E>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a batch of [`BufferedEvent`]s from an iterator.
|
/// Writes a batch of [`BufferedEvent`]s from an iterator.
|
||||||
/// This method returns the [IDs](`EventId`) of the sent `events`,
|
/// This method returns the [IDs](`EventId`) of the written `events`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn send_event_batch<E: BufferedEvent>(
|
pub fn write_event_batch<E: BufferedEvent>(
|
||||||
&mut self,
|
&mut self,
|
||||||
events: impl IntoIterator<Item = E>,
|
events: impl IntoIterator<Item = E>,
|
||||||
) -> Option<SendBatchIds<E>> {
|
) -> Option<WriteBatchIds<E>> {
|
||||||
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||||
@ -538,7 +559,22 @@ impl<'w> DeferredWorld<'w> {
|
|||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
Some(events_resource.send_batch(events))
|
Some(events_resource.write_batch(events))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a batch of [`BufferedEvent`]s from an iterator.
|
||||||
|
/// This method returns the [IDs](`EventId`) of the written `events`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.17.0",
|
||||||
|
note = "Use `DeferredWorld::write_event_batch` instead."
|
||||||
|
)]
|
||||||
|
pub fn send_event_batch<E: BufferedEvent>(
|
||||||
|
&mut self,
|
||||||
|
events: impl IntoIterator<Item = E>,
|
||||||
|
) -> Option<WriteBatchIds<E>> {
|
||||||
|
self.write_event_batch(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
|
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
|
||||||
|
|||||||
@ -2418,7 +2418,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for component_id in archetype.components() {
|
for component_id in archetype.components() {
|
||||||
world.removed_components.send(component_id, self.entity);
|
world.removed_components.write(component_id, self.entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observers and on_remove hooks may reserve new entities, which
|
// Observers and on_remove hooks may reserve new entities, which
|
||||||
|
|||||||
@ -50,7 +50,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
entity::{Entities, Entity, EntityDoesNotExistError},
|
entity::{Entities, Entity, EntityDoesNotExistError},
|
||||||
entity_disabling::DefaultQueryFilters,
|
entity_disabling::DefaultQueryFilters,
|
||||||
event::{Event, EventId, Events, SendBatchIds},
|
event::{Event, EventId, Events, WriteBatchIds},
|
||||||
lifecycle::RemovedComponentEvents,
|
lifecycle::RemovedComponentEvents,
|
||||||
observer::Observers,
|
observer::Observers,
|
||||||
query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState},
|
query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState},
|
||||||
@ -2626,30 +2626,48 @@ impl World {
|
|||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a [`BufferedEvent`].
|
/// Writes a [`BufferedEvent`].
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn write_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
||||||
|
self.write_event_batch(core::iter::once(event))?.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a [`BufferedEvent`].
|
||||||
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `World::write_event` instead.")]
|
||||||
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
pub fn send_event<E: BufferedEvent>(&mut self, event: E) -> Option<EventId<E>> {
|
||||||
self.send_event_batch(core::iter::once(event))?.next()
|
self.write_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the default value of the [`BufferedEvent`] of type `E`.
|
/// Writes the default value of the [`BufferedEvent`] of type `E`.
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn write_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
||||||
|
self.write_event(E::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the default value of the [`BufferedEvent`] of type `E`.
|
||||||
|
/// This method returns the [ID](`EventId`) of the written `event`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `World::write_event_default` instead.")]
|
||||||
pub fn send_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
pub fn send_event_default<E: BufferedEvent + Default>(&mut self) -> Option<EventId<E>> {
|
||||||
self.send_event(E::default())
|
self.write_event_default::<E>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a batch of [`BufferedEvent`]s from an iterator.
|
/// Writes a batch of [`BufferedEvent`]s from an iterator.
|
||||||
/// This method returns the [IDs](`EventId`) of the sent `events`,
|
/// This method returns the [IDs](`EventId`) of the written `events`,
|
||||||
/// or [`None`] if the `event` could not be sent.
|
/// or [`None`] if the `event` could not be written.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn send_event_batch<E: BufferedEvent>(
|
pub fn write_event_batch<E: BufferedEvent>(
|
||||||
&mut self,
|
&mut self,
|
||||||
events: impl IntoIterator<Item = E>,
|
events: impl IntoIterator<Item = E>,
|
||||||
) -> Option<SendBatchIds<E>> {
|
) -> Option<WriteBatchIds<E>> {
|
||||||
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||||
@ -2657,7 +2675,19 @@ impl World {
|
|||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
Some(events_resource.send_batch(events))
|
Some(events_resource.write_batch(events))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a batch of [`BufferedEvent`]s from an iterator.
|
||||||
|
/// This method returns the [IDs](`EventId`) of the written `events`,
|
||||||
|
/// or [`None`] if the `event` could not be written.
|
||||||
|
#[inline]
|
||||||
|
#[deprecated(since = "0.17.0", note = "Use `World::write_event_batch` instead.")]
|
||||||
|
pub fn send_event_batch<E: BufferedEvent>(
|
||||||
|
&mut self,
|
||||||
|
events: impl IntoIterator<Item = E>,
|
||||||
|
) -> Option<WriteBatchIds<E>> {
|
||||||
|
self.write_event_batch(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a new resource with the given `value`. Will replace the value if it already existed.
|
/// Inserts a new resource with the given `value`. Will replace the value if it already existed.
|
||||||
|
|||||||
@ -13,7 +13,6 @@ keywords = ["bevy"]
|
|||||||
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
|
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||||
bevy_input = { path = "../bevy_input", version = "0.17.0-dev" }
|
bevy_input = { path = "../bevy_input", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
|
|||||||
@ -16,7 +16,6 @@ proc-macro = true
|
|||||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" }
|
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" }
|
||||||
|
|
||||||
syn = "2.0"
|
syn = "2.0"
|
||||||
proc-macro2 = "1.0"
|
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|||||||
@ -36,7 +36,6 @@ bevy_scene = { path = "../bevy_scene", version = "0.17.0-dev", features = [
|
|||||||
] }
|
] }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev" }
|
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"serialize",
|
"serialize",
|
||||||
|
|||||||
@ -63,7 +63,6 @@ libm = ["bevy_math/libm"]
|
|||||||
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
|
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev", default-features = false }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
|
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
|
||||||
"glam",
|
"glam",
|
||||||
], default-features = false, optional = true }
|
], default-features = false, optional = true }
|
||||||
|
|||||||
@ -2223,7 +2223,7 @@ mod tests {
|
|||||||
self.app
|
self.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<GamepadConnectionEvent>>()
|
.resource_mut::<Events<GamepadConnectionEvent>>()
|
||||||
.send(GamepadConnectionEvent::new(
|
.write(GamepadConnectionEvent::new(
|
||||||
gamepad,
|
gamepad,
|
||||||
Connected {
|
Connected {
|
||||||
name: "Test gamepad".to_string(),
|
name: "Test gamepad".to_string(),
|
||||||
@ -2238,14 +2238,14 @@ mod tests {
|
|||||||
self.app
|
self.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<GamepadConnectionEvent>>()
|
.resource_mut::<Events<GamepadConnectionEvent>>()
|
||||||
.send(GamepadConnectionEvent::new(gamepad, Disconnected));
|
.write(GamepadConnectionEvent::new(gamepad, Disconnected));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
|
pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
|
||||||
self.app
|
self.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send(event);
|
.write(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_raw_gamepad_event_batch(
|
pub fn send_raw_gamepad_event_batch(
|
||||||
@ -2255,7 +2255,7 @@ mod tests {
|
|||||||
self.app
|
self.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2449,7 +2449,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch([
|
.write_batch([
|
||||||
RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
|
RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
|
||||||
entity,
|
entity,
|
||||||
GamepadAxis::LeftStickY,
|
GamepadAxis::LeftStickY,
|
||||||
@ -2513,7 +2513,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.app
|
ctx.app
|
||||||
@ -2550,7 +2550,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.app
|
ctx.app
|
||||||
@ -2598,7 +2598,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
|
|
||||||
let events = ctx
|
let events = ctx
|
||||||
@ -2654,7 +2654,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.app
|
ctx.app
|
||||||
@ -2692,7 +2692,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
|
|
||||||
let events = ctx
|
let events = ctx
|
||||||
@ -2728,7 +2728,7 @@ mod tests {
|
|||||||
ctx.app
|
ctx.app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.resource_mut::<Events<RawGamepadEvent>>()
|
.resource_mut::<Events<RawGamepadEvent>>()
|
||||||
.send_batch(events);
|
.write_batch(events);
|
||||||
ctx.update();
|
ctx.update();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@ -546,7 +546,7 @@ mod tests {
|
|||||||
assert!(!app.world().is_focus_visible(child_of_b));
|
assert!(!app.world().is_focus_visible(child_of_b));
|
||||||
|
|
||||||
// entity_a should receive this event
|
// entity_a should receive this event
|
||||||
app.world_mut().send_event(key_a_event());
|
app.world_mut().write_event(key_a_event());
|
||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
assert_eq!(get_gathered(&app, entity_a), "A");
|
assert_eq!(get_gathered(&app, entity_a), "A");
|
||||||
@ -559,7 +559,7 @@ mod tests {
|
|||||||
assert!(!app.world().is_focus_visible(entity_a));
|
assert!(!app.world().is_focus_visible(entity_a));
|
||||||
|
|
||||||
// This event should be lost
|
// This event should be lost
|
||||||
app.world_mut().send_event(key_a_event());
|
app.world_mut().write_event(key_a_event());
|
||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
assert_eq!(get_gathered(&app, entity_a), "A");
|
assert_eq!(get_gathered(&app, entity_a), "A");
|
||||||
@ -580,7 +580,7 @@ mod tests {
|
|||||||
|
|
||||||
// These events should be received by entity_b and child_of_b
|
// These events should be received by entity_b and child_of_b
|
||||||
app.world_mut()
|
app.world_mut()
|
||||||
.send_event_batch(core::iter::repeat_n(key_a_event(), 4));
|
.write_event_batch(core::iter::repeat_n(key_a_event(), 4));
|
||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
assert_eq!(get_gathered(&app, entity_a), "A");
|
assert_eq!(get_gathered(&app, entity_a), "A");
|
||||||
|
|||||||
@ -19,7 +19,6 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
|||||||
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
|
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev" }
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev" }
|
||||||
bevy_color = { path = "../bevy_color", version = "0.17.0-dev", features = [
|
bevy_color = { path = "../bevy_color", version = "0.17.0-dev", features = [
|
||||||
@ -27,7 +26,6 @@ bevy_color = { path = "../bevy_color", version = "0.17.0-dev", features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
serde = { version = "1", default-features = false, features = ["derive"] }
|
|
||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@ -27,7 +27,7 @@ use cluster::{
|
|||||||
mod ambient_light;
|
mod ambient_light;
|
||||||
pub use ambient_light::AmbientLight;
|
pub use ambient_light::AmbientLight;
|
||||||
mod probe;
|
mod probe;
|
||||||
pub use probe::{EnvironmentMapLight, LightProbe};
|
pub use probe::{EnvironmentMapLight, IrradianceVolume, LightProbe};
|
||||||
mod volumetric;
|
mod volumetric;
|
||||||
pub use volumetric::{FogVolume, VolumetricFog, VolumetricLight};
|
pub use volumetric::{FogVolume, VolumetricFog, VolumetricLight};
|
||||||
pub mod cascade;
|
pub mod cascade;
|
||||||
@ -121,6 +121,7 @@ impl Plugin for LightPlugin {
|
|||||||
.register_type::<PointLight>()
|
.register_type::<PointLight>()
|
||||||
.register_type::<LightProbe>()
|
.register_type::<LightProbe>()
|
||||||
.register_type::<EnvironmentMapLight>()
|
.register_type::<EnvironmentMapLight>()
|
||||||
|
.register_type::<IrradianceVolume>()
|
||||||
.register_type::<VolumetricFog>()
|
.register_type::<VolumetricFog>()
|
||||||
.register_type::<VolumetricLight>()
|
.register_type::<VolumetricLight>()
|
||||||
.register_type::<PointLightShadowMap>()
|
.register_type::<PointLightShadowMap>()
|
||||||
|
|||||||
@ -107,3 +107,50 @@ impl Default for EnvironmentMapLight {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The component that defines an irradiance volume.
|
||||||
|
///
|
||||||
|
/// See `bevy_pbr::irradiance_volume` for detailed information.
|
||||||
|
///
|
||||||
|
/// This component requires the [`LightProbe`] component, and is typically used with
|
||||||
|
/// [`bevy_transform::components::Transform`] to place the volume appropriately.
|
||||||
|
#[derive(Clone, Reflect, Component, Debug)]
|
||||||
|
#[reflect(Component, Default, Debug, Clone)]
|
||||||
|
#[require(LightProbe)]
|
||||||
|
pub struct IrradianceVolume {
|
||||||
|
/// The 3D texture that represents the ambient cubes, encoded in the format
|
||||||
|
/// described in `bevy_pbr::irradiance_volume`.
|
||||||
|
pub voxels: Handle<Image>,
|
||||||
|
|
||||||
|
/// Scale factor applied to the diffuse and specular light generated by this component.
|
||||||
|
///
|
||||||
|
/// After applying this multiplier, the resulting values should
|
||||||
|
/// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
|
||||||
|
///
|
||||||
|
/// See also <https://google.github.io/filament/Filament.html#lighting/imagebasedlights/iblunit>.
|
||||||
|
pub intensity: f32,
|
||||||
|
|
||||||
|
/// Whether the light from this irradiance volume has an effect on meshes
|
||||||
|
/// with lightmaps.
|
||||||
|
///
|
||||||
|
/// Set this to false if your lightmap baking tool bakes the light from this
|
||||||
|
/// irradiance volume into the lightmaps in order to avoid counting the
|
||||||
|
/// irradiance twice. Frequently, applications use irradiance volumes as a
|
||||||
|
/// lower-quality alternative to lightmaps for capturing indirect
|
||||||
|
/// illumination on dynamic objects, and such applications will want to set
|
||||||
|
/// this value to false.
|
||||||
|
///
|
||||||
|
/// By default, this is set to true.
|
||||||
|
pub affects_lightmapped_meshes: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IrradianceVolume {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
IrradianceVolume {
|
||||||
|
voxels: Handle::default(),
|
||||||
|
intensity: 0.0,
|
||||||
|
affects_lightmapped_meshes: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use core::f32::consts::FRAC_1_SQRT_2;
|
use core::f32::consts::FRAC_1_SQRT_2;
|
||||||
|
use core::fmt;
|
||||||
use derive_more::derive::Into;
|
use derive_more::derive::Into;
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
@ -325,6 +326,12 @@ impl core::ops::Mul<Dir2> for Rot2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Dir2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "approx", test))]
|
#[cfg(any(feature = "approx", test))]
|
||||||
impl approx::AbsDiffEq for Dir2 {
|
impl approx::AbsDiffEq for Dir2 {
|
||||||
type Epsilon = f32;
|
type Epsilon = f32;
|
||||||
@ -587,6 +594,12 @@ impl core::ops::Mul<Dir3> for Quat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Dir3 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "approx")]
|
#[cfg(feature = "approx")]
|
||||||
impl approx::AbsDiffEq for Dir3 {
|
impl approx::AbsDiffEq for Dir3 {
|
||||||
type Epsilon = f32;
|
type Epsilon = f32;
|
||||||
@ -834,6 +847,12 @@ impl core::ops::Mul<Dir3A> for Quat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Dir3A {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "approx")]
|
#[cfg(feature = "approx")]
|
||||||
impl approx::AbsDiffEq for Dir3A {
|
impl approx::AbsDiffEq for Dir3A {
|
||||||
type Epsilon = f32;
|
type Epsilon = f32;
|
||||||
@ -1022,6 +1041,12 @@ impl core::ops::Mul<Dir4> for f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Dir4 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "approx")]
|
#[cfg(feature = "approx")]
|
||||||
impl approx::AbsDiffEq for Dir4 {
|
impl approx::AbsDiffEq for Dir4 {
|
||||||
type Epsilon = f32;
|
type Epsilon = f32;
|
||||||
|
|||||||
@ -18,7 +18,6 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
|||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.17.0-dev" }
|
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.17.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"serialize",
|
"serialize",
|
||||||
|
|||||||
@ -21,7 +21,7 @@ pbr_light_textures = []
|
|||||||
shader_format_glsl = ["bevy_render/shader_format_glsl"]
|
shader_format_glsl = ["bevy_render/shader_format_glsl"]
|
||||||
trace = ["bevy_render/trace"]
|
trace = ["bevy_render/trace"]
|
||||||
# Enables the meshlet renderer for dense high-poly scenes (experimental)
|
# Enables the meshlet renderer for dense high-poly scenes (experimental)
|
||||||
meshlet = ["dep:lz4_flex", "dep:range-alloc", "dep:half", "dep:bevy_tasks"]
|
meshlet = ["dep:lz4_flex", "dep:range-alloc", "dep:bevy_tasks"]
|
||||||
# Enables processing meshes into meshlet meshes
|
# Enables processing meshes into meshlet meshes
|
||||||
meshlet_processor = [
|
meshlet_processor = [
|
||||||
"meshlet",
|
"meshlet",
|
||||||
@ -51,7 +51,6 @@ bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
|
|||||||
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", optional = true }
|
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", optional = true }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
||||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
] }
|
] }
|
||||||
@ -66,14 +65,12 @@ lz4_flex = { version = "0.11", default-features = false, features = [
|
|||||||
"frame",
|
"frame",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
range-alloc = { version = "0.1.3", optional = true }
|
range-alloc = { version = "0.1.3", optional = true }
|
||||||
half = { version = "2", features = ["bytemuck"], optional = true }
|
|
||||||
meshopt = { version = "0.4.1", optional = true }
|
meshopt = { version = "0.4.1", optional = true }
|
||||||
metis = { version = "0.2", optional = true }
|
metis = { version = "0.2", optional = true }
|
||||||
itertools = { version = "0.14", optional = true }
|
itertools = { version = "0.14", optional = true }
|
||||||
bitvec = { version = "1", optional = true }
|
bitvec = { version = "1", optional = true }
|
||||||
# direct dependency required for derive macro
|
# direct dependency required for derive macro
|
||||||
bytemuck = { version = "1", features = ["derive", "must_cast"] }
|
bytemuck = { version = "1", features = ["derive", "must_cast"] }
|
||||||
radsort = "0.1"
|
|
||||||
smallvec = { version = "1", default-features = false }
|
smallvec = { version = "1", default-features = false }
|
||||||
nonmax = "0.5"
|
nonmax = "0.5"
|
||||||
static_assertions = "1"
|
static_assertions = "1"
|
||||||
|
|||||||
@ -51,9 +51,9 @@ use bevy_light::SimulationLightSystems;
|
|||||||
pub use bevy_light::{
|
pub use bevy_light::{
|
||||||
light_consts, AmbientLight, CascadeShadowConfig, CascadeShadowConfigBuilder, Cascades,
|
light_consts, AmbientLight, CascadeShadowConfig, CascadeShadowConfigBuilder, Cascades,
|
||||||
ClusteredDecal, DirectionalLight, DirectionalLightShadowMap, DirectionalLightTexture,
|
ClusteredDecal, DirectionalLight, DirectionalLightShadowMap, DirectionalLightTexture,
|
||||||
FogVolume, LightPlugin, LightProbe, NotShadowCaster, NotShadowReceiver, PointLight,
|
FogVolume, IrradianceVolume, LightPlugin, LightProbe, NotShadowCaster, NotShadowReceiver,
|
||||||
PointLightShadowMap, PointLightTexture, ShadowFilteringMethod, SpotLight, SpotLightTexture,
|
PointLight, PointLightShadowMap, PointLightTexture, ShadowFilteringMethod, SpotLight,
|
||||||
TransmittedShadowReceiver, VolumetricFog, VolumetricLight,
|
SpotLightTexture, TransmittedShadowReceiver, VolumetricFog, VolumetricLight,
|
||||||
};
|
};
|
||||||
pub use cluster::*;
|
pub use cluster::*;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
|
|||||||
@ -133,9 +133,8 @@
|
|||||||
//!
|
//!
|
||||||
//! [Why ambient cubes?]: #why-ambient-cubes
|
//! [Why ambient cubes?]: #why-ambient-cubes
|
||||||
|
|
||||||
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
|
||||||
use bevy_image::Image;
|
use bevy_image::Image;
|
||||||
use bevy_light::LightProbe;
|
pub use bevy_light::IrradianceVolume;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
render_resource::{
|
render_resource::{
|
||||||
@ -145,11 +144,9 @@ use bevy_render::{
|
|||||||
renderer::{RenderAdapter, RenderDevice},
|
renderer::{RenderAdapter, RenderDevice},
|
||||||
texture::{FallbackImage, GpuImage},
|
texture::{FallbackImage, GpuImage},
|
||||||
};
|
};
|
||||||
use bevy_utils::default;
|
|
||||||
use core::{num::NonZero, ops::Deref};
|
use core::{num::NonZero, ops::Deref};
|
||||||
|
|
||||||
use bevy_asset::{AssetId, Handle};
|
use bevy_asset::AssetId;
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
add_cubemap_texture_view, binding_arrays_are_usable, RenderViewLightProbes,
|
add_cubemap_texture_view, binding_arrays_are_usable, RenderViewLightProbes,
|
||||||
@ -163,53 +160,6 @@ use super::LightProbeComponent;
|
|||||||
/// (see issue #11885).
|
/// (see issue #11885).
|
||||||
pub(crate) const IRRADIANCE_VOLUMES_ARE_USABLE: bool = cfg!(not(target_arch = "wasm32"));
|
pub(crate) const IRRADIANCE_VOLUMES_ARE_USABLE: bool = cfg!(not(target_arch = "wasm32"));
|
||||||
|
|
||||||
/// The component that defines an irradiance volume.
|
|
||||||
///
|
|
||||||
/// See [`crate::irradiance_volume`] for detailed information.
|
|
||||||
///
|
|
||||||
/// This component requires the [`LightProbe`] component, and is typically used with
|
|
||||||
/// [`bevy_transform::components::Transform`] to place the volume appropriately.
|
|
||||||
#[derive(Clone, Reflect, Component, Debug)]
|
|
||||||
#[reflect(Component, Default, Debug, Clone)]
|
|
||||||
#[require(LightProbe)]
|
|
||||||
pub struct IrradianceVolume {
|
|
||||||
/// The 3D texture that represents the ambient cubes, encoded in the format
|
|
||||||
/// described in [`crate::irradiance_volume`].
|
|
||||||
pub voxels: Handle<Image>,
|
|
||||||
|
|
||||||
/// Scale factor applied to the diffuse and specular light generated by this component.
|
|
||||||
///
|
|
||||||
/// After applying this multiplier, the resulting values should
|
|
||||||
/// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
|
|
||||||
///
|
|
||||||
/// See also <https://google.github.io/filament/Filament.html#lighting/imagebasedlights/iblunit>.
|
|
||||||
pub intensity: f32,
|
|
||||||
|
|
||||||
/// Whether the light from this irradiance volume has an effect on meshes
|
|
||||||
/// with lightmaps.
|
|
||||||
///
|
|
||||||
/// Set this to false if your lightmap baking tool bakes the light from this
|
|
||||||
/// irradiance volume into the lightmaps in order to avoid counting the
|
|
||||||
/// irradiance twice. Frequently, applications use irradiance volumes as a
|
|
||||||
/// lower-quality alternative to lightmaps for capturing indirect
|
|
||||||
/// illumination on dynamic objects, and such applications will want to set
|
|
||||||
/// this value to false.
|
|
||||||
///
|
|
||||||
/// By default, this is set to true.
|
|
||||||
pub affects_lightmapped_meshes: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for IrradianceVolume {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
IrradianceVolume {
|
|
||||||
voxels: default(),
|
|
||||||
intensity: 0.0,
|
|
||||||
affects_lightmapped_meshes: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All the bind group entries necessary for PBR shaders to access the
|
/// All the bind group entries necessary for PBR shaders to access the
|
||||||
/// irradiance volumes exposed to a view.
|
/// irradiance volumes exposed to a view.
|
||||||
pub(crate) enum RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
pub(crate) enum RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
||||||
|
|||||||
@ -288,8 +288,7 @@ impl Plugin for LightProbePlugin {
|
|||||||
load_shader_library!(app, "environment_map.wgsl");
|
load_shader_library!(app, "environment_map.wgsl");
|
||||||
load_shader_library!(app, "irradiance_volume.wgsl");
|
load_shader_library!(app, "irradiance_volume.wgsl");
|
||||||
|
|
||||||
app.register_type::<IrradianceVolume>()
|
app.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new());
|
||||||
.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&self, app: &mut App) {
|
fn finish(&self, app: &mut App) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use super::resource_manager::ResourceManager;
|
use super::resource_manager::ResourceManager;
|
||||||
use bevy_asset::{load_embedded_asset, Handle};
|
use bevy_asset::{load_embedded_asset, Handle};
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::CORE_3D_DEPTH_FORMAT, experimental::mip_generation::DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
core_3d::CORE_3D_DEPTH_FORMAT, experimental::mip_generation::DownsampleDepthShader,
|
||||||
FullscreenShader,
|
FullscreenShader,
|
||||||
};
|
};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
@ -84,6 +84,7 @@ impl FromWorld for MeshletPipelines {
|
|||||||
.remap_1d_to_2d_dispatch_bind_group_layout
|
.remap_1d_to_2d_dispatch_bind_group_layout
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
|
let downsample_depth_shader = (*world.resource::<DownsampleDepthShader>()).clone();
|
||||||
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state();
|
let vertex_state = world.resource::<FullscreenShader>().to_vertex_state();
|
||||||
let fill_counts_layout = resource_manager.fill_counts_bind_group_layout.clone();
|
let fill_counts_layout = resource_manager.fill_counts_bind_group_layout.clone();
|
||||||
|
|
||||||
@ -230,7 +231,7 @@ impl FromWorld for MeshletPipelines {
|
|||||||
stages: ShaderStages::COMPUTE,
|
stages: ShaderStages::COMPUTE,
|
||||||
range: 0..4,
|
range: 0..4,
|
||||||
}],
|
}],
|
||||||
shader: DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
shader: downsample_depth_shader.clone(),
|
||||||
shader_defs: vec![
|
shader_defs: vec![
|
||||||
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into(),
|
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into(),
|
||||||
"MESHLET".into(),
|
"MESHLET".into(),
|
||||||
@ -248,7 +249,7 @@ impl FromWorld for MeshletPipelines {
|
|||||||
stages: ShaderStages::COMPUTE,
|
stages: ShaderStages::COMPUTE,
|
||||||
range: 0..4,
|
range: 0..4,
|
||||||
}],
|
}],
|
||||||
shader: DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
shader: downsample_depth_shader.clone(),
|
||||||
shader_defs: vec![
|
shader_defs: vec![
|
||||||
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into(),
|
"MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into(),
|
||||||
"MESHLET".into(),
|
"MESHLET".into(),
|
||||||
@ -266,7 +267,7 @@ impl FromWorld for MeshletPipelines {
|
|||||||
stages: ShaderStages::COMPUTE,
|
stages: ShaderStages::COMPUTE,
|
||||||
range: 0..4,
|
range: 0..4,
|
||||||
}],
|
}],
|
||||||
shader: DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
shader: downsample_depth_shader.clone(),
|
||||||
shader_defs: vec!["MESHLET".into()],
|
shader_defs: vec!["MESHLET".into()],
|
||||||
entry_point: Some("downsample_depth_first".into()),
|
entry_point: Some("downsample_depth_first".into()),
|
||||||
..default()
|
..default()
|
||||||
@ -281,7 +282,7 @@ impl FromWorld for MeshletPipelines {
|
|||||||
stages: ShaderStages::COMPUTE,
|
stages: ShaderStages::COMPUTE,
|
||||||
range: 0..4,
|
range: 0..4,
|
||||||
}],
|
}],
|
||||||
shader: DOWNSAMPLE_DEPTH_SHADER_HANDLE,
|
shader: downsample_depth_shader,
|
||||||
shader_defs: vec!["MESHLET".into()],
|
shader_defs: vec!["MESHLET".into()],
|
||||||
entry_point: Some("downsample_depth_second".into()),
|
entry_point: Some("downsample_depth_second".into()),
|
||||||
zero_initialize_workgroup_memory: false,
|
zero_initialize_workgroup_memory: false,
|
||||||
|
|||||||
@ -30,12 +30,12 @@
|
|||||||
//! [Henyey-Greenstein phase function]: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
|
//! [Henyey-Greenstein phase function]: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
|
||||||
|
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{embedded_asset, Assets};
|
use bevy_asset::{embedded_asset, Assets, Handle};
|
||||||
use bevy_core_pipeline::core_3d::{
|
use bevy_core_pipeline::core_3d::{
|
||||||
graph::{Core3d, Node3d},
|
graph::{Core3d, Node3d},
|
||||||
prepare_core_3d_depth_textures,
|
prepare_core_3d_depth_textures,
|
||||||
};
|
};
|
||||||
use bevy_ecs::schedule::IntoScheduleConfigs as _;
|
use bevy_ecs::{resource::Resource, schedule::IntoScheduleConfigs as _};
|
||||||
use bevy_light::FogVolume;
|
use bevy_light::FogVolume;
|
||||||
use bevy_math::{
|
use bevy_math::{
|
||||||
primitives::{Cuboid, Plane3d},
|
primitives::{Cuboid, Plane3d},
|
||||||
@ -48,9 +48,7 @@ use bevy_render::{
|
|||||||
sync_component::SyncComponentPlugin,
|
sync_component::SyncComponentPlugin,
|
||||||
ExtractSchedule, Render, RenderApp, RenderSystems,
|
ExtractSchedule, Render, RenderApp, RenderSystems,
|
||||||
};
|
};
|
||||||
use render::{
|
use render::{VolumetricFogNode, VolumetricFogPipeline, VolumetricFogUniformBuffer};
|
||||||
VolumetricFogNode, VolumetricFogPipeline, VolumetricFogUniformBuffer, CUBE_MESH, PLANE_MESH,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::graph::NodePbr;
|
use crate::graph::NodePbr;
|
||||||
|
|
||||||
@ -59,13 +57,19 @@ pub mod render;
|
|||||||
/// A plugin that implements volumetric fog.
|
/// A plugin that implements volumetric fog.
|
||||||
pub struct VolumetricFogPlugin;
|
pub struct VolumetricFogPlugin;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct FogAssets {
|
||||||
|
plane_mesh: Handle<Mesh>,
|
||||||
|
cube_mesh: Handle<Mesh>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Plugin for VolumetricFogPlugin {
|
impl Plugin for VolumetricFogPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
embedded_asset!(app, "volumetric_fog.wgsl");
|
embedded_asset!(app, "volumetric_fog.wgsl");
|
||||||
|
|
||||||
let mut meshes = app.world_mut().resource_mut::<Assets<Mesh>>();
|
let mut meshes = app.world_mut().resource_mut::<Assets<Mesh>>();
|
||||||
meshes.insert(&PLANE_MESH, Plane3d::new(Vec3::Z, Vec2::ONE).mesh().into());
|
let plane_mesh = meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE).mesh());
|
||||||
meshes.insert(&CUBE_MESH, Cuboid::new(1.0, 1.0, 1.0).mesh().into());
|
let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0).mesh());
|
||||||
|
|
||||||
app.add_plugins(SyncComponentPlugin::<FogVolume>::default());
|
app.add_plugins(SyncComponentPlugin::<FogVolume>::default());
|
||||||
|
|
||||||
@ -74,6 +78,10 @@ impl Plugin for VolumetricFogPlugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render_app
|
render_app
|
||||||
|
.insert_resource(FogAssets {
|
||||||
|
plane_mesh,
|
||||||
|
cube_mesh,
|
||||||
|
})
|
||||||
.init_resource::<SpecializedRenderPipelines<VolumetricFogPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<VolumetricFogPipeline>>()
|
||||||
.init_resource::<VolumetricFogUniformBuffer>()
|
.init_resource::<VolumetricFogUniformBuffer>()
|
||||||
.add_systems(ExtractSchedule, render::extract_volumetric_fog)
|
.add_systems(ExtractSchedule, render::extract_volumetric_fog)
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use core::array;
|
use core::array;
|
||||||
|
|
||||||
use bevy_asset::{load_embedded_asset, uuid_handle, AssetId, Handle};
|
use bevy_asset::{load_embedded_asset, AssetId, Handle};
|
||||||
use bevy_color::ColorToComponents as _;
|
use bevy_color::ColorToComponents as _;
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::Camera3d,
|
core_3d::Camera3d,
|
||||||
@ -54,6 +54,8 @@ use crate::{
|
|||||||
VolumetricLight,
|
VolumetricLight,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::FogAssets;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Flags that describe the bind group layout used to render volumetric fog.
|
/// Flags that describe the bind group layout used to render volumetric fog.
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
@ -77,20 +79,6 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The plane mesh, which is used to render a fog volume that the camera is
|
|
||||||
/// inside.
|
|
||||||
///
|
|
||||||
/// This mesh is simply stretched to the size of the framebuffer, as when the
|
|
||||||
/// camera is inside a fog volume it's essentially a full-screen effect.
|
|
||||||
pub const PLANE_MESH: Handle<Mesh> = uuid_handle!("92523617-c708-4fd0-b42f-ceb4300c930b");
|
|
||||||
|
|
||||||
/// The cube mesh, which is used to render a fog volume that the camera is
|
|
||||||
/// outside.
|
|
||||||
///
|
|
||||||
/// Note that only the front faces of this cuboid will be rasterized in
|
|
||||||
/// hardware. The back faces will be calculated in the shader via raytracing.
|
|
||||||
pub const CUBE_MESH: Handle<Mesh> = uuid_handle!("4a1dd661-2d91-4377-a17a-a914e21e277e");
|
|
||||||
|
|
||||||
/// The total number of bind group layouts.
|
/// The total number of bind group layouts.
|
||||||
///
|
///
|
||||||
/// This is the total number of combinations of all
|
/// This is the total number of combinations of all
|
||||||
@ -370,6 +358,7 @@ impl ViewNode for VolumetricFogNode {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fog_assets = world.resource::<FogAssets>();
|
||||||
let render_meshes = world.resource::<RenderAssets<RenderMesh>>();
|
let render_meshes = world.resource::<RenderAssets<RenderMesh>>();
|
||||||
|
|
||||||
for view_fog_volume in view_fog_volumes.iter() {
|
for view_fog_volume in view_fog_volumes.iter() {
|
||||||
@ -377,9 +366,9 @@ impl ViewNode for VolumetricFogNode {
|
|||||||
// otherwise, pick the plane mesh. In the latter case we'll be
|
// otherwise, pick the plane mesh. In the latter case we'll be
|
||||||
// effectively rendering a full-screen quad.
|
// effectively rendering a full-screen quad.
|
||||||
let mesh_handle = if view_fog_volume.exterior {
|
let mesh_handle = if view_fog_volume.exterior {
|
||||||
CUBE_MESH.clone()
|
fog_assets.cube_mesh.clone()
|
||||||
} else {
|
} else {
|
||||||
PLANE_MESH.clone()
|
fog_assets.plane_mesh.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_handle.id())
|
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_handle.id())
|
||||||
@ -615,6 +604,7 @@ pub fn prepare_volumetric_fog_pipelines(
|
|||||||
pipeline_cache: Res<PipelineCache>,
|
pipeline_cache: Res<PipelineCache>,
|
||||||
mut pipelines: ResMut<SpecializedRenderPipelines<VolumetricFogPipeline>>,
|
mut pipelines: ResMut<SpecializedRenderPipelines<VolumetricFogPipeline>>,
|
||||||
volumetric_lighting_pipeline: Res<VolumetricFogPipeline>,
|
volumetric_lighting_pipeline: Res<VolumetricFogPipeline>,
|
||||||
|
fog_assets: Res<FogAssets>,
|
||||||
view_targets: Query<
|
view_targets: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
@ -629,7 +619,7 @@ pub fn prepare_volumetric_fog_pipelines(
|
|||||||
>,
|
>,
|
||||||
meshes: Res<RenderAssets<RenderMesh>>,
|
meshes: Res<RenderAssets<RenderMesh>>,
|
||||||
) {
|
) {
|
||||||
let Some(plane_mesh) = meshes.get(&PLANE_MESH) else {
|
let Some(plane_mesh) = meshes.get(&fog_assets.plane_mesh) else {
|
||||||
// There's an off chance that the mesh won't be prepared yet if `RenderAssetBytesPerFrame` limiting is in use.
|
// There's an off chance that the mesh won't be prepared yet if `RenderAssetBytesPerFrame` limiting is in use.
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -24,7 +24,6 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
|||||||
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
||||||
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
|
|||||||
@ -344,7 +344,7 @@ pub fn update_is_hovered(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Algorithm: for each entity having a `Hovered` component, we want to know if the current
|
// Algorithm: for each entity having a `Hovered` component, we want to know if the current
|
||||||
// entry in the hover map is "within" (that is, in the set of descenants of) that entity. Rather
|
// entry in the hover map is "within" (that is, in the set of descendants of) that entity. Rather
|
||||||
// than doing an expensive breadth-first traversal of children, instead start with the hovermap
|
// than doing an expensive breadth-first traversal of children, instead start with the hovermap
|
||||||
// entry and search upwards. We can make this even cheaper by building a set of ancestors for
|
// entry and search upwards. We can make this even cheaper by building a set of ancestors for
|
||||||
// the hovermap entry, and then testing each `Hovered` entity against that set.
|
// the hovermap entry, and then testing each `Hovered` entity against that set.
|
||||||
|
|||||||
@ -39,24 +39,30 @@ pub mod prelude {
|
|||||||
pub use crate::input::PointerInputPlugin;
|
pub use crate::input::PointerInputPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds mouse and touch inputs for picking pointers to your app. This is a default input plugin,
|
|
||||||
/// that you can replace with your own plugin as needed.
|
|
||||||
///
|
|
||||||
/// [`crate::PickingPlugin::is_input_enabled`] can be used to toggle whether
|
|
||||||
/// the core picking plugin processes the inputs sent by this, or other input plugins, in one place.
|
|
||||||
///
|
|
||||||
/// This plugin contains several settings, and is added to the world as a resource after initialization.
|
|
||||||
/// You can configure pointer input settings at runtime by accessing the resource.
|
|
||||||
#[derive(Copy, Clone, Resource, Debug, Reflect)]
|
#[derive(Copy, Clone, Resource, Debug, Reflect)]
|
||||||
#[reflect(Resource, Default, Clone)]
|
#[reflect(Resource, Default, Clone)]
|
||||||
pub struct PointerInputPlugin {
|
/// Settings for enabling and disabling updating mouse and touch inputs for picking
|
||||||
|
///
|
||||||
|
/// ## Custom initialization
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_app::App;
|
||||||
|
/// # use bevy_picking::input::{PointerInputSettings,PointerInputPlugin};
|
||||||
|
/// App::new()
|
||||||
|
/// .insert_resource(PointerInputSettings {
|
||||||
|
/// is_touch_enabled: false,
|
||||||
|
/// is_mouse_enabled: true,
|
||||||
|
/// })
|
||||||
|
/// // or DefaultPlugins
|
||||||
|
/// .add_plugins(PointerInputPlugin);
|
||||||
|
/// ```
|
||||||
|
pub struct PointerInputSettings {
|
||||||
/// Should touch inputs be updated?
|
/// Should touch inputs be updated?
|
||||||
pub is_touch_enabled: bool,
|
pub is_touch_enabled: bool,
|
||||||
/// Should mouse inputs be updated?
|
/// Should mouse inputs be updated?
|
||||||
pub is_mouse_enabled: bool,
|
pub is_mouse_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointerInputPlugin {
|
impl PointerInputSettings {
|
||||||
fn is_mouse_enabled(state: Res<Self>) -> bool {
|
fn is_mouse_enabled(state: Res<Self>) -> bool {
|
||||||
state.is_mouse_enabled
|
state.is_mouse_enabled
|
||||||
}
|
}
|
||||||
@ -66,7 +72,7 @@ impl PointerInputPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PointerInputPlugin {
|
impl Default for PointerInputSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_touch_enabled: true,
|
is_touch_enabled: true,
|
||||||
@ -75,25 +81,35 @@ impl Default for PointerInputPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds mouse and touch inputs for picking pointers to your app. This is a default input plugin,
|
||||||
|
/// that you can replace with your own plugin as needed.
|
||||||
|
///
|
||||||
|
/// Toggling mouse input or touch input can be done at runtime by modifying
|
||||||
|
/// [`PointerInputSettings`] resource.
|
||||||
|
///
|
||||||
|
/// [`PointerInputSettings`] can be initialized with custom values, but will be
|
||||||
|
/// initialized with default values if it is not present at the moment this is
|
||||||
|
/// added to the app.
|
||||||
|
pub struct PointerInputPlugin;
|
||||||
|
|
||||||
impl Plugin for PointerInputPlugin {
|
impl Plugin for PointerInputPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(*self)
|
app.init_resource::<PointerInputSettings>()
|
||||||
|
.register_type::<PointerInputSettings>()
|
||||||
.add_systems(Startup, spawn_mouse_pointer)
|
.add_systems(Startup, spawn_mouse_pointer)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
First,
|
First,
|
||||||
(
|
(
|
||||||
mouse_pick_events.run_if(PointerInputPlugin::is_mouse_enabled),
|
mouse_pick_events.run_if(PointerInputSettings::is_mouse_enabled),
|
||||||
touch_pick_events.run_if(PointerInputPlugin::is_touch_enabled),
|
touch_pick_events.run_if(PointerInputSettings::is_touch_enabled),
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.in_set(PickingSystems::Input),
|
.in_set(PickingSystems::Input),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Last,
|
Last,
|
||||||
deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled),
|
deactivate_touch_pointers.run_if(PointerInputSettings::is_touch_enabled),
|
||||||
)
|
);
|
||||||
.register_type::<Self>()
|
|
||||||
.register_type::<PointerInputPlugin>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -293,20 +293,31 @@ pub struct DefaultPickingPlugins;
|
|||||||
impl PluginGroup for DefaultPickingPlugins {
|
impl PluginGroup for DefaultPickingPlugins {
|
||||||
fn build(self) -> PluginGroupBuilder {
|
fn build(self) -> PluginGroupBuilder {
|
||||||
PluginGroupBuilder::start::<Self>()
|
PluginGroupBuilder::start::<Self>()
|
||||||
.add(input::PointerInputPlugin::default())
|
.add(input::PointerInputPlugin)
|
||||||
.add(PickingPlugin::default())
|
.add(PickingPlugin)
|
||||||
.add(InteractionPlugin)
|
.add(InteractionPlugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared
|
|
||||||
/// types used by other picking plugins.
|
|
||||||
///
|
|
||||||
/// This plugin contains several settings, and is added to the world as a resource after initialization. You
|
|
||||||
/// can configure picking settings at runtime through the resource.
|
|
||||||
#[derive(Copy, Clone, Debug, Resource, Reflect)]
|
#[derive(Copy, Clone, Debug, Resource, Reflect)]
|
||||||
#[reflect(Resource, Default, Debug, Clone)]
|
#[reflect(Resource, Default, Debug, Clone)]
|
||||||
pub struct PickingPlugin {
|
/// Controls the behavior of picking
|
||||||
|
///
|
||||||
|
/// ## Custom initialization
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_app::App;
|
||||||
|
/// # use bevy_picking::{PickingSettings, PickingPlugin};
|
||||||
|
/// App::new()
|
||||||
|
/// .insert_resource(PickingSettings {
|
||||||
|
/// is_enabled: true,
|
||||||
|
/// is_input_enabled: false,
|
||||||
|
/// is_hover_enabled: true,
|
||||||
|
/// is_window_picking_enabled: false,
|
||||||
|
/// })
|
||||||
|
/// // or DefaultPlugins
|
||||||
|
/// .add_plugins(PickingPlugin);
|
||||||
|
/// ```
|
||||||
|
pub struct PickingSettings {
|
||||||
/// Enables and disables all picking features.
|
/// Enables and disables all picking features.
|
||||||
pub is_enabled: bool,
|
pub is_enabled: bool,
|
||||||
/// Enables and disables input collection.
|
/// Enables and disables input collection.
|
||||||
@ -317,7 +328,7 @@ pub struct PickingPlugin {
|
|||||||
pub is_window_picking_enabled: bool,
|
pub is_window_picking_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickingPlugin {
|
impl PickingSettings {
|
||||||
/// Whether or not input collection systems should be running.
|
/// Whether or not input collection systems should be running.
|
||||||
pub fn input_should_run(state: Res<Self>) -> bool {
|
pub fn input_should_run(state: Res<Self>) -> bool {
|
||||||
state.is_input_enabled && state.is_enabled
|
state.is_input_enabled && state.is_enabled
|
||||||
@ -335,7 +346,7 @@ impl PickingPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PickingPlugin {
|
impl Default for PickingSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_enabled: true,
|
is_enabled: true,
|
||||||
@ -346,9 +357,18 @@ impl Default for PickingPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared
|
||||||
|
/// types used by other picking plugins.
|
||||||
|
///
|
||||||
|
/// Behavior of picking can be controlled by modifying [`PickingSettings`].
|
||||||
|
///
|
||||||
|
/// [`PickingSettings`] will be initialized with default values if it
|
||||||
|
/// is not present at the moment this is added to the app.
|
||||||
|
pub struct PickingPlugin;
|
||||||
|
|
||||||
impl Plugin for PickingPlugin {
|
impl Plugin for PickingPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(*self)
|
app.init_resource::<PickingSettings>()
|
||||||
.init_resource::<pointer::PointerMap>()
|
.init_resource::<pointer::PointerMap>()
|
||||||
.init_resource::<backend::ray::RayMap>()
|
.init_resource::<backend::ray::RayMap>()
|
||||||
.add_event::<pointer::PointerInput>()
|
.add_event::<pointer::PointerInput>()
|
||||||
@ -369,7 +389,7 @@ impl Plugin for PickingPlugin {
|
|||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
window::update_window_hits
|
window::update_window_hits
|
||||||
.run_if(Self::window_picking_should_run)
|
.run_if(PickingSettings::window_picking_should_run)
|
||||||
.in_set(PickingSystems::Backend),
|
.in_set(PickingSystems::Backend),
|
||||||
)
|
)
|
||||||
.configure_sets(
|
.configure_sets(
|
||||||
@ -382,15 +402,15 @@ impl Plugin for PickingPlugin {
|
|||||||
.configure_sets(
|
.configure_sets(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
(
|
(
|
||||||
PickingSystems::ProcessInput.run_if(Self::input_should_run),
|
PickingSystems::ProcessInput.run_if(PickingSettings::input_should_run),
|
||||||
PickingSystems::Backend,
|
PickingSystems::Backend,
|
||||||
PickingSystems::Hover.run_if(Self::hover_should_run),
|
PickingSystems::Hover.run_if(PickingSettings::hover_should_run),
|
||||||
PickingSystems::PostHover,
|
PickingSystems::PostHover,
|
||||||
PickingSystems::Last,
|
PickingSystems::Last,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
)
|
)
|
||||||
.register_type::<Self>()
|
.register_type::<PickingSettings>()
|
||||||
.register_type::<Pickable>()
|
.register_type::<Pickable>()
|
||||||
.register_type::<hover::PickingInteraction>()
|
.register_type::<hover::PickingInteraction>()
|
||||||
.register_type::<hover::Hovered>()
|
.register_type::<hover::Hovered>()
|
||||||
|
|||||||
@ -9,6 +9,7 @@ mod structs {
|
|||||||
|
|
||||||
#[reflect_remote(external_crate::TheirStruct)]
|
#[reflect_remote(external_crate::TheirStruct)]
|
||||||
//~^ ERROR: `?` operator has incompatible types
|
//~^ ERROR: `?` operator has incompatible types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
struct MyStruct {
|
struct MyStruct {
|
||||||
// Reason: Should be `u32`
|
// Reason: Should be `u32`
|
||||||
pub value: bool,
|
pub value: bool,
|
||||||
@ -25,6 +26,7 @@ mod tuple_structs {
|
|||||||
|
|
||||||
#[reflect_remote(external_crate::TheirStruct)]
|
#[reflect_remote(external_crate::TheirStruct)]
|
||||||
//~^ ERROR: `?` operator has incompatible types
|
//~^ ERROR: `?` operator has incompatible types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
struct MyStruct(
|
struct MyStruct(
|
||||||
// Reason: Should be `u32`
|
// Reason: Should be `u32`
|
||||||
pub bool,
|
pub bool,
|
||||||
@ -48,6 +50,7 @@ mod enums {
|
|||||||
//~| ERROR: variant `enums::external_crate::TheirStruct::Unit` has no field named `0`
|
//~| ERROR: variant `enums::external_crate::TheirStruct::Unit` has no field named `0`
|
||||||
//~| ERROR: `?` operator has incompatible types
|
//~| ERROR: `?` operator has incompatible types
|
||||||
//~| ERROR: `?` operator has incompatible types
|
//~| ERROR: `?` operator has incompatible types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
enum MyStruct {
|
enum MyStruct {
|
||||||
// Reason: Should be unit variant
|
// Reason: Should be unit variant
|
||||||
Unit(i32),
|
Unit(i32),
|
||||||
@ -57,6 +60,7 @@ mod enums {
|
|||||||
// Reason: Should be `usize`
|
// Reason: Should be `usize`
|
||||||
Struct { value: String },
|
Struct { value: String },
|
||||||
//~^ ERROR: mismatched types
|
//~^ ERROR: mismatched types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,8 +26,8 @@ mod incorrect_inner_type {
|
|||||||
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
|
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
|
||||||
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
|
//~| ERROR: `TheirInner<T>` does not implement `PartialReflect` so cannot be introspected
|
||||||
//~| ERROR: `TheirInner<T>` does not implement `TypePath` so cannot provide dynamic type path information
|
//~| ERROR: `TheirInner<T>` does not implement `TypePath` so cannot provide dynamic type path information
|
||||||
//~| ERROR: `TheirInner<T>` does not implement `TypePath` so cannot provide dynamic type path information
|
|
||||||
//~| ERROR: `?` operator has incompatible types
|
//~| ERROR: `?` operator has incompatible types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
struct MyOuter<T: FromReflect + GetTypeRegistration> {
|
struct MyOuter<T: FromReflect + GetTypeRegistration> {
|
||||||
// Reason: Should not use `MyInner<T>` directly
|
// Reason: Should not use `MyInner<T>` directly
|
||||||
pub inner: MyInner<T>,
|
pub inner: MyInner<T>,
|
||||||
|
|||||||
@ -77,6 +77,7 @@ mod enums {
|
|||||||
|
|
||||||
#[reflect_remote(external_crate::TheirBar)]
|
#[reflect_remote(external_crate::TheirBar)]
|
||||||
//~^ ERROR: `?` operator has incompatible types
|
//~^ ERROR: `?` operator has incompatible types
|
||||||
|
//~| ERROR: mismatched types
|
||||||
enum MyBar {
|
enum MyBar {
|
||||||
// Reason: Should use `i32`
|
// Reason: Should use `i32`
|
||||||
Value(u32),
|
Value(u32),
|
||||||
|
|||||||
@ -24,7 +24,6 @@ indexmap = "2.0"
|
|||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "2.0", features = ["full", "extra-traits"] }
|
syn = { version = "2.0", features = ["full", "extra-traits"] }
|
||||||
uuid = { version = "1.13.1", features = ["v4"] }
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
|
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
|
||||||
|
|||||||
@ -722,18 +722,7 @@ impl<'a> ReflectStruct<'a> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
#bevy_reflect_path::PartialReflect::reflect_clone(#accessor)?
|
<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#accessor)?
|
||||||
.take()
|
|
||||||
.map_err(|value| #bevy_reflect_path::ReflectCloneError::FailedDowncast {
|
|
||||||
expected: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
|
|
||||||
<#field_ty as #bevy_reflect_path::TypePath>::type_path()
|
|
||||||
),
|
|
||||||
received: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Owned(
|
|
||||||
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(
|
|
||||||
#bevy_reflect_path::DynamicTypePath::reflect_type_path(&*value)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -316,9 +316,7 @@ impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {
|
|||||||
|
|
||||||
fn construct_field(&self, field: VariantField) -> TokenStream {
|
fn construct_field(&self, field: VariantField) -> TokenStream {
|
||||||
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
|
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
|
||||||
|
|
||||||
let field_ty = field.field.reflected_type();
|
let field_ty = field.field.reflected_type();
|
||||||
|
|
||||||
let alias = field.alias;
|
let alias = field.alias;
|
||||||
let alias = match &field.field.attrs.remote {
|
let alias = match &field.field.attrs.remote {
|
||||||
Some(wrapper_ty) => {
|
Some(wrapper_ty) => {
|
||||||
@ -332,18 +330,7 @@ impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {
|
|||||||
match &field.field.attrs.clone {
|
match &field.field.attrs.clone {
|
||||||
CloneBehavior::Default => {
|
CloneBehavior::Default => {
|
||||||
quote! {
|
quote! {
|
||||||
#bevy_reflect_path::PartialReflect::reflect_clone(#alias)?
|
<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#alias)?
|
||||||
.take()
|
|
||||||
.map_err(|value| #bevy_reflect_path::ReflectCloneError::FailedDowncast {
|
|
||||||
expected: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(
|
|
||||||
<#field_ty as #bevy_reflect_path::TypePath>::type_path()
|
|
||||||
),
|
|
||||||
received: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Owned(
|
|
||||||
#bevy_reflect_path::__macro_exports::alloc_utils::ToString::to_string(
|
|
||||||
#bevy_reflect_path::DynamicTypePath::reflect_type_path(&*value)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
})?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CloneBehavior::Trait => {
|
CloneBehavior::Trait => {
|
||||||
|
|||||||
@ -231,11 +231,11 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre
|
|||||||
/// // Generates a where clause like:
|
/// // Generates a where clause like:
|
||||||
/// // impl bevy_reflect::Reflect for Foo
|
/// // impl bevy_reflect::Reflect for Foo
|
||||||
/// // where
|
/// // where
|
||||||
/// // Self: Any + Send + Sync,
|
/// // Foo: Any + Send + Sync,
|
||||||
/// // Vec<Foo>: FromReflect + TypePath,
|
/// // Vec<Foo>: FromReflect + TypePath + MaybeTyped + RegisterForReflection,
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// In this case, `Foo` is given the bounds `Vec<Foo>: FromReflect + TypePath`,
|
/// In this case, `Foo` is given the bounds `Vec<Foo>: FromReflect + ...`,
|
||||||
/// which requires that `Foo` implements `FromReflect`,
|
/// which requires that `Foo` implements `FromReflect`,
|
||||||
/// which requires that `Vec<Foo>` implements `FromReflect`,
|
/// which requires that `Vec<Foo>` implements `FromReflect`,
|
||||||
/// and so on, resulting in the error.
|
/// and so on, resulting in the error.
|
||||||
@ -283,10 +283,10 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre
|
|||||||
/// //
|
/// //
|
||||||
/// // impl<T: Trait> bevy_reflect::Reflect for Foo<T>
|
/// // impl<T: Trait> bevy_reflect::Reflect for Foo<T>
|
||||||
/// // where
|
/// // where
|
||||||
/// // Self: Any + Send + Sync,
|
/// // Foo<T>: Any + Send + Sync,
|
||||||
/// // T::Assoc: Default,
|
/// // T::Assoc: Default,
|
||||||
/// // T: TypePath,
|
/// // T: TypePath,
|
||||||
/// // T::Assoc: FromReflect + TypePath,
|
/// // T::Assoc: FromReflect + TypePath + MaybeTyped + RegisterForReflection,
|
||||||
/// // T::Assoc: List,
|
/// // T::Assoc: List,
|
||||||
/// // {/* ... */}
|
/// // {/* ... */}
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::derive_data::ReflectMeta;
|
use crate::derive_data::ReflectMeta;
|
||||||
use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
|
use bevy_macro_utils::fq_std::{FQAny, FQSend, FQSync};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{TokenStream, TokenTree};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{punctuated::Punctuated, Token, Type, WhereClause};
|
use syn::{punctuated::Punctuated, Ident, Token, Type, WhereClause};
|
||||||
|
|
||||||
/// Options defining how to extend the `where` clause for reflection.
|
/// Options defining how to extend the `where` clause for reflection.
|
||||||
pub(crate) struct WhereClauseOptions<'a, 'b> {
|
pub(crate) struct WhereClauseOptions<'a, 'b> {
|
||||||
@ -29,15 +29,24 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
|||||||
self.meta
|
self.meta
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the `where` clause for a type with additional bounds needed for the reflection impls.
|
/// Extends the `where` clause for a type with additional bounds needed for the reflection
|
||||||
|
/// impls.
|
||||||
///
|
///
|
||||||
/// The default bounds added are as follows:
|
/// The default bounds added are as follows:
|
||||||
/// - `Self` has the bounds `Any + Send + Sync`
|
/// - `Self` has:
|
||||||
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is present
|
/// - `Any + Send + Sync` bounds, if generic over types
|
||||||
/// - Active fields have the bounds `TypePath` and either `PartialReflect` if `#[reflect(from_reflect = false)]` is present
|
/// - An `Any` bound, if generic over lifetimes but not types
|
||||||
/// or `FromReflect` otherwise (or no bounds at all if `#[reflect(no_field_bounds)]` is present)
|
/// - No bounds, if generic over neither types nor lifetimes
|
||||||
|
/// - Any given bounds in a `where` clause on the type
|
||||||
|
/// - Type parameters have the bound `TypePath` unless `#[reflect(type_path = false)]` is
|
||||||
|
/// present
|
||||||
|
/// - Active fields with non-generic types have the bounds `TypePath`, either `PartialReflect`
|
||||||
|
/// if `#[reflect(from_reflect = false)]` is present or `FromReflect` otherwise,
|
||||||
|
/// `MaybeTyped`, and `RegisterForReflection` (or no bounds at all if
|
||||||
|
/// `#[reflect(no_field_bounds)]` is present)
|
||||||
///
|
///
|
||||||
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are added as well.
|
/// When the derive is used with `#[reflect(where)]`, the bounds specified in the attribute are
|
||||||
|
/// added as well.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -55,57 +64,69 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
|||||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||||
/// where
|
/// where
|
||||||
/// // `Self` bounds:
|
/// // `Self` bounds:
|
||||||
/// Self: Any + Send + Sync,
|
/// Foo<T, U>: Any + Send + Sync,
|
||||||
/// // Type parameter bounds:
|
/// // Type parameter bounds:
|
||||||
/// T: TypePath,
|
/// T: TypePath,
|
||||||
/// U: TypePath,
|
/// U: TypePath,
|
||||||
/// // Field bounds
|
/// // Active non-generic field bounds
|
||||||
/// T: FromReflect + TypePath,
|
/// T: FromReflect + TypePath + MaybeTyped + RegisterForReflection,
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// If we had added `#[reflect(where T: MyTrait)]` to the type, it would instead generate:
|
/// If we add various things to the type:
|
||||||
|
///
|
||||||
|
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||||
|
/// #[derive(Reflect)]
|
||||||
|
/// #[reflect(where T: MyTrait)]
|
||||||
|
/// #[reflect(no_field_bounds)]
|
||||||
|
/// struct Foo<T, U>
|
||||||
|
/// where T: Clone
|
||||||
|
/// {
|
||||||
|
/// a: T,
|
||||||
|
/// #[reflect(ignore)]
|
||||||
|
/// b: U
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// It will instead generate the following where clause:
|
||||||
///
|
///
|
||||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
/// ```ignore (bevy_reflect is not accessible from this crate)
|
||||||
/// where
|
/// where
|
||||||
/// // `Self` bounds:
|
/// // `Self` bounds:
|
||||||
/// Self: Any + Send + Sync,
|
/// Foo<T, U>: Any + Send + Sync,
|
||||||
/// // Type parameter bounds:
|
/// // Given bounds:
|
||||||
/// T: TypePath,
|
/// T: Clone,
|
||||||
/// U: TypePath,
|
|
||||||
/// // Field bounds
|
|
||||||
/// T: FromReflect + TypePath,
|
|
||||||
/// // Custom bounds
|
|
||||||
/// T: MyTrait,
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// And if we also added `#[reflect(no_field_bounds)]` to the type, it would instead generate:
|
|
||||||
///
|
|
||||||
/// ```ignore (bevy_reflect is not accessible from this crate)
|
|
||||||
/// where
|
|
||||||
/// // `Self` bounds:
|
|
||||||
/// Self: Any + Send + Sync,
|
|
||||||
/// // Type parameter bounds:
|
/// // Type parameter bounds:
|
||||||
/// T: TypePath,
|
/// T: TypePath,
|
||||||
/// U: TypePath,
|
/// U: TypePath,
|
||||||
|
/// // No active non-generic field bounds
|
||||||
/// // Custom bounds
|
/// // Custom bounds
|
||||||
/// T: MyTrait,
|
/// T: MyTrait,
|
||||||
/// ```
|
/// ```
|
||||||
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
|
pub fn extend_where_clause(&self, where_clause: Option<&WhereClause>) -> TokenStream {
|
||||||
// We would normally just use `Self`, but that won't work for generating things like assertion functions
|
let mut generic_where_clause = quote! { where };
|
||||||
// and trait impls for a type's reference (e.g. `impl FromArg for &MyType`)
|
|
||||||
|
// Bounds on `Self`. We would normally just use `Self`, but that won't work for generating
|
||||||
|
// things like assertion functions and trait impls for a type's reference (e.g. `impl
|
||||||
|
// FromArg for &MyType`).
|
||||||
|
let generics = self.meta.type_path().generics();
|
||||||
|
if generics.type_params().next().is_some() {
|
||||||
|
// Generic over types? We need `Any + Send + Sync`.
|
||||||
let this = self.meta.type_path().true_type();
|
let this = self.meta.type_path().true_type();
|
||||||
|
generic_where_clause.extend(quote! { #this: #FQAny + #FQSend + #FQSync, });
|
||||||
|
} else if generics.lifetimes().next().is_some() {
|
||||||
|
// Generic only over lifetimes? We need `'static`.
|
||||||
|
let this = self.meta.type_path().true_type();
|
||||||
|
generic_where_clause.extend(quote! { #this: 'static, });
|
||||||
|
}
|
||||||
|
|
||||||
let required_bounds = self.required_bounds();
|
// Maintain existing where clause bounds, if any.
|
||||||
|
if let Some(where_clause) = where_clause {
|
||||||
// Maintain existing where clause, if any.
|
|
||||||
let mut generic_where_clause = if let Some(where_clause) = where_clause {
|
|
||||||
let predicates = where_clause.predicates.iter();
|
let predicates = where_clause.predicates.iter();
|
||||||
quote! {where #this: #required_bounds, #(#predicates,)*}
|
generic_where_clause.extend(quote! { #(#predicates,)* });
|
||||||
} else {
|
}
|
||||||
quote!(where #this: #required_bounds,)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add additional reflection trait bounds
|
// Add additional reflection trait bounds.
|
||||||
let predicates = self.predicates();
|
let predicates = self.predicates();
|
||||||
generic_where_clause.extend(quote! {
|
generic_where_clause.extend(quote! {
|
||||||
#predicates
|
#predicates
|
||||||
@ -157,19 +178,57 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
|||||||
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
let bevy_reflect_path = self.meta.bevy_reflect_path();
|
||||||
let reflect_bound = self.reflect_bound();
|
let reflect_bound = self.reflect_bound();
|
||||||
|
|
||||||
// `TypePath` is always required for active fields since they are used to
|
// Get the identifiers of all type parameters.
|
||||||
// construct `NamedField` and `UnnamedField` instances for the `Typed` impl.
|
let type_param_idents = self
|
||||||
// Likewise, `GetTypeRegistration` is always required for active fields since
|
.meta
|
||||||
// they are used to register the type's dependencies.
|
.type_path()
|
||||||
Some(self.active_fields.iter().map(move |ty| {
|
.generics()
|
||||||
|
.type_params()
|
||||||
|
.map(|type_param| type_param.ident.clone())
|
||||||
|
.collect::<Vec<Ident>>();
|
||||||
|
|
||||||
|
// Do any of the identifiers in `idents` appear in `token_stream`?
|
||||||
|
fn is_any_ident_in_token_stream(idents: &[Ident], token_stream: TokenStream) -> bool {
|
||||||
|
for token_tree in token_stream {
|
||||||
|
match token_tree {
|
||||||
|
TokenTree::Ident(ident) => {
|
||||||
|
if idents.contains(&ident) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenTree::Group(group) => {
|
||||||
|
if is_any_ident_in_token_stream(idents, group.stream()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenTree::Punct(_) | TokenTree::Literal(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self.active_fields.iter().filter_map(move |ty| {
|
||||||
|
// Field type bounds are only required if `ty` is generic. How to determine that?
|
||||||
|
// Search `ty`s token stream for identifiers that match the identifiers from the
|
||||||
|
// function's type params. E.g. if `T` and `U` are the type param identifiers and
|
||||||
|
// `ty` is `Vec<[T; 4]>` then the `T` identifiers match. This is a bit hacky, but
|
||||||
|
// it works.
|
||||||
|
let is_generic =
|
||||||
|
is_any_ident_in_token_stream(&type_param_idents, ty.to_token_stream());
|
||||||
|
|
||||||
|
is_generic.then(|| {
|
||||||
quote!(
|
quote!(
|
||||||
#ty: #reflect_bound
|
#ty: #reflect_bound
|
||||||
|
// Needed to construct `NamedField` and `UnnamedField` instances for
|
||||||
|
// the `Typed` impl.
|
||||||
+ #bevy_reflect_path::TypePath
|
+ #bevy_reflect_path::TypePath
|
||||||
// Needed for `Typed` impls
|
// Needed for `Typed` impls
|
||||||
+ #bevy_reflect_path::MaybeTyped
|
+ #bevy_reflect_path::MaybeTyped
|
||||||
// Needed for `GetTypeRegistration` impls
|
// Needed for registering type dependencies in the
|
||||||
|
// `GetTypeRegistration` impl.
|
||||||
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
|
+ #bevy_reflect_path::__macro_exports::RegisterForReflection
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,9 +253,4 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum required bounds for a type to be reflected.
|
|
||||||
fn required_bounds(&self) -> TokenStream {
|
|
||||||
quote!(#FQAny + #FQSend + #FQSync)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ use crate::{
|
|||||||
type_registry::{FromType, GetTypeRegistration, ReflectFromPtr, TypeRegistration},
|
type_registry::{FromType, GetTypeRegistration, ReflectFromPtr, TypeRegistration},
|
||||||
utility::GenericTypeInfoCell,
|
utility::GenericTypeInfoCell,
|
||||||
};
|
};
|
||||||
use alloc::borrow::Cow;
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bevy_platform::prelude::*;
|
use bevy_platform::prelude::*;
|
||||||
use bevy_reflect_derive::impl_type_path;
|
use bevy_reflect_derive::impl_type_path;
|
||||||
@ -144,21 +143,8 @@ where
|
|||||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||||
let mut map = Self::new();
|
let mut map = Self::new();
|
||||||
for (key, value) in self.iter() {
|
for (key, value) in self.iter() {
|
||||||
let key =
|
let key = key.reflect_clone_and_take()?;
|
||||||
key.reflect_clone()?
|
let value = value.reflect_clone_and_take()?;
|
||||||
.take()
|
|
||||||
.map_err(|_| ReflectCloneError::FailedDowncast {
|
|
||||||
expected: Cow::Borrowed(<Self as TypePath>::type_path()),
|
|
||||||
received: Cow::Owned(key.reflect_type_path().to_string()),
|
|
||||||
})?;
|
|
||||||
let value =
|
|
||||||
value
|
|
||||||
.reflect_clone()?
|
|
||||||
.take()
|
|
||||||
.map_err(|_| ReflectCloneError::FailedDowncast {
|
|
||||||
expected: Cow::Borrowed(<Self as TypePath>::type_path()),
|
|
||||||
received: Cow::Owned(value.reflect_type_path().to_string()),
|
|
||||||
})?;
|
|
||||||
map.insert(key, value);
|
map.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use bevy_platform::prelude::*;
|
use bevy_platform::prelude::*;
|
||||||
use bevy_reflect_derive::impl_type_path;
|
use bevy_reflect_derive::impl_type_path;
|
||||||
use core::any::Any;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
macro_rules! impl_reflect_for_atomic {
|
macro_rules! impl_reflect_for_atomic {
|
||||||
@ -21,10 +20,7 @@ macro_rules! impl_reflect_for_atomic {
|
|||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
crate::func::macros::impl_function_traits!($ty);
|
crate::func::macros::impl_function_traits!($ty);
|
||||||
|
|
||||||
impl GetTypeRegistration for $ty
|
impl GetTypeRegistration for $ty {
|
||||||
where
|
|
||||||
$ty: Any + Send + Sync,
|
|
||||||
{
|
|
||||||
fn get_type_registration() -> TypeRegistration {
|
fn get_type_registration() -> TypeRegistration {
|
||||||
let mut registration = TypeRegistration::of::<Self>();
|
let mut registration = TypeRegistration::of::<Self>();
|
||||||
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
|
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
|
||||||
@ -42,10 +38,7 @@ macro_rules! impl_reflect_for_atomic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Typed for $ty
|
impl Typed for $ty {
|
||||||
where
|
|
||||||
$ty: Any + Send + Sync,
|
|
||||||
{
|
|
||||||
fn type_info() -> &'static TypeInfo {
|
fn type_info() -> &'static TypeInfo {
|
||||||
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
|
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
|
||||||
CELL.get_or_set(|| {
|
CELL.get_or_set(|| {
|
||||||
@ -55,10 +48,7 @@ macro_rules! impl_reflect_for_atomic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialReflect for $ty
|
impl PartialReflect for $ty {
|
||||||
where
|
|
||||||
$ty: Any + Send + Sync,
|
|
||||||
{
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
||||||
Some(<Self as Typed>::type_info())
|
Some(<Self as Typed>::type_info())
|
||||||
@ -128,10 +118,7 @@ macro_rules! impl_reflect_for_atomic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromReflect for $ty
|
impl FromReflect for $ty {
|
||||||
where
|
|
||||||
$ty: Any + Send + Sync,
|
|
||||||
{
|
|
||||||
fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> {
|
fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> {
|
||||||
Some(<$ty>::new(
|
Some(<$ty>::new(
|
||||||
reflect.try_downcast_ref::<$ty>()?.load($ordering),
|
reflect.try_downcast_ref::<$ty>()?.load($ordering),
|
||||||
@ -140,7 +127,7 @@ macro_rules! impl_reflect_for_atomic {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
impl_full_reflect!(for $ty where $ty: Any + Send + Sync);
|
impl_full_reflect!(for $ty);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -113,14 +113,7 @@ macro_rules! impl_reflect_for_veclike {
|
|||||||
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
||||||
Ok(bevy_platform::prelude::Box::new(
|
Ok(bevy_platform::prelude::Box::new(
|
||||||
self.iter()
|
self.iter()
|
||||||
.map(|value| {
|
.map(|value| value.reflect_clone_and_take())
|
||||||
value.reflect_clone()?.take().map_err(|_| {
|
|
||||||
$crate::error::ReflectCloneError::FailedDowncast {
|
|
||||||
expected: alloc::borrow::Cow::Borrowed(<T as $crate::type_path::TypePath>::type_path()),
|
|
||||||
received: alloc::borrow::Cow::Owned(alloc::string::ToString::to_string(value.reflect_type_path())),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Self, $crate::error::ReflectCloneError>>()?,
|
.collect::<Result<Self, $crate::error::ReflectCloneError>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,18 +146,8 @@ macro_rules! impl_reflect_for_hashmap {
|
|||||||
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
||||||
let mut map = Self::with_capacity_and_hasher(self.len(), S::default());
|
let mut map = Self::with_capacity_and_hasher(self.len(), S::default());
|
||||||
for (key, value) in self.iter() {
|
for (key, value) in self.iter() {
|
||||||
let key = key.reflect_clone()?.take().map_err(|_| {
|
let key = key.reflect_clone_and_take()?;
|
||||||
$crate::error::ReflectCloneError::FailedDowncast {
|
let value = value.reflect_clone_and_take()?;
|
||||||
expected: alloc::borrow::Cow::Borrowed(<K as $crate::type_path::TypePath>::type_path()),
|
|
||||||
received: alloc::borrow::Cow::Owned(alloc::string::ToString::to_string(key.reflect_type_path())),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
let value = value.reflect_clone()?.take().map_err(|_| {
|
|
||||||
$crate::error::ReflectCloneError::FailedDowncast {
|
|
||||||
expected: alloc::borrow::Cow::Borrowed(<V as $crate::type_path::TypePath>::type_path()),
|
|
||||||
received: alloc::borrow::Cow::Owned(alloc::string::ToString::to_string(value.reflect_type_path())),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
map.insert(key, value);
|
map.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -129,12 +129,7 @@ macro_rules! impl_reflect_for_hashset {
|
|||||||
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
fn reflect_clone(&self) -> Result<bevy_platform::prelude::Box<dyn $crate::reflect::Reflect>, $crate::error::ReflectCloneError> {
|
||||||
let mut set = Self::with_capacity_and_hasher(self.len(), S::default());
|
let mut set = Self::with_capacity_and_hasher(self.len(), S::default());
|
||||||
for value in self.iter() {
|
for value in self.iter() {
|
||||||
let value = value.reflect_clone()?.take().map_err(|_| {
|
let value = value.reflect_clone_and_take()?;
|
||||||
$crate::error::ReflectCloneError::FailedDowncast {
|
|
||||||
expected: alloc::borrow::Cow::Borrowed(<V as $crate::type_path::TypePath>::type_path()),
|
|
||||||
received: alloc::borrow::Cow::Owned(alloc::string::ToString::to_string(value.reflect_type_path())),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
set.insert(value);
|
set.insert(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::{
|
|||||||
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo, TypePath, TypeRegistration,
|
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo, TypePath, TypeRegistration,
|
||||||
Typed,
|
Typed,
|
||||||
};
|
};
|
||||||
use alloc::{borrow::Cow, boxed::Box, string::ToString, vec::Vec};
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
use bevy_reflect::ReflectCloneError;
|
use bevy_reflect::ReflectCloneError;
|
||||||
use bevy_reflect_derive::impl_type_path;
|
use bevy_reflect_derive::impl_type_path;
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
@ -137,16 +137,11 @@ where
|
|||||||
|
|
||||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||||
Ok(Box::new(
|
Ok(Box::new(
|
||||||
self.iter()
|
// `(**self)` avoids getting `SmallVec<T> as List::iter`, which
|
||||||
.map(|value| {
|
// would give us the wrong item type.
|
||||||
value
|
(**self)
|
||||||
.reflect_clone()?
|
.iter()
|
||||||
.take()
|
.map(PartialReflect::reflect_clone_and_take)
|
||||||
.map_err(|_| ReflectCloneError::FailedDowncast {
|
|
||||||
expected: Cow::Borrowed(<T::Item as TypePath>::type_path()),
|
|
||||||
received: Cow::Owned(value.reflect_type_path().to_string()),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Self, ReflectCloneError>>()?,
|
.collect::<Result<Self, ReflectCloneError>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -215,7 +215,7 @@ pub enum ReflectRef<'a> {
|
|||||||
/// [function-like]: Function
|
/// [function-like]: Function
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Function(&'a dyn Function),
|
Function(&'a dyn Function),
|
||||||
/// An immutable refeence to an [opaque] type.
|
/// An immutable reference to an [opaque] type.
|
||||||
///
|
///
|
||||||
/// [opaque]: ReflectKind::Opaque
|
/// [opaque]: ReflectKind::Opaque
|
||||||
Opaque(&'a dyn PartialReflect),
|
Opaque(&'a dyn PartialReflect),
|
||||||
@ -281,7 +281,7 @@ pub enum ReflectMut<'a> {
|
|||||||
///
|
///
|
||||||
/// [function-like]: Function
|
/// [function-like]: Function
|
||||||
Function(&'a mut dyn Function),
|
Function(&'a mut dyn Function),
|
||||||
/// A mutable refeence to an [opaque] type.
|
/// A mutable reference to an [opaque] type.
|
||||||
///
|
///
|
||||||
/// [opaque]: ReflectKind::Opaque
|
/// [opaque]: ReflectKind::Opaque
|
||||||
Opaque(&'a mut dyn PartialReflect),
|
Opaque(&'a mut dyn PartialReflect),
|
||||||
|
|||||||
@ -2615,9 +2615,11 @@ bevy_reflect::tests::Test {
|
|||||||
#[reflect(where T: Default)]
|
#[reflect(where T: Default)]
|
||||||
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
|
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
|
||||||
|
|
||||||
|
#[expect(dead_code, reason = "Bar is never constructed")]
|
||||||
#[derive(Default, TypePath)]
|
#[derive(Default, TypePath)]
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
|
#[expect(dead_code, reason = "Baz is never constructed")]
|
||||||
#[derive(TypePath)]
|
#[derive(TypePath)]
|
||||||
struct Baz;
|
struct Baz;
|
||||||
|
|
||||||
@ -2631,6 +2633,7 @@ bevy_reflect::tests::Test {
|
|||||||
#[reflect(where)]
|
#[reflect(where)]
|
||||||
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
|
struct Foo<T>(String, #[reflect(ignore)] PhantomData<T>);
|
||||||
|
|
||||||
|
#[expect(dead_code, reason = "Bar is never constructed")]
|
||||||
#[derive(TypePath)]
|
#[derive(TypePath)]
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
@ -2665,6 +2668,7 @@ bevy_reflect::tests::Test {
|
|||||||
#[reflect(where T::Assoc: core::fmt::Display)]
|
#[reflect(where T::Assoc: core::fmt::Display)]
|
||||||
struct Foo<T: Trait>(T::Assoc);
|
struct Foo<T: Trait>(T::Assoc);
|
||||||
|
|
||||||
|
#[expect(dead_code, reason = "Bar is never constructed")]
|
||||||
#[derive(TypePath)]
|
#[derive(TypePath)]
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
@ -2672,6 +2676,7 @@ bevy_reflect::tests::Test {
|
|||||||
type Assoc = usize;
|
type Assoc = usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code, reason = "Baz is never constructed")]
|
||||||
#[derive(TypePath)]
|
#[derive(TypePath)]
|
||||||
struct Baz;
|
struct Baz;
|
||||||
|
|
||||||
|
|||||||
@ -313,6 +313,24 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For a type implementing [`PartialReflect`], combines `reflect_clone` and
|
||||||
|
/// `take` in a useful fashion, automatically constructing an appropriate
|
||||||
|
/// [`ReflectCloneError`] if the downcast fails.
|
||||||
|
///
|
||||||
|
/// This is an associated function, rather than a method, because methods
|
||||||
|
/// with generic types prevent dyn-compatibility.
|
||||||
|
fn reflect_clone_and_take<T: 'static>(&self) -> Result<T, ReflectCloneError>
|
||||||
|
where
|
||||||
|
Self: TypePath + Sized,
|
||||||
|
{
|
||||||
|
self.reflect_clone()?
|
||||||
|
.take()
|
||||||
|
.map_err(|_| ReflectCloneError::FailedDowncast {
|
||||||
|
expected: Cow::Borrowed(<Self as TypePath>::type_path()),
|
||||||
|
received: Cow::Owned(self.reflect_type_path().to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a hash of the value (which includes the type).
|
/// Returns a hash of the value (which includes the type).
|
||||||
///
|
///
|
||||||
/// If the underlying type does not support hashing, returns `None`.
|
/// If the underlying type does not support hashing, returns `None`.
|
||||||
|
|||||||
@ -197,7 +197,7 @@ fn renderer_extract(app_world: &mut World, _world: &mut World) {
|
|||||||
render_channels.send_blocking(render_app);
|
render_channels.send_blocking(render_app);
|
||||||
} else {
|
} else {
|
||||||
// Renderer thread panicked
|
// Renderer thread panicked
|
||||||
world.send_event(AppExit::error());
|
world.write_event(AppExit::error());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,7 +13,6 @@ bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" }
|
|||||||
|
|
||||||
syn = { version = "2.0", features = ["full"] }
|
syn = { version = "2.0", features = ["full"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
proc-macro2 = "1.0"
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@ -107,7 +107,7 @@ impl AppExtStates for SubApp {
|
|||||||
);
|
);
|
||||||
S::register_state(schedule);
|
S::register_state(schedule);
|
||||||
let state = self.world().resource::<State<S>>().get().clone();
|
let state = self.world().resource::<State<S>>().get().clone();
|
||||||
self.world_mut().send_event(StateTransitionEvent {
|
self.world_mut().write_event(StateTransitionEvent {
|
||||||
exited: None,
|
exited: None,
|
||||||
entered: Some(state),
|
entered: Some(state),
|
||||||
});
|
});
|
||||||
@ -132,7 +132,7 @@ impl AppExtStates for SubApp {
|
|||||||
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling insert_state?"
|
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling insert_state?"
|
||||||
);
|
);
|
||||||
S::register_state(schedule);
|
S::register_state(schedule);
|
||||||
self.world_mut().send_event(StateTransitionEvent {
|
self.world_mut().write_event(StateTransitionEvent {
|
||||||
exited: None,
|
exited: None,
|
||||||
entered: Some(state),
|
entered: Some(state),
|
||||||
});
|
});
|
||||||
@ -145,7 +145,7 @@ impl AppExtStates for SubApp {
|
|||||||
self.world_mut()
|
self.world_mut()
|
||||||
.resource_mut::<Events<StateTransitionEvent<S>>>()
|
.resource_mut::<Events<StateTransitionEvent<S>>>()
|
||||||
.clear();
|
.clear();
|
||||||
self.world_mut().send_event(StateTransitionEvent {
|
self.world_mut().write_event(StateTransitionEvent {
|
||||||
exited: None,
|
exited: None,
|
||||||
entered: Some(state),
|
entered: Some(state),
|
||||||
});
|
});
|
||||||
@ -169,7 +169,7 @@ impl AppExtStates for SubApp {
|
|||||||
.world()
|
.world()
|
||||||
.get_resource::<State<S>>()
|
.get_resource::<State<S>>()
|
||||||
.map(|s| s.get().clone());
|
.map(|s| s.get().clone());
|
||||||
self.world_mut().send_event(StateTransitionEvent {
|
self.world_mut().write_event(StateTransitionEvent {
|
||||||
exited: None,
|
exited: None,
|
||||||
entered: state,
|
entered: state,
|
||||||
});
|
});
|
||||||
@ -200,7 +200,7 @@ impl AppExtStates for SubApp {
|
|||||||
.world()
|
.world()
|
||||||
.get_resource::<State<S>>()
|
.get_resource::<State<S>>()
|
||||||
.map(|s| s.get().clone());
|
.map(|s| s.get().clone());
|
||||||
self.world_mut().send_event(StateTransitionEvent {
|
self.world_mut().write_event(StateTransitionEvent {
|
||||||
exited: None,
|
exited: None,
|
||||||
entered: state,
|
entered: state,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -50,6 +50,8 @@ pub struct OnTransition<S: States> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This schedule is split up into four phases, as described in [`StateTransitionSteps`].
|
||||||
|
///
|
||||||
/// [`PreStartup`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreStartup.html
|
/// [`PreStartup`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreStartup.html
|
||||||
/// [`PreUpdate`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreUpdate.html
|
/// [`PreUpdate`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreUpdate.html
|
||||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
||||||
|
|||||||
@ -213,8 +213,8 @@ mod tests {
|
|||||||
app.add_event::<StateScopedEvent>()
|
app.add_event::<StateScopedEvent>()
|
||||||
.clear_events_on_exit_state::<StateScopedEvent>(TestState::A);
|
.clear_events_on_exit_state::<StateScopedEvent>(TestState::A);
|
||||||
|
|
||||||
app.world_mut().send_event(StandardEvent).unwrap();
|
app.world_mut().write_event(StandardEvent).unwrap();
|
||||||
app.world_mut().send_event(StateScopedEvent).unwrap();
|
app.world_mut().write_event(StateScopedEvent).unwrap();
|
||||||
assert!(!app.world().resource::<Events<StandardEvent>>().is_empty());
|
assert!(!app.world().resource::<Events<StandardEvent>>().is_empty());
|
||||||
assert!(!app
|
assert!(!app
|
||||||
.world()
|
.world()
|
||||||
@ -243,8 +243,8 @@ mod tests {
|
|||||||
app.add_event::<StateScopedEvent>()
|
app.add_event::<StateScopedEvent>()
|
||||||
.clear_events_on_enter_state::<StateScopedEvent>(TestState::B);
|
.clear_events_on_enter_state::<StateScopedEvent>(TestState::B);
|
||||||
|
|
||||||
app.world_mut().send_event(StandardEvent).unwrap();
|
app.world_mut().write_event(StandardEvent).unwrap();
|
||||||
app.world_mut().send_event(StateScopedEvent).unwrap();
|
app.world_mut().write_event(StateScopedEvent).unwrap();
|
||||||
assert!(!app.world().resource::<Events<StandardEvent>>().is_empty());
|
assert!(!app.world().resource::<Events<StandardEvent>>().is_empty());
|
||||||
assert!(!app
|
assert!(!app
|
||||||
.world()
|
.world()
|
||||||
|
|||||||
@ -343,15 +343,15 @@ mod tests {
|
|||||||
let fixed_update_timestep = Time::<Fixed>::default().timestep();
|
let fixed_update_timestep = Time::<Fixed>::default().timestep();
|
||||||
let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
|
let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
|
||||||
|
|
||||||
fn send_event(mut events: ResMut<Events<DummyEvent>>) {
|
fn write_event(mut events: ResMut<Events<DummyEvent>>) {
|
||||||
events.send(DummyEvent);
|
events.write(DummyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.add_plugins(TimePlugin)
|
app.add_plugins(TimePlugin)
|
||||||
.add_event::<DummyEvent>()
|
.add_event::<DummyEvent>()
|
||||||
.init_resource::<FixedUpdateCounter>()
|
.init_resource::<FixedUpdateCounter>()
|
||||||
.add_systems(Startup, send_event)
|
.add_systems(Startup, write_event)
|
||||||
.add_systems(FixedUpdate, count_fixed_updates)
|
.add_systems(FixedUpdate, count_fixed_updates)
|
||||||
.insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
|
.insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
|
||||||
|
|
||||||
|
|||||||
@ -36,10 +36,8 @@ bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-fea
|
|||||||
taffy = { version = "0.7" }
|
taffy = { version = "0.7" }
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
uuid = { version = "1.1", features = ["v4"], optional = true }
|
uuid = { version = "1.1", features = ["v4"], optional = true }
|
||||||
bytemuck = { version = "1.5", features = ["derive"] }
|
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
derive_more = { version = "2", default-features = false, features = ["from"] }
|
derive_more = { version = "2", default-features = false, features = ["from"] }
|
||||||
nonmax = "0.5"
|
|
||||||
smallvec = { version = "1", default-features = false }
|
smallvec = { version = "1", default-features = false }
|
||||||
accesskit = "0.19"
|
accesskit = "0.19"
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|||||||
@ -631,6 +631,14 @@ pub enum InterpolationColorSpace {
|
|||||||
Srgb,
|
Srgb,
|
||||||
/// Interpolates in linear sRGB space.
|
/// Interpolates in linear sRGB space.
|
||||||
LinearRgb,
|
LinearRgb,
|
||||||
|
/// Interpolates in HSL space, taking the shortest hue path.
|
||||||
|
Hsl,
|
||||||
|
/// Interpolates in HSL space, taking the longest hue path.
|
||||||
|
HslLong,
|
||||||
|
/// Interpolates in HSV space, taking the shortest hue path.
|
||||||
|
Hsv,
|
||||||
|
/// Interpolates in HSV space, taking the longest hue path.
|
||||||
|
HsvLong,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the color space used for interpolation.
|
/// Set the color space used for interpolation.
|
||||||
|
|||||||
@ -147,7 +147,7 @@ pub fn viewport_picking(
|
|||||||
};
|
};
|
||||||
viewport_pointer_location.location = Some(location.clone());
|
viewport_pointer_location.location = Some(location.clone());
|
||||||
|
|
||||||
commands.send_event(PointerInput {
|
commands.write_event(PointerInput {
|
||||||
location,
|
location,
|
||||||
pointer_id: viewport_pointer_id,
|
pointer_id: viewport_pointer_id,
|
||||||
action: input.action,
|
action: input.action,
|
||||||
|
|||||||
@ -23,7 +23,6 @@ bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
|||||||
bevy_sprite = { path = "../bevy_sprite", version = "0.17.0-dev" }
|
bevy_sprite = { path = "../bevy_sprite", version = "0.17.0-dev" }
|
||||||
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev", optional = true }
|
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev", optional = true }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
@ -32,23 +31,13 @@ bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev" }
|
|||||||
bevy_text = { path = "../bevy_text", version = "0.17.0-dev", default-features = false }
|
bevy_text = { path = "../bevy_text", version = "0.17.0-dev", default-features = false }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
|
||||||
bytemuck = { version = "1.5", features = ["derive"] }
|
bytemuck = { version = "1.5", features = ["derive"] }
|
||||||
thiserror = { version = "2", default-features = false }
|
|
||||||
derive_more = { version = "1", default-features = false, features = ["from"] }
|
derive_more = { version = "1", default-features = false, features = ["from"] }
|
||||||
nonmax = "0.5"
|
|
||||||
smallvec = { version = "1", default-features = false }
|
|
||||||
accesskit = "0.18"
|
|
||||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
serialize = [
|
serialize = ["bevy_math/serialize", "bevy_platform/serialize"]
|
||||||
"serde",
|
|
||||||
"smallvec/serde",
|
|
||||||
"bevy_math/serialize",
|
|
||||||
"bevy_platform/serialize",
|
|
||||||
]
|
|
||||||
bevy_ui_picking_backend = ["bevy_picking"]
|
bevy_ui_picking_backend = ["bevy_picking"]
|
||||||
bevy_ui_debug = []
|
bevy_ui_debug = []
|
||||||
|
|
||||||
|
|||||||
@ -186,6 +186,10 @@ impl SpecializedRenderPipeline for GradientPipeline {
|
|||||||
InterpolationColorSpace::OkLchLong => "IN_OKLCH_LONG",
|
InterpolationColorSpace::OkLchLong => "IN_OKLCH_LONG",
|
||||||
InterpolationColorSpace::Srgb => "IN_SRGB",
|
InterpolationColorSpace::Srgb => "IN_SRGB",
|
||||||
InterpolationColorSpace::LinearRgb => "IN_LINEAR_RGB",
|
InterpolationColorSpace::LinearRgb => "IN_LINEAR_RGB",
|
||||||
|
InterpolationColorSpace::Hsl => "IN_HSL",
|
||||||
|
InterpolationColorSpace::HslLong => "IN_HSL_LONG",
|
||||||
|
InterpolationColorSpace::Hsv => "IN_HSV",
|
||||||
|
InterpolationColorSpace::HsvLong => "IN_HSV_LONG",
|
||||||
};
|
};
|
||||||
|
|
||||||
let shader_defs = if key.anti_alias {
|
let shader_defs = if key.anti_alias {
|
||||||
|
|||||||
@ -114,15 +114,15 @@ fn fragment(in: GradientVertexOutput) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function converts two linear rgb colors to srgb space, mixes them, and then converts the result back to linear rgb space.
|
// This function converts two linear rgba colors to srgba space, mixes them, and then converts the result back to linear rgb space.
|
||||||
fn mix_linear_rgb_in_srgb_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_linear_rgba_in_srgba_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
let a_srgb = pow(a.rgb, vec3(1. / 2.2));
|
let a_srgb = pow(a.rgb, vec3(1. / 2.2));
|
||||||
let b_srgb = pow(b.rgb, vec3(1. / 2.2));
|
let b_srgb = pow(b.rgb, vec3(1. / 2.2));
|
||||||
let mixed_srgb = mix(a_srgb, b_srgb, t);
|
let mixed_srgb = mix(a_srgb, b_srgb, t);
|
||||||
return vec4(pow(mixed_srgb, vec3(2.2)), mix(a.a, b.a, t));
|
return vec4(pow(mixed_srgb, vec3(2.2)), mix(a.a, b.a, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linear_rgb_to_oklab(c: vec4<f32>) -> vec4<f32> {
|
fn linear_rgba_to_oklaba(c: vec4<f32>) -> vec4<f32> {
|
||||||
let l = pow(0.41222146 * c.x + 0.53633255 * c.y + 0.051445995 * c.z, 1. / 3.);
|
let l = pow(0.41222146 * c.x + 0.53633255 * c.y + 0.051445995 * c.z, 1. / 3.);
|
||||||
let m = pow(0.2119035 * c.x + 0.6806995 * c.y + 0.10739696 * c.z, 1. / 3.);
|
let m = pow(0.2119035 * c.x + 0.6806995 * c.y + 0.10739696 * c.z, 1. / 3.);
|
||||||
let s = pow(0.08830246 * c.x + 0.28171885 * c.y + 0.6299787 * c.z, 1. / 3.);
|
let s = pow(0.08830246 * c.x + 0.28171885 * c.y + 0.6299787 * c.z, 1. / 3.);
|
||||||
@ -130,11 +130,11 @@ fn linear_rgb_to_oklab(c: vec4<f32>) -> vec4<f32> {
|
|||||||
0.21045426 * l + 0.7936178 * m - 0.004072047 * s,
|
0.21045426 * l + 0.7936178 * m - 0.004072047 * s,
|
||||||
1.9779985 * l - 2.4285922 * m + 0.4505937 * s,
|
1.9779985 * l - 2.4285922 * m + 0.4505937 * s,
|
||||||
0.025904037 * l + 0.78277177 * m - 0.80867577 * s,
|
0.025904037 * l + 0.78277177 * m - 0.80867577 * s,
|
||||||
c.w
|
c.a
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oklab_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
fn oklaba_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
||||||
let l_ = c.x + 0.39633778 * c.y + 0.21580376 * c.z;
|
let l_ = c.x + 0.39633778 * c.y + 0.21580376 * c.z;
|
||||||
let m_ = c.x - 0.105561346 * c.y - 0.06385417 * c.z;
|
let m_ = c.x - 0.105561346 * c.y - 0.06385417 * c.z;
|
||||||
let s_ = c.x - 0.08948418 * c.y - 1.2914855 * c.z;
|
let s_ = c.x - 0.08948418 * c.y - 1.2914855 * c.z;
|
||||||
@ -145,26 +145,126 @@ fn oklab_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
|||||||
4.0767417 * l - 3.3077116 * m + 0.23096994 * s,
|
4.0767417 * l - 3.3077116 * m + 0.23096994 * s,
|
||||||
-1.268438 * l + 2.6097574 * m - 0.34131938 * s,
|
-1.268438 * l + 2.6097574 * m - 0.34131938 * s,
|
||||||
-0.0041960863 * l - 0.7034186 * m + 1.7076147 * s,
|
-0.0041960863 * l - 0.7034186 * m + 1.7076147 * s,
|
||||||
c.w
|
c.a
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_linear_rgb_in_oklab_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_linear_rgba_in_oklaba_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
return oklab_to_linear_rgba(mix(linear_rgb_to_oklab(a), linear_rgb_to_oklab(b), t));
|
return oklaba_to_linear_rgba(mix(linear_rgba_to_oklaba(a), linear_rgba_to_oklaba(b), t));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linear_rgba_to_hsla(c: vec4<f32>) -> vec4<f32> {
|
||||||
|
let max = max(max(c.r, c.g), c.b);
|
||||||
|
let min = min(min(c.r, c.g), c.b);
|
||||||
|
let l = (max + min) * 0.5;
|
||||||
|
if max == min {
|
||||||
|
return vec4(0., 0., l, c.a);
|
||||||
|
} else {
|
||||||
|
let delta = max - min;
|
||||||
|
let s = delta / (1. - abs(2. * l - 1.));
|
||||||
|
var h = 0.;
|
||||||
|
if max == c.r {
|
||||||
|
h = ((c.g - c.b) / delta) % 6.;
|
||||||
|
} else if max == c.g {
|
||||||
|
h = ((c.b - c.r) / delta) + 2.;
|
||||||
|
} else {
|
||||||
|
h = ((c.r - c.g) / delta) + 4.;
|
||||||
|
}
|
||||||
|
h = h / 6.;
|
||||||
|
return vec4<f32>(h, s, l, c.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
||||||
|
let h = hsl.x;
|
||||||
|
let s = hsl.y;
|
||||||
|
let l = hsl.z;
|
||||||
|
let c = (1.0 - abs(2.0 * l - 1.0)) * s;
|
||||||
|
let hp = h * 6.0;
|
||||||
|
let x = c * (1.0 - abs(hp % 2.0 - 1.0));
|
||||||
|
var r: f32 = 0.0;
|
||||||
|
var g: f32 = 0.0;
|
||||||
|
var b: f32 = 0.0;
|
||||||
|
if 0.0 <= hp && hp < 1.0 {
|
||||||
|
r = c; g = x; b = 0.0;
|
||||||
|
} else if 1.0 <= hp && hp < 2.0 {
|
||||||
|
r = x; g = c; b = 0.0;
|
||||||
|
} else if 2.0 <= hp && hp < 3.0 {
|
||||||
|
r = 0.0; g = c; b = x;
|
||||||
|
} else if 3.0 <= hp && hp < 4.0 {
|
||||||
|
r = 0.0; g = x; b = c;
|
||||||
|
} else if 4.0 <= hp && hp < 5.0 {
|
||||||
|
r = x; g = 0.0; b = c;
|
||||||
|
} else if 5.0 <= hp && hp < 6.0 {
|
||||||
|
r = c; g = 0.0; b = x;
|
||||||
|
}
|
||||||
|
let m = l - 0.5 * c;
|
||||||
|
return vec4<f32>(r + m, g + m, b + m, hsl.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linear_rgba_to_hsva(c: vec4<f32>) -> vec4<f32> {
|
||||||
|
let maxc = max(max(c.r, c.g), c.b);
|
||||||
|
let minc = min(min(c.r, c.g), c.b);
|
||||||
|
let delta = maxc - minc;
|
||||||
|
var h: f32 = 0.0;
|
||||||
|
var s: f32 = 0.0;
|
||||||
|
let v: f32 = maxc;
|
||||||
|
if delta != 0.0 {
|
||||||
|
s = delta / maxc;
|
||||||
|
if maxc == c.r {
|
||||||
|
h = ((c.g - c.b) / delta) % 6.0;
|
||||||
|
} else if maxc == c.g {
|
||||||
|
h = ((c.b - c.r) / delta) + 2.0;
|
||||||
|
} else {
|
||||||
|
h = ((c.r - c.g) / delta) + 4.0;
|
||||||
|
}
|
||||||
|
h = h / 6.0;
|
||||||
|
if h < 0.0 {
|
||||||
|
h = h + 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vec4<f32>(h, s, v, c.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hsva_to_linear_rgba(hsva: vec4<f32>) -> vec4<f32> {
|
||||||
|
let h = hsva.x * 6.0;
|
||||||
|
let s = hsva.y;
|
||||||
|
let v = hsva.z;
|
||||||
|
let c = v * s;
|
||||||
|
let x = c * (1.0 - abs(h % 2.0 - 1.0));
|
||||||
|
let m = v - c;
|
||||||
|
var r: f32 = 0.0;
|
||||||
|
var g: f32 = 0.0;
|
||||||
|
var b: f32 = 0.0;
|
||||||
|
if 0.0 <= h && h < 1.0 {
|
||||||
|
r = c; g = x; b = 0.0;
|
||||||
|
} else if 1.0 <= h && h < 2.0 {
|
||||||
|
r = x; g = c; b = 0.0;
|
||||||
|
} else if 2.0 <= h && h < 3.0 {
|
||||||
|
r = 0.0; g = c; b = x;
|
||||||
|
} else if 3.0 <= h && h < 4.0 {
|
||||||
|
r = 0.0; g = x; b = c;
|
||||||
|
} else if 4.0 <= h && h < 5.0 {
|
||||||
|
r = x; g = 0.0; b = c;
|
||||||
|
} else if 5.0 <= h && h < 6.0 {
|
||||||
|
r = c; g = 0.0; b = x;
|
||||||
|
}
|
||||||
|
return vec4<f32>(r + m, g + m, b + m, hsva.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// hue is left in radians and not converted to degrees
|
/// hue is left in radians and not converted to degrees
|
||||||
fn linear_rgb_to_oklch(c: vec4<f32>) -> vec4<f32> {
|
fn linear_rgba_to_oklcha(c: vec4<f32>) -> vec4<f32> {
|
||||||
let o = linear_rgb_to_oklab(c);
|
let o = linear_rgba_to_oklaba(c);
|
||||||
let chroma = sqrt(o.y * o.y + o.z * o.z);
|
let chroma = sqrt(o.y * o.y + o.z * o.z);
|
||||||
let hue = atan2(o.z, o.y);
|
let hue = atan2(o.z, o.y);
|
||||||
return vec4(o.x, chroma, select(hue + TAU, hue, hue < 0.0), o.w);
|
return vec4(o.x, chroma, rem_euclid(hue, TAU), o.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oklch_to_linear_rgb(c: vec4<f32>) -> vec4<f32> {
|
fn oklcha_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
||||||
let a = c.y * cos(c.z);
|
let a = c.y * cos(c.z);
|
||||||
let b = c.y * sin(c.z);
|
let b = c.y * sin(c.z);
|
||||||
return oklab_to_linear_rgba(vec4(c.x, a, b, c.w));
|
return oklaba_to_linear_rgba(vec4(c.x, a, b, c.a));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rem_euclid(a: f32, b: f32) -> f32 {
|
fn rem_euclid(a: f32, b: f32) -> f32 {
|
||||||
@ -178,31 +278,82 @@ fn lerp_hue(a: f32, b: f32, t: f32) -> f32 {
|
|||||||
|
|
||||||
fn lerp_hue_long(a: f32, b: f32, t: f32) -> f32 {
|
fn lerp_hue_long(a: f32, b: f32, t: f32) -> f32 {
|
||||||
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
||||||
return rem_euclid(a + select(diff - TAU, diff + TAU, 0. < diff) * t, TAU);
|
return rem_euclid(a + (diff + select(TAU, -TAU, 0. < diff)) * t, TAU);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_oklch(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_oklcha(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ah = select(a.z, b.z, a.y == 0.);
|
||||||
|
let bh = select(b.z, a.z, b.y == 0.);
|
||||||
return vec4(
|
return vec4(
|
||||||
mix(a.xy, b.xy, t),
|
mix(a.xy, b.xy, t),
|
||||||
lerp_hue(a.z, b.z, t),
|
lerp_hue(ah, bh, t),
|
||||||
|
mix(a.a, b.a, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_oklcha_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ah = select(a.z, b.z, a.y == 0.);
|
||||||
|
let bh = select(b.z, a.z, b.y == 0.);
|
||||||
|
return vec4(
|
||||||
|
mix(a.xy, b.xy, t),
|
||||||
|
lerp_hue_long(ah, bh, t),
|
||||||
mix(a.w, b.w, t)
|
mix(a.w, b.w, t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_oklch_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_linear_rgba_in_oklcha_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
return vec4(
|
return oklcha_to_linear_rgba(mix_oklcha(linear_rgba_to_oklcha(a), linear_rgba_to_oklcha(b), t));
|
||||||
mix(a.xy, b.xy, t),
|
|
||||||
lerp_hue_long(a.z, b.z, t),
|
|
||||||
mix(a.w, b.w, t)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_linear_rgb_in_oklch_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_linear_rgba_in_oklcha_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
return oklch_to_linear_rgb(mix_oklch(linear_rgb_to_oklch(a), linear_rgb_to_oklch(b), t));
|
return oklcha_to_linear_rgba(mix_oklcha_long(linear_rgba_to_oklcha(a), linear_rgba_to_oklcha(b), t));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_linear_rgb_in_oklch_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_linear_rgba_in_hsva_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
return oklch_to_linear_rgb(mix_oklch_long(linear_rgb_to_oklch(a), linear_rgb_to_oklch(b), t));
|
let ha = linear_rgba_to_hsva(a);
|
||||||
|
let hb = linear_rgba_to_hsva(b);
|
||||||
|
var h: f32;
|
||||||
|
if ha.y == 0. {
|
||||||
|
h = hb.x;
|
||||||
|
} else if hb.y == 0. {
|
||||||
|
h = ha.x;
|
||||||
|
} else {
|
||||||
|
h = lerp_hue(ha.x * TAU, hb.x * TAU, t) / TAU;
|
||||||
|
}
|
||||||
|
let s = mix(ha.y, hb.y, t);
|
||||||
|
let v = mix(ha.z, hb.z, t);
|
||||||
|
let a_alpha = mix(ha.a, hb.a, t);
|
||||||
|
return hsva_to_linear_rgba(vec4<f32>(h, s, v, a_alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_linear_rgba_in_hsva_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ha = linear_rgba_to_hsva(a);
|
||||||
|
let hb = linear_rgba_to_hsva(b);
|
||||||
|
let h = lerp_hue_long(ha.x * TAU, hb.x * TAU, t) / TAU;
|
||||||
|
let s = mix(ha.y, hb.y, t);
|
||||||
|
let v = mix(ha.z, hb.z, t);
|
||||||
|
let a_alpha = mix(ha.a, hb.a, t);
|
||||||
|
return hsva_to_linear_rgba(vec4<f32>(h, s, v, a_alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_linear_rgba_in_hsla_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ha = linear_rgba_to_hsla(a);
|
||||||
|
let hb = linear_rgba_to_hsla(b);
|
||||||
|
let h = lerp_hue(ha.x * TAU, hb.x * TAU, t) / TAU;
|
||||||
|
let s = mix(ha.y, hb.y, t);
|
||||||
|
let l = mix(ha.z, hb.z, t);
|
||||||
|
let a_alpha = mix(ha.a, hb.a, t);
|
||||||
|
return hsla_to_linear_rgba(vec4<f32>(h, s, l, a_alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_linear_rgba_in_hsla_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ha = linear_rgba_to_hsla(a);
|
||||||
|
let hb = linear_rgba_to_hsla(b);
|
||||||
|
let h = lerp_hue_long(ha.x * TAU, hb.x * TAU, t) / TAU;
|
||||||
|
let s = mix(ha.y, hb.y, t);
|
||||||
|
let l = mix(ha.z, hb.z, t);
|
||||||
|
let a_alpha = mix(ha.a, hb.a, t);
|
||||||
|
return hsla_to_linear_rgba(vec4<f32>(h, s, l, a_alpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
// These functions are used to calculate the distance in gradient space from the start of the gradient to the point.
|
// These functions are used to calculate the distance in gradient space from the start of the gradient to the point.
|
||||||
@ -277,13 +428,21 @@ fn interpolate_gradient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IN_SRGB
|
#ifdef IN_SRGB
|
||||||
return mix_linear_rgb_in_srgb_space(start_color, end_color, t);
|
return mix_linear_rgba_in_srgba_space(start_color, end_color, t);
|
||||||
#else ifdef IN_OKLAB
|
#else ifdef IN_OKLAB
|
||||||
return mix_linear_rgb_in_oklab_space(start_color, end_color, t);
|
return mix_linear_rgba_in_oklaba_space(start_color, end_color, t);
|
||||||
#else ifdef IN_OKLCH
|
#else ifdef IN_OKLCH
|
||||||
return mix_linear_rgb_in_oklch_space(start_color, end_color, t);
|
return mix_linear_rgba_in_oklcha_space(start_color, end_color, t);
|
||||||
#else ifdef IN_OKLCH_LONG
|
#else ifdef IN_OKLCH_LONG
|
||||||
return mix_linear_rgb_in_oklch_space_long(start_color, end_color, t);
|
return mix_linear_rgba_in_oklcha_space_long(start_color, end_color, t);
|
||||||
|
#else ifdef IN_HSV
|
||||||
|
return mix_linear_rgba_in_hsva_space(start_color, end_color, t);
|
||||||
|
#else ifdef IN_HSV_LONG
|
||||||
|
return mix_linear_rgba_in_hsva_space_long(start_color, end_color, t);
|
||||||
|
#else ifdef IN_HSL
|
||||||
|
return mix_linear_rgba_in_hsla_space(start_color, end_color, t);
|
||||||
|
#else ifdef IN_HSL_LONG
|
||||||
|
return mix_linear_rgba_in_hsla_space_long(start_color, end_color, t);
|
||||||
#else
|
#else
|
||||||
return mix(start_color, end_color, t);
|
return mix(start_color, end_color, t);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -79,7 +79,7 @@ impl DebugName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`ShortName`] corresping to this debug name
|
/// Get the [`ShortName`] corresponding to this debug name
|
||||||
///
|
///
|
||||||
/// The value will be a static string if the `debug` feature is not enabled
|
/// The value will be a static string if the `debug` feature is not enabled
|
||||||
pub fn shortname(&self) -> ShortName {
|
pub fn shortname(&self) -> ShortName {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ use thread_local::ThreadLocal;
|
|||||||
/// A cohesive set of thread-local values of a given type.
|
/// A cohesive set of thread-local values of a given type.
|
||||||
///
|
///
|
||||||
/// Mutable references can be fetched if `T: Default` via [`Parallel::scope`].
|
/// Mutable references can be fetched if `T: Default` via [`Parallel::scope`].
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Parallel<T: Send> {
|
pub struct Parallel<T: Send> {
|
||||||
locals: ThreadLocal<RefCell<T>>,
|
locals: ThreadLocal<RefCell<T>>,
|
||||||
}
|
}
|
||||||
@ -20,6 +19,25 @@ impl<T: Send> Parallel<T> {
|
|||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.locals.clear();
|
self.locals.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the thread-local value for the current thread and runs `f` on it.
|
||||||
|
///
|
||||||
|
/// If there is no thread-local value, it will be initialized to the result
|
||||||
|
/// of `create`.
|
||||||
|
pub fn scope_or<R>(&self, create: impl FnOnce() -> T, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
|
f(&mut self.borrow_local_mut_or(create))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrows the thread-local value.
|
||||||
|
///
|
||||||
|
/// If there is no thread-local value, it will be initialized to the result
|
||||||
|
/// of `create`.
|
||||||
|
pub fn borrow_local_mut_or(
|
||||||
|
&self,
|
||||||
|
create: impl FnOnce() -> T,
|
||||||
|
) -> impl DerefMut<Target = T> + '_ {
|
||||||
|
self.locals.get_or(|| RefCell::new(create())).borrow_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Send> Parallel<T> {
|
impl<T: Default + Send> Parallel<T> {
|
||||||
@ -27,15 +45,14 @@ impl<T: Default + Send> Parallel<T> {
|
|||||||
///
|
///
|
||||||
/// If there is no thread-local value, it will be initialized to its default.
|
/// If there is no thread-local value, it will be initialized to its default.
|
||||||
pub fn scope<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
pub fn scope<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
let mut cell = self.locals.get_or_default().borrow_mut();
|
self.scope_or(Default::default, f)
|
||||||
f(cell.deref_mut())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutably borrows the thread-local value.
|
/// Mutably borrows the thread-local value.
|
||||||
///
|
///
|
||||||
/// If there is no thread-local value, it will be initialized to its default.
|
/// If there is no thread-local value, it will be initialized to its default.
|
||||||
pub fn borrow_local_mut(&self) -> impl DerefMut<Target = T> + '_ {
|
pub fn borrow_local_mut(&self) -> impl DerefMut<Target = T> + '_ {
|
||||||
self.locals.get_or_default().borrow_mut()
|
self.borrow_local_mut_or(Default::default)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,3 +89,12 @@ impl<T: Send> Parallel<Vec<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `Default` is manually implemented to avoid the `T: Default` bound.
|
||||||
|
impl<T: Send> Default for Parallel<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
locals: ThreadLocal::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -22,12 +22,7 @@ bevy_reflect = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
## Adds serialization support through `serde`.
|
## Adds serialization support through `serde`.
|
||||||
serialize = [
|
serialize = ["serde", "bevy_ecs/serialize", "bevy_input/serialize"]
|
||||||
"serde",
|
|
||||||
"smol_str/serde",
|
|
||||||
"bevy_ecs/serialize",
|
|
||||||
"bevy_input/serialize",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Platform Compatibility
|
# Platform Compatibility
|
||||||
|
|
||||||
@ -56,9 +51,7 @@ bevy_input = { path = "../bevy_input", version = "0.17.0-dev", default-features
|
|||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev", default-features = false }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev", default-features = false }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"glam",
|
"glam",
|
||||||
"smol_str",
|
|
||||||
], optional = true }
|
], optional = true }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
|
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false }
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false }
|
||||||
|
|
||||||
# other
|
# other
|
||||||
@ -69,7 +62,6 @@ serde = { version = "1.0", features = [
|
|||||||
raw-window-handle = { version = "0.6", features = [
|
raw-window-handle = { version = "0.6", features = [
|
||||||
"alloc",
|
"alloc",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
smol_str = { version = "0.2", default-features = false }
|
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
|
|||||||
@ -16,7 +16,6 @@ x11 = ["winit/x11"]
|
|||||||
accesskit_unix = ["accesskit_winit/accesskit_unix", "accesskit_winit/async-io"]
|
accesskit_unix = ["accesskit_winit/accesskit_unix", "accesskit_winit/async-io"]
|
||||||
|
|
||||||
serialize = [
|
serialize = [
|
||||||
"serde",
|
|
||||||
"bevy_input/serialize",
|
"bevy_input/serialize",
|
||||||
"bevy_window/serialize",
|
"bevy_window/serialize",
|
||||||
"bevy_platform/serialize",
|
"bevy_platform/serialize",
|
||||||
@ -38,7 +37,6 @@ bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
|
|||||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
||||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
|
||||||
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev" }
|
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev" }
|
||||||
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
@ -57,7 +55,6 @@ accesskit_winit = { version = "0.27", default-features = false, features = [
|
|||||||
approx = { version = "0.5", default-features = false }
|
approx = { version = "0.5", default-features = false }
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
|
||||||
bytemuck = { version = "1.5", optional = true }
|
bytemuck = { version = "1.5", optional = true }
|
||||||
wgpu-types = { version = "25", optional = true }
|
wgpu-types = { version = "25", optional = true }
|
||||||
accesskit = "0.19"
|
accesskit = "0.19"
|
||||||
|
|||||||
@ -240,7 +240,7 @@ impl<T: BufferedEvent> ApplicationHandler<T> for WinitAppRunnerState<T> {
|
|||||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: T) {
|
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: T) {
|
||||||
self.user_event_received = true;
|
self.user_event_received = true;
|
||||||
|
|
||||||
self.world_mut().send_event(event);
|
self.world_mut().write_event(event);
|
||||||
self.redraw_requested = true;
|
self.redraw_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -790,91 +790,91 @@ impl<T: BufferedEvent> WinitAppRunnerState<T> {
|
|||||||
if !raw_winit_events.is_empty() {
|
if !raw_winit_events.is_empty() {
|
||||||
world
|
world
|
||||||
.resource_mut::<Events<RawWinitWindowEvent>>()
|
.resource_mut::<Events<RawWinitWindowEvent>>()
|
||||||
.send_batch(raw_winit_events);
|
.write_batch(raw_winit_events);
|
||||||
}
|
}
|
||||||
|
|
||||||
for winit_event in buffered_events.iter() {
|
for winit_event in buffered_events.iter() {
|
||||||
match winit_event.clone() {
|
match winit_event.clone() {
|
||||||
BevyWindowEvent::AppLifecycle(e) => {
|
BevyWindowEvent::AppLifecycle(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::CursorEntered(e) => {
|
BevyWindowEvent::CursorEntered(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::CursorLeft(e) => {
|
BevyWindowEvent::CursorLeft(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::CursorMoved(e) => {
|
BevyWindowEvent::CursorMoved(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::FileDragAndDrop(e) => {
|
BevyWindowEvent::FileDragAndDrop(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::Ime(e) => {
|
BevyWindowEvent::Ime(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::RequestRedraw(e) => {
|
BevyWindowEvent::RequestRedraw(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowBackendScaleFactorChanged(e) => {
|
BevyWindowEvent::WindowBackendScaleFactorChanged(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowCloseRequested(e) => {
|
BevyWindowEvent::WindowCloseRequested(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowCreated(e) => {
|
BevyWindowEvent::WindowCreated(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowDestroyed(e) => {
|
BevyWindowEvent::WindowDestroyed(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowFocused(e) => {
|
BevyWindowEvent::WindowFocused(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowMoved(e) => {
|
BevyWindowEvent::WindowMoved(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowOccluded(e) => {
|
BevyWindowEvent::WindowOccluded(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowResized(e) => {
|
BevyWindowEvent::WindowResized(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowScaleFactorChanged(e) => {
|
BevyWindowEvent::WindowScaleFactorChanged(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::WindowThemeChanged(e) => {
|
BevyWindowEvent::WindowThemeChanged(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::MouseButtonInput(e) => {
|
BevyWindowEvent::MouseButtonInput(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::MouseMotion(e) => {
|
BevyWindowEvent::MouseMotion(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::MouseWheel(e) => {
|
BevyWindowEvent::MouseWheel(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::PinchGesture(e) => {
|
BevyWindowEvent::PinchGesture(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::RotationGesture(e) => {
|
BevyWindowEvent::RotationGesture(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::DoubleTapGesture(e) => {
|
BevyWindowEvent::DoubleTapGesture(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::PanGesture(e) => {
|
BevyWindowEvent::PanGesture(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::TouchInput(e) => {
|
BevyWindowEvent::TouchInput(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::KeyboardInput(e) => {
|
BevyWindowEvent::KeyboardInput(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
BevyWindowEvent::KeyboardFocusLost(e) => {
|
BevyWindowEvent::KeyboardFocusLost(e) => {
|
||||||
world.send_event(e);
|
world.write_event(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -882,7 +882,7 @@ impl<T: BufferedEvent> WinitAppRunnerState<T> {
|
|||||||
if !buffered_events.is_empty() {
|
if !buffered_events.is_empty() {
|
||||||
world
|
world
|
||||||
.resource_mut::<Events<BevyWindowEvent>>()
|
.resource_mut::<Events<BevyWindowEvent>>()
|
||||||
.send_batch(buffered_events);
|
.write_batch(buffered_events);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
//! [`Material2d`]: bevy::sprite::Material2d
|
//! [`Material2d`]: bevy::sprite::Material2d
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
asset::uuid_handle,
|
|
||||||
color::palettes::basic::YELLOW,
|
color::palettes::basic::YELLOW,
|
||||||
core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT},
|
core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT},
|
||||||
math::{ops, FloatOrd},
|
math::{ops, FloatOrd},
|
||||||
@ -129,12 +128,16 @@ pub struct ColoredMesh2d;
|
|||||||
pub struct ColoredMesh2dPipeline {
|
pub struct ColoredMesh2dPipeline {
|
||||||
/// This pipeline wraps the standard [`Mesh2dPipeline`]
|
/// This pipeline wraps the standard [`Mesh2dPipeline`]
|
||||||
mesh2d_pipeline: Mesh2dPipeline,
|
mesh2d_pipeline: Mesh2dPipeline,
|
||||||
|
/// The shader asset handle.
|
||||||
|
shader: Handle<Shader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWorld for ColoredMesh2dPipeline {
|
impl FromWorld for ColoredMesh2dPipeline {
|
||||||
fn from_world(world: &mut World) -> Self {
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mesh2d_pipeline: Mesh2dPipeline::from_world(world),
|
mesh2d_pipeline: Mesh2dPipeline::from_world(world),
|
||||||
|
// Get the shader from the shader resource we inserted in the plugin.
|
||||||
|
shader: world.resource::<ColoredMesh2dShader>().0.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,14 +167,14 @@ impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
|
|||||||
RenderPipelineDescriptor {
|
RenderPipelineDescriptor {
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
// Use our custom shader
|
// Use our custom shader
|
||||||
shader: COLORED_MESH2D_SHADER_HANDLE,
|
shader: self.shader.clone(),
|
||||||
// Use our custom vertex buffer
|
// Use our custom vertex buffer
|
||||||
buffers: vec![vertex_layout],
|
buffers: vec![vertex_layout],
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
fragment: Some(FragmentState {
|
fragment: Some(FragmentState {
|
||||||
// Use our custom shader
|
// Use our custom shader
|
||||||
shader: COLORED_MESH2D_SHADER_HANDLE,
|
shader: self.shader.clone(),
|
||||||
targets: vec![Some(ColorTargetState {
|
targets: vec![Some(ColorTargetState {
|
||||||
format,
|
format,
|
||||||
blend: Some(BlendState::ALPHA_BLENDING),
|
blend: Some(BlendState::ALPHA_BLENDING),
|
||||||
@ -278,9 +281,10 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
/// Plugin that renders [`ColoredMesh2d`]s
|
/// Plugin that renders [`ColoredMesh2d`]s
|
||||||
pub struct ColoredMesh2dPlugin;
|
pub struct ColoredMesh2dPlugin;
|
||||||
|
|
||||||
/// Handle to the custom shader with a unique random ID
|
/// A resource holding the shader asset handle for the pipeline to take. There are many ways to get
|
||||||
pub const COLORED_MESH2D_SHADER_HANDLE: Handle<Shader> =
|
/// the shader into the pipeline - this is just one option.
|
||||||
uuid_handle!("f48b148f-7373-4638-9900-392b3b3ccc66");
|
#[derive(Resource)]
|
||||||
|
struct ColoredMesh2dShader(Handle<Shader>);
|
||||||
|
|
||||||
/// Our custom pipeline needs its own instance storage
|
/// Our custom pipeline needs its own instance storage
|
||||||
#[derive(Resource, Deref, DerefMut, Default)]
|
#[derive(Resource, Deref, DerefMut, Default)]
|
||||||
@ -290,15 +294,16 @@ impl Plugin for ColoredMesh2dPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
// Load our custom shader
|
// Load our custom shader
|
||||||
let mut shaders = app.world_mut().resource_mut::<Assets<Shader>>();
|
let mut shaders = app.world_mut().resource_mut::<Assets<Shader>>();
|
||||||
shaders.insert(
|
// Here, we construct and add the shader asset manually. There are many ways to load this
|
||||||
&COLORED_MESH2D_SHADER_HANDLE,
|
// shader, including `embedded_asset`/`load_embedded_asset`.
|
||||||
Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()),
|
let shader = shaders.add(Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()));
|
||||||
);
|
|
||||||
app.add_plugins(SyncComponentPlugin::<ColoredMesh2d>::default());
|
app.add_plugins(SyncComponentPlugin::<ColoredMesh2d>::default());
|
||||||
|
|
||||||
// Register our custom draw function, and add our render systems
|
// Register our custom draw function, and add our render systems
|
||||||
app.get_sub_app_mut(RenderApp)
|
app.get_sub_app_mut(RenderApp)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.insert_resource(ColoredMesh2dShader(shader))
|
||||||
.add_render_command::<Transparent2d, DrawColoredMesh2d>()
|
.add_render_command::<Transparent2d, DrawColoredMesh2d>()
|
||||||
.init_resource::<SpecializedRenderPipelines<ColoredMesh2dPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<ColoredMesh2dPipeline>>()
|
||||||
.init_resource::<RenderColoredMesh2dInstances>()
|
.init_resource::<RenderColoredMesh2dInstances>()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user