RenderCommand for SetMatUiViewBindGroup {
+ type Param = SRes;
+ type ViewWorldQuery = Read;
+ type ItemWorldQuery = ();
+
+ fn render<'w>(
+ _item: &P,
+ view_uniform: &'w ViewUniformOffset,
+ _entity: (),
+ ui_meta: SystemParamItem<'w, '_, Self::Param>,
+ pass: &mut TrackedRenderPass<'w>,
+ ) -> RenderCommandResult {
+ pass.set_bind_group(
+ I,
+ ui_meta.into_inner().view_bind_group.as_ref().unwrap(),
+ &[view_uniform.offset],
+ );
+ RenderCommandResult::Success
+ }
+}
+
+pub struct SetUiMaterialBindGroup(PhantomData);
+impl RenderCommand
+ for SetUiMaterialBindGroup
+{
+ type Param = SRes>;
+ type ViewWorldQuery = ();
+ type ItemWorldQuery = Read>;
+
+ fn render<'w>(
+ _item: &P,
+ _view: (),
+ material_handle: ROQueryItem<'_, Self::ItemWorldQuery>,
+ materials: SystemParamItem<'w, '_, Self::Param>,
+ pass: &mut TrackedRenderPass<'w>,
+ ) -> RenderCommandResult {
+ let material = materials
+ .into_inner()
+ .get(&material_handle.material)
+ .unwrap();
+ pass.set_bind_group(I, &material.bind_group, &[]);
+ RenderCommandResult::Success
+ }
+}
+
+pub struct DrawUiMaterialNode(PhantomData);
+impl RenderCommand for DrawUiMaterialNode {
+ type Param = SRes;
+ type ViewWorldQuery = ();
+ type ItemWorldQuery = Read>;
+
+ #[inline]
+ fn render<'w>(
+ _item: &P,
+ _view: (),
+ batch: &'w UiMaterialBatch,
+ ui_meta: SystemParamItem<'w, '_, Self::Param>,
+ pass: &mut TrackedRenderPass<'w>,
+ ) -> RenderCommandResult {
+ pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..));
+ pass.draw(batch.range.clone(), 0..1);
+ RenderCommandResult::Success
+ }
+}
+
+pub struct ExtractedUiMaterialNode {
+ pub stack_index: usize,
+ pub transform: Mat4,
+ pub rect: Rect,
+ pub border: [f32; 4],
+ pub material: AssetId,
+ pub clip: Option,
+}
+
+#[derive(Resource)]
+pub struct ExtractedUiMaterialNodes {
+ pub uinodes: SparseSet>,
+}
+
+impl Default for ExtractedUiMaterialNodes {
+ fn default() -> Self {
+ Self {
+ uinodes: Default::default(),
+ }
+ }
+}
+
+pub fn extract_ui_material_nodes(
+ mut extracted_uinodes: ResMut>,
+ materials: Extract>>,
+ ui_stack: Extract>,
+ uinode_query: Extract<
+ Query<(
+ Entity,
+ &Node,
+ &Style,
+ &GlobalTransform,
+ &Handle,
+ &ViewVisibility,
+ Option<&CalculatedClip>,
+ )>,
+ >,
+ windows: Extract>>,
+ ui_scale: Extract>,
+) {
+ let ui_logical_viewport_size = windows
+ .get_single()
+ .map(|window| Vec2::new(window.resolution.width(), window.resolution.height()))
+ .unwrap_or(Vec2::ZERO)
+ // The logical window resolution returned by `Window` only takes into account the window scale factor and not `UiScale`,
+ // so we have to divide by `UiScale` to get the size of the UI viewport.
+ / ui_scale.0 as f32;
+ for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() {
+ if let Ok((entity, uinode, style, transform, handle, view_visibility, clip)) =
+ uinode_query.get(*entity)
+ {
+ // skip invisible nodes
+ if !view_visibility.get() {
+ continue;
+ }
+
+ // Skip loading materials
+ if !materials.contains(handle) {
+ continue;
+ }
+
+ // Both vertical and horizontal percentage border values are calculated based on the width of the parent node
+ //
+ let parent_width = uinode.size().x;
+ let left =
+ resolve_border_thickness(style.border.left, parent_width, ui_logical_viewport_size)
+ / uinode.size().x;
+ let right = resolve_border_thickness(
+ style.border.right,
+ parent_width,
+ ui_logical_viewport_size,
+ ) / uinode.size().y;
+ let top =
+ resolve_border_thickness(style.border.top, parent_width, ui_logical_viewport_size)
+ / uinode.size().y;
+ let bottom = resolve_border_thickness(
+ style.border.bottom,
+ parent_width,
+ ui_logical_viewport_size,
+ ) / uinode.size().y;
+
+ extracted_uinodes.uinodes.insert(
+ entity,
+ ExtractedUiMaterialNode {
+ stack_index,
+ transform: transform.compute_matrix(),
+ material: handle.id(),
+ rect: Rect {
+ min: Vec2::ZERO,
+ max: uinode.calculated_size,
+ },
+ border: [left, right, top, bottom],
+ clip: clip.map(|clip| clip.clip),
+ },
+ );
+ };
+ }
+}
+
+#[allow(clippy::too_many_arguments)]
+pub fn prepare_uimaterial_nodes(
+ mut commands: Commands,
+ render_device: Res,
+ render_queue: Res,
+ mut ui_meta: ResMut,
+ mut extracted_uinodes: ResMut>,
+ view_uniforms: Res,
+ ui_material_pipeline: Res>,
+ mut phases: Query<&mut RenderPhase>,
+ mut previous_len: Local,
+) {
+ if let Some(view_binding) = view_uniforms.uniforms.binding() {
+ let mut batches: Vec<(Entity, UiMaterialBatch)> = Vec::with_capacity(*previous_len);
+
+ ui_meta.vertices.clear();
+ ui_meta.view_bind_group = Some(render_device.create_bind_group(
+ "ui_material_view_bind_group",
+ &ui_material_pipeline.view_layout,
+ &BindGroupEntries::single(view_binding),
+ ));
+ let mut index = 0;
+
+ for mut ui_phase in &mut phases {
+ let mut batch_item_index = 0;
+ let mut batch_shader_handle = AssetId::invalid();
+
+ for item_index in 0..ui_phase.items.len() {
+ let item = &mut ui_phase.items[item_index];
+ if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(item.entity) {
+ let mut existing_batch = batches
+ .last_mut()
+ .filter(|_| batch_shader_handle == extracted_uinode.material);
+
+ if existing_batch.is_none() {
+ batch_item_index = item_index;
+ batch_shader_handle = extracted_uinode.material;
+
+ let new_batch = UiMaterialBatch {
+ range: index..index,
+ material: extracted_uinode.material,
+ };
+
+ batches.push((item.entity, new_batch));
+
+ existing_batch = batches.last_mut();
+ }
+
+ let uinode_rect = extracted_uinode.rect;
+
+ let rect_size = uinode_rect.size().extend(1.0);
+
+ let positions = QUAD_VERTEX_POSITIONS.map(|pos| {
+ (extracted_uinode.transform * (pos * rect_size).extend(1.0)).xyz()
+ });
+
+ let positions_diff = if let Some(clip) = extracted_uinode.clip {
+ [
+ Vec2::new(
+ f32::max(clip.min.x - positions[0].x, 0.),
+ f32::max(clip.min.y - positions[0].y, 0.),
+ ),
+ Vec2::new(
+ f32::min(clip.max.x - positions[1].x, 0.),
+ f32::max(clip.min.y - positions[1].y, 0.),
+ ),
+ Vec2::new(
+ f32::min(clip.max.x - positions[2].x, 0.),
+ f32::min(clip.max.y - positions[2].y, 0.),
+ ),
+ Vec2::new(
+ f32::max(clip.min.x - positions[3].x, 0.),
+ f32::min(clip.max.y - positions[3].y, 0.),
+ ),
+ ]
+ } else {
+ [Vec2::ZERO; 4]
+ };
+
+ let positions_clipped = [
+ positions[0] + positions_diff[0].extend(0.),
+ positions[1] + positions_diff[1].extend(0.),
+ positions[2] + positions_diff[2].extend(0.),
+ positions[3] + positions_diff[3].extend(0.),
+ ];
+
+ let transformed_rect_size =
+ extracted_uinode.transform.transform_vector3(rect_size);
+
+ // Don't try to cull nodes that have a rotation
+ // In a rotation around the Z-axis, this value is 0.0 for an angle of 0.0 or π
+ // In those two cases, the culling check can proceed normally as corners will be on
+ // horizontal / vertical lines
+ // For all other angles, bypass the culling check
+ // This does not properly handles all rotations on all axis
+ if extracted_uinode.transform.x_axis[1] == 0.0 {
+ // Cull nodes that are completely clipped
+ if positions_diff[0].x - positions_diff[1].x >= transformed_rect_size.x
+ || positions_diff[1].y - positions_diff[2].y >= transformed_rect_size.y
+ {
+ continue;
+ }
+ }
+ let uvs = [
+ Vec2::new(
+ uinode_rect.min.x + positions_diff[0].x,
+ uinode_rect.min.y + positions_diff[0].y,
+ ),
+ Vec2::new(
+ uinode_rect.max.x + positions_diff[1].x,
+ uinode_rect.min.y + positions_diff[1].y,
+ ),
+ Vec2::new(
+ uinode_rect.max.x + positions_diff[2].x,
+ uinode_rect.max.y + positions_diff[2].y,
+ ),
+ Vec2::new(
+ uinode_rect.min.x + positions_diff[3].x,
+ uinode_rect.max.y + positions_diff[3].y,
+ ),
+ ]
+ .map(|pos| pos / uinode_rect.max);
+
+ for i in QUAD_INDICES {
+ ui_meta.vertices.push(UiMaterialVertex {
+ position: positions_clipped[i].into(),
+ uv: uvs[i].into(),
+ border_widths: extracted_uinode.border,
+ });
+ }
+
+ index += QUAD_INDICES.len() as u32;
+ existing_batch.unwrap().1.range.end = index;
+ ui_phase.items[batch_item_index].batch_range_mut().end += 1;
+ } else {
+ batch_shader_handle = AssetId::invalid();
+ }
+ }
+ }
+ ui_meta.vertices.write_buffer(&render_device, &render_queue);
+ *previous_len = batches.len();
+ commands.insert_or_spawn_batch(batches);
+ }
+ extracted_uinodes.uinodes.clear();
+}
+
+#[derive(Resource, Deref, DerefMut)]
+pub struct RenderUiMaterials(HashMap, PreparedUiMaterial>);
+
+impl Default for RenderUiMaterials {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+pub struct PreparedUiMaterial {
+ pub bindings: Vec<(u32, OwnedBindingResource)>,
+ pub bind_group: BindGroup,
+ pub key: T::Data,
+}
+
+#[derive(Resource)]
+pub struct ExtractedUiMaterials {
+ extracted: Vec<(AssetId, M)>,
+ removed: Vec>,
+}
+
+impl Default for ExtractedUiMaterials {
+ fn default() -> Self {
+ Self {
+ extracted: Default::default(),
+ removed: Default::default(),
+ }
+ }
+}
+
+pub fn extract_ui_materials(
+ mut commands: Commands,
+ mut events: Extract>>,
+ assets: Extract>>,
+) {
+ let mut changed_assets = HashSet::default();
+ let mut removed = Vec::new();
+ for event in events.read() {
+ match event {
+ AssetEvent::Added { id } | AssetEvent::Modified { id } => {
+ changed_assets.insert(*id);
+ }
+ AssetEvent::Removed { id } => {
+ changed_assets.remove(id);
+ removed.push(*id);
+ }
+ AssetEvent::LoadedWithDependencies { .. } => {
+ // not implemented
+ }
+ }
+ }
+
+ let mut extracted_assets = Vec::new();
+ for id in changed_assets.drain() {
+ if let Some(asset) = assets.get(id) {
+ extracted_assets.push((id, asset.clone()));
+ }
+ }
+
+ commands.insert_resource(ExtractedUiMaterials {
+ extracted: extracted_assets,
+ removed,
+ });
+}
+
+pub struct PrepareNextFrameMaterials {
+ assets: Vec<(AssetId, M)>,
+}
+
+impl Default for PrepareNextFrameMaterials {
+ fn default() -> Self {
+ Self {
+ assets: Default::default(),
+ }
+ }
+}
+
+pub fn prepare_ui_materials(
+ mut prepare_next_frame: Local>,
+ mut extracted_assets: ResMut>,
+ mut render_materials: ResMut>,
+ render_device: Res,
+ images: Res>,
+ fallback_image: Res,
+ pipeline: Res>,
+) {
+ let queued_assets = std::mem::take(&mut prepare_next_frame.assets);
+ for (id, material) in queued_assets {
+ match prepare_ui_material(
+ &material,
+ &render_device,
+ &images,
+ &fallback_image,
+ &pipeline,
+ ) {
+ Ok(prepared_asset) => {
+ render_materials.insert(id, prepared_asset);
+ }
+ Err(AsBindGroupError::RetryNextUpdate) => {
+ prepare_next_frame.assets.push((id, material));
+ }
+ }
+ }
+
+ for removed in std::mem::take(&mut extracted_assets.removed) {
+ render_materials.remove(&removed);
+ }
+
+ for (handle, material) in std::mem::take(&mut extracted_assets.extracted) {
+ match prepare_ui_material(
+ &material,
+ &render_device,
+ &images,
+ &fallback_image,
+ &pipeline,
+ ) {
+ Ok(prepared_asset) => {
+ render_materials.insert(handle, prepared_asset);
+ }
+ Err(AsBindGroupError::RetryNextUpdate) => {
+ prepare_next_frame.assets.push((handle, material));
+ }
+ }
+ }
+}
+
+fn prepare_ui_material(
+ material: &M,
+ render_device: &RenderDevice,
+ images: &RenderAssets,
+ fallback_image: &Res,
+ pipeline: &UiMaterialPipeline,
+) -> Result, AsBindGroupError> {
+ let prepared =
+ material.as_bind_group(&pipeline.ui_layout, render_device, images, fallback_image)?;
+ Ok(PreparedUiMaterial {
+ bindings: prepared.bindings,
+ bind_group: prepared.bind_group,
+ key: prepared.data,
+ })
+}
+
+#[allow(clippy::too_many_arguments)]
+pub fn queue_ui_material_nodes(
+ extracted_uinodes: Res>,
+ draw_functions: Res>,
+ ui_material_pipeline: Res>,
+ mut pipelines: ResMut>>,
+ pipeline_cache: Res,
+ render_materials: Res>,
+ mut views: Query<(&ExtractedView, &mut RenderPhase)>,
+) where
+ M::Data: PartialEq + Eq + Hash + Clone,
+{
+ let draw_function = draw_functions.read().id::>();
+
+ for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() {
+ let material = render_materials.get(&extracted_uinode.material).unwrap();
+ for (view, mut transparent_phase) in &mut views {
+ let pipeline = pipelines.specialize(
+ &pipeline_cache,
+ &ui_material_pipeline,
+ UiMaterialKey {
+ hdr: view.hdr,
+ bind_group_data: material.key.clone(),
+ },
+ );
+ transparent_phase
+ .items
+ .reserve(extracted_uinodes.uinodes.len());
+ transparent_phase.add(TransparentUi {
+ draw_function,
+ pipeline,
+ entity: *entity,
+ sort_key: (
+ FloatOrd(extracted_uinode.stack_index as f32),
+ entity.index(),
+ ),
+ batch_range: 0..0,
+ dynamic_offset: None,
+ });
+ }
+ }
+}
diff --git a/crates/bevy_ui/src/render/ui_vertex_output.wgsl b/crates/bevy_ui/src/render/ui_vertex_output.wgsl
new file mode 100644
index 0000000000000..de41c52819c64
--- /dev/null
+++ b/crates/bevy_ui/src/render/ui_vertex_output.wgsl
@@ -0,0 +1,9 @@
+#define_import_path bevy_ui::ui_vertex_output
+
+// The Vertex output of the default vertex shader for the Ui Material pipeline.
+struct UiVertexOutput {
+ @location(0) uv: vec2,
+ // The size of the borders in UV space. Order is Left, Right, Top, Bottom.
+ @location(1) border_widths: vec4,
+ @builtin(position) position: vec4,
+};
diff --git a/crates/bevy_ui/src/ui_material.rs b/crates/bevy_ui/src/ui_material.rs
new file mode 100644
index 0000000000000..680d4aa4100e6
--- /dev/null
+++ b/crates/bevy_ui/src/ui_material.rs
@@ -0,0 +1,145 @@
+use std::hash::Hash;
+
+use bevy_asset::Asset;
+use bevy_render::render_resource::{AsBindGroup, RenderPipelineDescriptor, ShaderRef};
+
+/// Materials are used alongside [`UiMaterialPlugin`](crate::UiMaterialPipeline) and [`MaterialNodeBundle`](crate::prelude::MaterialNodeBundle)
+/// to spawn entities that are rendered with a specific [`UiMaterial`] type. They serve as an easy to use high level
+/// way to render `Node` entities with custom shader logic.
+///
+/// `UiMaterials` must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders.
+/// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details.
+///
+/// Materials must also implement [`Asset`] so they can be treated as such.
+///
+/// If you are only using the fragment shader, make sure your shader imports the `UiVertexOutput`
+/// from `bevy_ui::ui_vertex_output` and uses it as the input of your fragment shader like the
+/// example below does.
+///
+/// # Example
+///
+/// Here is a simple [`UiMaterial`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available,
+/// check out the [`AsBindGroup`] documentation.
+/// ```
+/// # use bevy_ui::prelude::*;
+/// # use bevy_ecs::prelude::*;
+/// # use bevy_reflect::TypePath;
+/// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image, color::Color};
+/// # use bevy_asset::{Handle, AssetServer, Assets, Asset};
+///
+/// #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
+/// pub struct CustomMaterial {
+/// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to
+/// // its shader-compatible equivalent. Most core math types already implement `ShaderType`.
+/// #[uniform(0)]
+/// color: Color,
+/// // Images can be bound as textures in shaders. If the Image's sampler is also needed, just
+/// // add the sampler attribute with a different binding index.
+/// #[texture(1)]
+/// #[sampler(2)]
+/// color_texture: Handle,
+/// }
+///
+/// // All functions on `UiMaterial` have default impls. You only need to implement the
+/// // functions that are relevant for your material.
+/// impl UiMaterial for CustomMaterial {
+/// fn fragment_shader() -> ShaderRef {
+/// "shaders/custom_material.wgsl".into()
+/// }
+/// }
+///
+/// // Spawn an entity using `CustomMaterial`.
+/// fn setup(mut commands: Commands, mut materials: ResMut>, asset_server: Res) {
+/// commands.spawn(MaterialNodeBundle {
+/// style: Style {
+/// width: Val::Percent(100.0),
+/// ..Default::default()
+/// },
+/// material: materials.add(CustomMaterial {
+/// color: Color::RED,
+/// color_texture: asset_server.load("some_image.png"),
+/// }),
+/// ..Default::default()
+/// });
+/// }
+/// ```
+/// In WGSL shaders, the material's binding would look like this:
+///
+/// If you only use the fragment shader make sure to import `UiVertexOutput` from
+/// `bevy_ui::ui_vertex_output` in your wgsl shader.
+/// Also note that bind group 0 is always bound to the [`View Uniform`](bevy_render::view::ViewUniform).
+///
+/// ```wgsl
+/// #import bevy_ui::ui_vertex_output UiVertexOutput
+///
+/// struct CustomMaterial {
+/// color: vec4,
+/// }
+///
+/// @group(1) @binding(0)
+/// var material: CustomMaterial;
+/// @group(1) @binding(1)
+/// var color_texture: texture_2d;
+/// @group(1) @binding(2)
+/// var color_sampler: sampler;
+///
+/// @fragment
+/// fn fragment(in: UiVertexOutput) -> @location(0) vec4 {
+///
+/// }
+/// ```
+pub trait UiMaterial: AsBindGroup + Asset + Clone + Sized {
+ /// Returns this materials vertex shader. If [`ShaderRef::Default`] is returned, the default UI
+ /// vertex shader will be used.
+ fn vertex_shader() -> ShaderRef {
+ ShaderRef::Default
+ }
+
+ /// Returns this materials fragment shader. If [`ShaderRef::Default`] is returned, the default
+ /// UI fragment shader will be used.
+ fn fragment_shader() -> ShaderRef {
+ ShaderRef::Default
+ }
+
+ #[allow(unused_variables)]
+ #[inline]
+ fn specialize(descriptor: &mut RenderPipelineDescriptor, key: UiMaterialKey) {}
+}
+
+pub struct UiMaterialKey {
+ pub hdr: bool,
+ pub bind_group_data: M::Data,
+}
+
+impl Eq for UiMaterialKey where M::Data: PartialEq {}
+
+impl PartialEq for UiMaterialKey
+where
+ M::Data: PartialEq,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.hdr == other.hdr && self.bind_group_data == other.bind_group_data
+ }
+}
+
+impl Clone for UiMaterialKey
+where
+ M::Data: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ hdr: self.hdr,
+ bind_group_data: self.bind_group_data.clone(),
+ }
+ }
+}
+
+impl Hash for UiMaterialKey
+where
+ M::Data: Hash,
+{
+ fn hash(&self, state: &mut H) {
+ self.hdr.hash(state);
+ self.bind_group_data.hash(state);
+ }
+}
diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs
index b77b633dae62b..7b5c75d38f6d5 100644
--- a/crates/bevy_window/src/window.rs
+++ b/crates/bevy_window/src/window.rs
@@ -589,7 +589,7 @@ pub struct WindowResolution {
physical_height: u32,
/// Code-provided ratio of physical size to logical size.
///
- /// Should be used instead `scale_factor` when set.
+ /// Should be used instead of `scale_factor` when set.
scale_factor_override: Option,
/// OS-provided ratio of physical size to logical size.
///
diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs
index 955f954a6f45f..91c9858c79293 100644
--- a/crates/bevy_winit/src/lib.rs
+++ b/crates/bevy_winit/src/lib.rs
@@ -372,6 +372,7 @@ pub fn winit_runner(mut app: App) {
WindowAndInputEventWriters,
NonSend,
Query<(&mut Window, &mut CachedWindow)>,
+ NonSend,
)> = SystemState::new(&mut app.world);
#[cfg(not(target_arch = "wasm32"))]
@@ -476,7 +477,7 @@ pub fn winit_runner(mut app: App) {
event::Event::WindowEvent {
event, window_id, ..
} => {
- let (mut event_writers, winit_windows, mut windows) =
+ let (mut event_writers, winit_windows, mut windows, access_kit_adapters) =
event_writer_system_state.get_mut(&mut app.world);
let Some(window_entity) = winit_windows.get_window_entity(window_id) else {
@@ -495,6 +496,18 @@ pub fn winit_runner(mut app: App) {
return;
};
+ // Allow AccessKit to respond to `WindowEvent`s before they reach
+ // the engine.
+ if let Some(adapter) = access_kit_adapters.get(&window_entity) {
+ if let Some(window) = winit_windows.get_window(window_entity) {
+ // Somewhat surprisingly, this call has meaningful side effects
+ // See https://github.com/AccessKit/accesskit/issues/300
+ // AccessKit might later need to filter events based on this, but we currently do not.
+ // See https://github.com/bevyengine/bevy/pull/10239#issuecomment-1775572176
+ let _ = adapter.on_event(window, &event);
+ }
+ }
+
runner_state.window_event_received = true;
match event {
@@ -713,20 +726,20 @@ pub fn winit_runner(mut app: App) {
event: DeviceEvent::MouseMotion { delta: (x, y) },
..
} => {
- let (mut event_writers, _, _) = event_writer_system_state.get_mut(&mut app.world);
+ let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world);
event_writers.mouse_motion.send(MouseMotion {
delta: Vec2::new(x as f32, y as f32),
});
}
event::Event::Suspended => {
- let (mut event_writers, _, _) = event_writer_system_state.get_mut(&mut app.world);
+ let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world);
event_writers.lifetime.send(ApplicationLifetime::Suspended);
// Mark the state as `WillSuspend`. This will let the schedule run one last time
// before actually suspending to let the application react
runner_state.active = ActiveState::WillSuspend;
}
event::Event::Resumed => {
- let (mut event_writers, _, _) = event_writer_system_state.get_mut(&mut app.world);
+ let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world);
match runner_state.active {
ActiveState::NotYetStarted => {
event_writers.lifetime.send(ApplicationLifetime::Started);
diff --git a/example_showcase_config.ron b/example_showcase_config.ron
new file mode 100644
index 0000000000000..4503c3738b386
--- /dev/null
+++ b/example_showcase_config.ron
@@ -0,0 +1 @@
+(exit_after: Some(250))
\ No newline at end of file
diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs
index ca9095dfc6fbf..357865216a412 100644
--- a/examples/2d/bloom_2d.rs
+++ b/examples/2d/bloom_2d.rs
@@ -37,7 +37,7 @@ fn setup(
// Sprite
commands.spawn(SpriteBundle {
- texture: asset_server.load("branding/icon.png"),
+ texture: asset_server.load("branding/bevy_bird_dark.png"),
sprite: Sprite {
color: Color::rgb(5.0, 5.0, 5.0), // 4. Put something bright in a dark environment to see the effect
custom_size: Some(Vec2::splat(160.0)),
diff --git a/examples/2d/pixel_perfect.rs b/examples/2d/pixel_perfect.rs
index f0f276da619b7..ef7b89542f455 100644
--- a/examples/2d/pixel_perfect.rs
+++ b/examples/2d/pixel_perfect.rs
@@ -25,7 +25,7 @@ fn setup(mut commands: Commands, asset_server: Res) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
SpriteBundle {
- texture: asset_server.load("pixel/bevy_pixel_light.png"),
+ texture: asset_server.load("pixel/bevy_pixel_dark.png"),
transform: Transform::from_xyz(100., 0., 0.),
..default()
},
diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs
index d26478be5891a..5fdf172a5be19 100644
--- a/examples/2d/sprite.rs
+++ b/examples/2d/sprite.rs
@@ -12,7 +12,7 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res) {
commands.spawn(Camera2dBundle::default());
commands.spawn(SpriteBundle {
- texture: asset_server.load("branding/icon.png"),
+ texture: asset_server.load("branding/bevy_bird_dark.png"),
..default()
});
}
diff --git a/examples/2d/sprite_flipping.rs b/examples/2d/sprite_flipping.rs
index 4be1908819182..33ac914995a99 100644
--- a/examples/2d/sprite_flipping.rs
+++ b/examples/2d/sprite_flipping.rs
@@ -12,7 +12,7 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res) {
commands.spawn(Camera2dBundle::default());
commands.spawn(SpriteBundle {
- texture: asset_server.load("branding/icon.png"),
+ texture: asset_server.load("branding/bevy_bird_dark.png"),
sprite: Sprite {
// Flip the logo to the left
flip_x: true,
diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs
index 683613bf8b2bf..ca142616fdb53 100644
--- a/examples/3d/3d_scene.rs
+++ b/examples/3d/3d_scene.rs
@@ -15,7 +15,7 @@ fn setup(
mut meshes: ResMut>,
mut materials: ResMut>,
) {
- // plane
+ // circular base
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Circle::new(4.0).into()),
material: materials.add(Color::WHITE.into()),
diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs
index c92c67cd6556c..6e71640f74372 100644
--- a/examples/3d/anti_aliasing.rs
+++ b/examples/3d/anti_aliasing.rs
@@ -263,8 +263,8 @@ fn setup(
) {
// Plane
commands.spawn(PbrBundle {
- mesh: meshes.add(shape::Plane::from_size(5.0).into()),
- material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
+ mesh: meshes.add(shape::Plane::from_size(50.0).into()),
+ material: materials.add(Color::GRAY.into()),
..default()
});
@@ -325,6 +325,18 @@ fn setup(
enabled: false,
..default()
},
+ EnvironmentMapLight {
+ diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
+ specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
+ },
+ FogSettings {
+ color: Color::rgba_u8(43, 44, 47, 255),
+ falloff: FogFalloff::Linear {
+ start: 1.0,
+ end: 4.0,
+ },
+ ..default()
+ },
));
// example instructions
diff --git a/examples/3d/deferred_rendering.rs b/examples/3d/deferred_rendering.rs
index f1cf76058b798..1bf1978851779 100644
--- a/examples/3d/deferred_rendering.rs
+++ b/examples/3d/deferred_rendering.rs
@@ -52,7 +52,7 @@ fn setup(
..default()
},
FogSettings {
- color: Color::rgba(0.25, 0.25, 0.25, 1.0),
+ color: Color::rgba_u8(43, 44, 47, 255),
falloff: FogFalloff::Linear {
start: 1.0,
end: 8.0,
@@ -93,11 +93,11 @@ fn setup(
});
commands.spawn(SceneBundle {
scene: helmet_scene,
- transform: Transform::from_xyz(-3.0, 0.0, -3.0),
+ transform: Transform::from_xyz(-4.0, 0.0, -3.0),
..default()
});
- let mut forward_mat: StandardMaterial = Color::rgb(0.1, 0.2, 0.1).into();
+ let mut forward_mat: StandardMaterial = Color::GRAY.into();
forward_mat.opaque_render_method = OpaqueRendererMethod::Forward;
let forward_mat_h = materials.add(forward_mat);
diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs
index ccf7f7b5dc7b9..f6a412608b4d7 100644
--- a/examples/3d/load_gltf.rs
+++ b/examples/3d/load_gltf.rs
@@ -8,10 +8,6 @@ use std::f32::consts::*;
fn main() {
App::new()
- .insert_resource(AmbientLight {
- color: Color::WHITE,
- brightness: 1.0 / 5.0f32,
- })
.insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
diff --git a/examples/3d/spotlight.rs b/examples/3d/spotlight.rs
index 6c9083622d837..6813ad4babca3 100644
--- a/examples/3d/spotlight.rs
+++ b/examples/3d/spotlight.rs
@@ -9,10 +9,15 @@ use rand::{rngs::StdRng, Rng, SeedableRng};
fn main() {
App::new()
+ .insert_resource(AmbientLight {
+ brightness: 0.02,
+ ..default()
+ })
.add_plugins((
DefaultPlugins,
FrameTimeDiagnosticsPlugin,
LogDiagnosticsPlugin::default(),
+ bevy_internal::core_pipeline::experimental::taa::TemporalAntiAliasPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, (light_sway, movement))
@@ -31,21 +36,14 @@ fn setup(
// ground plane
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Plane::from_size(100.0).into()),
- material: materials.add(StandardMaterial {
- base_color: Color::GREEN,
- perceptual_roughness: 1.0,
- ..default()
- }),
+ material: materials.add(Color::WHITE.into()),
..default()
});
// cubes
let mut rng = StdRng::seed_from_u64(19878367467713);
let cube_mesh = meshes.add(Mesh::from(shape::Cube { size: 0.5 }));
- let blue = materials.add(StandardMaterial {
- base_color: Color::BLUE,
- ..default()
- });
+ let blue = materials.add(Color::rgb_u8(124, 144, 255).into());
for _ in 0..40 {
let x = rng.gen_range(-5.0..5.0);
let y = rng.gen_range(0.0..3.0);
@@ -61,12 +59,6 @@ fn setup(
));
}
- // ambient light
- commands.insert_resource(AmbientLight {
- color: Color::rgb(0.0, 1.0, 1.0),
- brightness: 0.14,
- });
-
let sphere_mesh = meshes.add(Mesh::from(shape::UVSphere {
radius: 0.05,
..default()
@@ -124,10 +116,14 @@ fn setup(
}
// camera
- commands.spawn(Camera3dBundle {
+ commands.spawn((Camera3dBundle {
+ camera: Camera {
+ hdr: true,
+ ..default()
+ },
transform: Transform::from_xyz(-4.0, 5.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
- });
+ },));
}
fn light_sway(time: Res