Skip to content

Commit

Permalink
Add UI Materials (bevyengine#9506)
Browse files Browse the repository at this point in the history
# Objective

- Add Ui Materials so that UI can render more complex and animated
widgets.
- Fixes bevyengine#5607 

## Solution
- Create a UiMaterial trait for specifying a Shader Asset and Bind Group
Layout/Data.
- Create a pipeline for rendering these Materials inside the Ui
layout/tree.
- Create a MaterialNodeBundle for simple spawning.

## Changelog

- Created a `UiMaterial` trait for specifying a Shader asset and Bind
Group.
- Created a `UiMaterialPipeline` for rendering said Materials.
- Added Example [`ui_material`
](https://github.com/MarkusTheOrt/bevy/blob/ui_material/examples/ui/ui_material.rs)
for example usage.
- Created
[`UiVertexOutput`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_vertex_output.wgsl)
export as VertexData for shaders.
- Created
[`material_ui`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_material.wgsl)
shader as default for both Vertex and Fragment shaders.

---------

Co-authored-by: ickshonpe <[email protected]>
Co-authored-by: François <[email protected]>
  • Loading branch information
3 people authored and Ray Redondo committed Jan 9, 2024
1 parent 068c11a commit a50a0c7
Show file tree
Hide file tree
Showing 11 changed files with 1,090 additions and 6 deletions.
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2412,6 +2412,17 @@ description = "Demonstrates resizing and responding to resizing a window"
category = "Window"
wasm = true

[[example]]
name = "ui_material"
path = "examples/ui/ui_material.rs"
doc-scrape-examples = true

[package.metadata.example.ui_material]
name = "UI Material"
description = "Demonstrates creating and using custom Ui materials"
category = "UI (User Interface)"
wasm = true

[profile.wasm-release]
inherits = "release"
opt-level = "z"
Expand Down
21 changes: 21 additions & 0 deletions assets/shaders/circle_shader.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This shader draws a circle with a given input color
#import bevy_ui::ui_vertex_output::UiVertexOutput

struct CustomUiMaterial {
@location(0) color: vec4<f32>
}

@group(1) @binding(0)
var<uniform> input: CustomUiMaterial;

@fragment
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
// the UVs are now adjusted around the middle of the rect.
let uv = in.uv * 2.0 - 1.0;

// circle alpha, the higher the power the harsher the falloff.
let alpha = 1.0 - pow(sqrt(dot(uv, uv)), 100.0);

return vec4<f32>(input.color.rgb, alpha);
}

6 changes: 4 additions & 2 deletions crates/bevy_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
pub mod camera_config;
pub mod measurement;
pub mod node_bundles;
pub mod ui_material;
pub mod update;
pub mod widget;

Expand All @@ -29,15 +30,16 @@ pub use geometry::*;
pub use layout::*;
pub use measurement::*;
pub use render::*;
pub use ui_material::*;
pub use ui_node::*;
use widget::UiImageSize;

#[doc(hidden)]
pub mod prelude {
#[doc(hidden)]
pub use crate::{
camera_config::*, geometry::*, node_bundles::*, ui_node::*, widget::Button, widget::Label,
Interaction, UiScale,
camera_config::*, geometry::*, node_bundles::*, ui_material::*, ui_node::*, widget::Button,
widget::Label, Interaction, UiMaterialPlugin, UiScale,
};
}

Expand Down
51 changes: 50 additions & 1 deletion crates/bevy_ui/src/node_bundles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::widget::TextFlags;
use crate::{
widget::{Button, UiImageSize},
BackgroundColor, BorderColor, ContentSize, FocusPolicy, Interaction, Node, Style, UiImage,
UiTextureAtlasImage, ZIndex,
UiMaterial, UiTextureAtlasImage, ZIndex,
};
use bevy_asset::Handle;
use bevy_ecs::bundle::Bundle;
Expand Down Expand Up @@ -342,3 +342,52 @@ impl Default for ButtonBundle {
}
}
}

/// A UI node that is rendered using a [`UiMaterial`]
#[derive(Bundle, Clone, Debug)]
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 it's 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: Handle<M>,
/// Whether this node should block interaction with lower nodes
pub focus_policy: FocusPolicy,
/// The transform of the node
///
/// This field is automatically managed by the UI layout system.
/// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component.
pub transform: Transform,
/// The global transform of the node
///
/// This field is automatically managed by the UI layout system.
/// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component.
pub global_transform: GlobalTransform,
/// Describes the visibility properties of the node
pub visibility: Visibility,
/// Inherited visibility of an entity.
pub inherited_visibility: InheritedVisibility,
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
pub view_visibility: ViewVisibility,
/// Indicates the depth at which the node should appear in the UI
pub z_index: ZIndex,
}

impl<M: UiMaterial> Default for MaterialNodeBundle<M> {
fn default() -> Self {
Self {
node: Default::default(),
style: Default::default(),
material: Default::default(),
focus_policy: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
visibility: Default::default(),
inherited_visibility: Default::default(),
view_visibility: Default::default(),
z_index: Default::default(),
}
}
}
8 changes: 5 additions & 3 deletions crates/bevy_ui/src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod pipeline;
mod render_pass;
mod ui_material_pipeline;

use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_hierarchy::Parent;
Expand All @@ -9,6 +10,7 @@ use bevy_render::{render_resource::BindGroupEntries, ExtractSchedule, Render};
use bevy_window::{PrimaryWindow, Window};
pub use pipeline::*;
pub use render_pass::*;
pub use ui_material_pipeline::*;

use crate::Outline;
use crate::{
Expand Down Expand Up @@ -253,7 +255,7 @@ pub fn extract_atlas_uinodes(
}
}

fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 {
pub(crate) fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 {
match value {
Val::Auto => 0.,
Val::Px(px) => px.max(0.),
Expand Down Expand Up @@ -695,14 +697,14 @@ impl Default for UiMeta {
}
}

const QUAD_VERTEX_POSITIONS: [Vec3; 4] = [
pub(crate) const QUAD_VERTEX_POSITIONS: [Vec3; 4] = [
Vec3::new(-0.5, -0.5, 0.0),
Vec3::new(0.5, -0.5, 0.0),
Vec3::new(0.5, 0.5, 0.0),
Vec3::new(-0.5, 0.5, 0.0),
];

const QUAD_INDICES: [usize; 6] = [0, 2, 3, 0, 1, 2];
pub(crate) const QUAD_INDICES: [usize; 6] = [0, 2, 3, 0, 1, 2];

#[derive(Component)]
pub struct UiBatch {
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_ui/src/render/ui_material.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#import bevy_render::view::View
#import bevy_ui::ui_vertex_output::UiVertexOutput

@group(0) @binding(0)
var<uniform> view: View;

@vertex
fn vertex(
@location(0) vertex_position: vec3<f32>,
@location(1) vertex_uv: vec2<f32>,
@location(2) border_widths: vec4<f32>,
) -> UiVertexOutput {
var out: UiVertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(vertex_position, 1.0);
out.border_widths = border_widths;
return out;
}

@fragment
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(1.0);
}
Loading

0 comments on commit a50a0c7

Please sign in to comment.