Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate UI bundles to required components #15898

Merged
merged 17 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub mod prelude {
ui_material::*,
ui_node::*,
widget::{Button, Label},
Interaction, UiMaterialHandle, UiMaterialPlugin, UiScale,
Interaction, MaterialNode, UiMaterialPlugin, UiScale,
},
// `bevy_sprite` re-exports for texture slicing
bevy_sprite::{BorderRect, ImageScaleMode, SliceScaleMode, TextureSlicer},
Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_ui/src/node_bundles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use crate::{
widget::{Button, UiImageSize},
BackgroundColor, BorderColor, BorderRadius, ContentSize, FocusPolicy, Interaction, Node,
ScrollPosition, Style, UiImage, UiMaterial, UiMaterialHandle, ZIndex,
BackgroundColor, BorderColor, BorderRadius, ContentSize, FocusPolicy, Interaction,
MaterialNode, Node, ScrollPosition, Style, UiImage, UiMaterial, ZIndex,
};
use bevy_ecs::bundle::Bundle;
use bevy_render::view::{InheritedVisibility, ViewVisibility, Visibility};
Expand Down Expand Up @@ -187,14 +187,18 @@ impl Default for ButtonBundle {
/// Adding a `BackgroundColor` component to an entity with this bundle will ignore the custom
/// material and use the background color instead.
#[derive(Bundle, Clone, Debug)]
#[deprecated(
since = "0.15.0",
note = "Use the `MaterialNode` component instead. Inserting `MaterialNode` will also insert the other components required automatically."
)]
pub struct MaterialNodeBundle<M: UiMaterial> {
/// Describes the logical size of the node
pub node: Node,
/// Styles which control the layout (size and position) of the node and its children
/// In some cases these styles also affect how the node drawn/painted.
pub style: Style,
/// The [`UiMaterial`] used to render the node.
pub material: UiMaterialHandle<M>,
pub material: MaterialNode<M>,
/// Whether this node should block interaction with lower nodes
pub focus_policy: FocusPolicy,
/// The transform of the node
Expand Down
21 changes: 20 additions & 1 deletion crates/bevy_ui/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ pub mod graph {
}
}

/// Z offsets of "extracted nodes" for a given entity. These exist to allow rendering multiple "extracted nodes"
/// for a given source entity (ex: render both a background color _and_ a custom material for a given node).
///
/// When possible these offsets should be defined in _this_ module to ensure z-index coordination across contexts.
/// When this is _not_ possible, pick a suitably unique index unlikely to clash with other things (ex: `0.1826823` not `0.1`).
///
/// Offsets should be unique for a given node entity to avoid z fighting.
/// These should pretty much _always_ be larger than -1.0 and smaller than 1.0 to avoid clipping into nodes
/// above / below the current node in the stack.
///
/// A z-index of 0.0 is the baseline, which is used as the primary "background color" of the node.
///
/// Note that nodes "stack" on each other, so a negative offset on the node above could clip _into_
/// a positive offset on a node below.
pub mod stack_z_offsets {
pub const BACKGROUND_COLOR: f32 = 0.0;
pub const MATERIAL: f32 = 0.18267;
}

pub const UI_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(13012847047162779583);

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
Expand Down Expand Up @@ -823,7 +842,7 @@ pub fn queue_uinodes(
pipeline,
entity: (*entity, extracted_uinode.main_entity),
sort_key: (
FloatOrd(extracted_uinode.stack_index as f32),
FloatOrd(extracted_uinode.stack_index as f32 + stack_z_offsets::BACKGROUND_COLOR),
Copy link
Contributor

@ickshonpe ickshonpe Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just set extract_ui_material_nodes after extract_ui_background_colors instead.
TransparentUi uses a stable sort, so if two phase items have the same stack index then the z-order is determined by the ordering of the extraction systems.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. I don't have strong opinions one way or the other. I doubt a single per-entity float add will meaningfully tip the performance scales. Doing this per-entity gives us more granularity than "per thing encapsulated by a system", but we also currently have no use case where this matters. I also like that it puts the sort behavior front-and-center in a centralized place. The current behavior is pretty implicit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't care about the performance impact but I don't like the inconsistancy very much and it breaks any existing code that rendered borders or text above ui materials on the same node.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but it also buys flexibility for future scenarios and decouples sort order from system execution order (which feels like a good thing as execution order could easily need to be different than sort order) .

I still don't have super strong opinions here. I think I prefer the explicitness of defining offsets. But I wouldn't strongly object to a change that moves this to using system execution order.

entity.index(),
),
// batch_range will be calculated in prepare_uinodes
Expand Down
27 changes: 12 additions & 15 deletions crates/bevy_ui/src/render/ui_material_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ where
Shader::from_wgsl
);
app.init_asset::<M>()
.register_type::<UiMaterialHandle<M>>()
.register_type::<MaterialNode<M>>()
.add_plugins((
ExtractComponentPlugin::<UiMaterialHandle<M>>::extract_visible(),
ExtractComponentPlugin::<MaterialNode<M>>::extract_visible(),
RenderAssetPlugin::<PreparedUiMaterial<M>>::default(),
));

Expand Down Expand Up @@ -363,18 +363,15 @@ pub fn extract_ui_material_nodes<M: UiMaterial>(
materials: Extract<Res<Assets<M>>>,
default_ui_camera: Extract<DefaultUiCamera>,
uinode_query: Extract<
Query<
(
Entity,
&Node,
&GlobalTransform,
&UiMaterialHandle<M>,
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
),
Without<BackgroundColor>,
>,
Query<(
Entity,
&Node,
&GlobalTransform,
&MaterialNode<M>,
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
)>,
>,
render_entity_lookup: Extract<Query<RenderEntity>>,
) {
Expand Down Expand Up @@ -652,7 +649,7 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
pipeline,
entity: (*entity, extracted_uinode.main_entity),
sort_key: (
FloatOrd(extracted_uinode.stack_index as f32),
FloatOrd(extracted_uinode.stack_index as f32 + stack_z_offsets::MATERIAL),
entity.index(),
),
batch_range: 0..0,
Expand Down
31 changes: 16 additions & 15 deletions crates/bevy_ui/src/ui_material.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::hash::Hash;

use crate::Node;
use bevy_asset::{Asset, AssetId, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
Expand All @@ -10,7 +11,7 @@ use bevy_render::{
};
use derive_more::derive::From;

/// Materials are used alongside [`UiMaterialPlugin`](crate::UiMaterialPlugin) and [`MaterialNodeBundle`](crate::prelude::MaterialNodeBundle)
/// Materials are used alongside [`UiMaterialPlugin`](crate::UiMaterialPlugin) and [`MaterialNode`](crate::prelude::MaterialNode)
/// 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.
///
Expand Down Expand Up @@ -58,17 +59,16 @@ use derive_more::derive::From;
///
/// // Spawn an entity using `CustomMaterial`.
/// fn setup(mut commands: Commands, mut materials: ResMut<Assets<CustomMaterial>>, asset_server: Res<AssetServer>) {
/// commands.spawn(MaterialNodeBundle {
/// style: Style {
/// width: Val::Percent(100.0),
/// ..Default::default()
/// },
/// material: UiMaterialHandle(materials.add(CustomMaterial {
/// commands.spawn((
/// MaterialNode(materials.add(CustomMaterial {
/// color: LinearRgba::RED,
/// color_texture: asset_server.load("some_image.png"),
/// })),
/// ..Default::default()
/// });
/// Style {
/// width: Val::Percent(100.0),
/// ..Default::default()
/// },
/// ));
/// }
/// ```
/// In WGSL shaders, the material's binding would look like this:
Expand Down Expand Up @@ -157,22 +157,23 @@ where
Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq, ExtractComponent, From,
)]
#[reflect(Component, Default)]
pub struct UiMaterialHandle<M: UiMaterial>(pub Handle<M>);
#[require(Node)]
pub struct MaterialNode<M: UiMaterial>(pub Handle<M>);

impl<M: UiMaterial> Default for UiMaterialHandle<M> {
impl<M: UiMaterial> Default for MaterialNode<M> {
fn default() -> Self {
Self(Handle::default())
}
}

impl<M: UiMaterial> From<UiMaterialHandle<M>> for AssetId<M> {
fn from(material: UiMaterialHandle<M>) -> Self {
impl<M: UiMaterial> From<MaterialNode<M>> for AssetId<M> {
fn from(material: MaterialNode<M>) -> Self {
material.id()
}
}

impl<M: UiMaterial> From<&UiMaterialHandle<M>> for AssetId<M> {
fn from(material: &UiMaterialHandle<M>) -> Self {
impl<M: UiMaterial> From<&MaterialNode<M>> for AssetId<M> {
fn from(material: &MaterialNode<M>) -> Self {
material.id()
}
}
24 changes: 12 additions & 12 deletions examples/ui/ui_material.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Demonstrates the use of [`UiMaterials`](UiMaterial) and how to change material values

use bevy::{prelude::*, reflect::TypePath, render::render_resource::*};
use bevy::{color::palettes::css::RED, prelude::*, reflect::TypePath, render::render_resource::*};

/// This example uses a shader source file from the assets subdirectory
const SHADER_ASSET_PATH: &str = "shaders/custom_ui_material.wgsl";
Expand Down Expand Up @@ -35,22 +35,22 @@ fn setup(
))
.with_children(|parent| {
let banner_scale_factor = 0.5;
parent.spawn(MaterialNodeBundle {
style: Style {
parent.spawn((
MaterialNode(ui_materials.add(CustomUiMaterial {
color: LinearRgba::WHITE.to_f32_array().into(),
slider: 0.5,
color_texture: asset_server.load("branding/banner.png"),
border_color: LinearRgba::WHITE.to_f32_array().into(),
})),
Style {
position_type: PositionType::Absolute,
width: Val::Px(905.0 * banner_scale_factor),
height: Val::Px(363.0 * banner_scale_factor),
border: UiRect::all(Val::Px(10.)),
..default()
},
material: UiMaterialHandle(ui_materials.add(CustomUiMaterial {
color: LinearRgba::WHITE.to_f32_array().into(),
slider: 0.5,
color_texture: asset_server.load("branding/banner.png"),
border_color: LinearRgba::WHITE.to_f32_array().into(),
})),
..default()
});
BackgroundColor(RED.into()),
));
});
}

Expand Down Expand Up @@ -82,7 +82,7 @@ impl UiMaterial for CustomUiMaterial {
// Also updates the color of the image to a rainbow color
fn animate(
mut materials: ResMut<Assets<CustomUiMaterial>>,
q: Query<&UiMaterialHandle<CustomUiMaterial>>,
q: Query<&MaterialNode<CustomUiMaterial>>,
time: Res<Time>,
) {
let duration = 2.0;
Expand Down
Loading