Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui_render/src/ui_material.rs
6596 views
1
use crate::Node;
2
use bevy_asset::{Asset, AssetId, Handle};
3
use bevy_derive::{Deref, DerefMut};
4
use bevy_ecs::{component::Component, reflect::ReflectComponent};
5
use bevy_reflect::{prelude::ReflectDefault, Reflect};
6
use bevy_render::{
7
extract_component::ExtractComponent,
8
render_resource::{AsBindGroup, RenderPipelineDescriptor},
9
};
10
use bevy_shader::ShaderRef;
11
use derive_more::derive::From;
12
13
/// Materials are used alongside [`UiMaterialPlugin`](crate::UiMaterialPlugin) and [`MaterialNode`]
14
/// to spawn entities that are rendered with a specific [`UiMaterial`] type. They serve as an easy to use high level
15
/// way to render `Node` entities with custom shader logic.
16
///
17
/// `UiMaterials` must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders.
18
/// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details.
19
///
20
/// Materials must also implement [`Asset`] so they can be treated as such.
21
///
22
/// If you are only using the fragment shader, make sure your shader imports the `UiVertexOutput`
23
/// from `bevy_ui::ui_vertex_output` and uses it as the input of your fragment shader like the
24
/// example below does.
25
///
26
/// # Example
27
///
28
/// Here is a simple [`UiMaterial`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available,
29
/// check out the [`AsBindGroup`] documentation.
30
/// ```
31
/// # use bevy_ui::prelude::*;
32
/// # use bevy_ecs::prelude::*;
33
/// # use bevy_image::Image;
34
/// # use bevy_reflect::TypePath;
35
/// # use bevy_render::render_resource::AsBindGroup;
36
/// # use bevy_color::LinearRgba;
37
/// # use bevy_shader::ShaderRef;
38
/// # use bevy_asset::{Handle, AssetServer, Assets, Asset};
39
/// # use bevy_ui_render::prelude::*;
40
///
41
/// #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
42
/// pub struct CustomMaterial {
43
/// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to
44
/// // its shader-compatible equivalent. Most core math types already implement `ShaderType`.
45
/// #[uniform(0)]
46
/// color: LinearRgba,
47
/// // Images can be bound as textures in shaders. If the Image's sampler is also needed, just
48
/// // add the sampler attribute with a different binding index.
49
/// #[texture(1)]
50
/// #[sampler(2)]
51
/// color_texture: Handle<Image>,
52
/// }
53
///
54
/// // All functions on `UiMaterial` have default impls. You only need to implement the
55
/// // functions that are relevant for your material.
56
/// impl UiMaterial for CustomMaterial {
57
/// fn fragment_shader() -> ShaderRef {
58
/// "shaders/custom_material.wgsl".into()
59
/// }
60
/// }
61
///
62
/// // Spawn an entity using `CustomMaterial`.
63
/// fn setup(mut commands: Commands, mut materials: ResMut<Assets<CustomMaterial>>, asset_server: Res<AssetServer>) {
64
/// commands.spawn((
65
/// MaterialNode(materials.add(CustomMaterial {
66
/// color: LinearRgba::RED,
67
/// color_texture: asset_server.load("some_image.png"),
68
/// })),
69
/// Node {
70
/// width: Val::Percent(100.0),
71
/// ..Default::default()
72
/// },
73
/// ));
74
/// }
75
/// ```
76
/// In WGSL shaders, the material's binding would look like this:
77
///
78
/// If you only use the fragment shader make sure to import `UiVertexOutput` from
79
/// `bevy_ui::ui_vertex_output` in your wgsl shader.
80
/// Also note that bind group 0 is always bound to the [`View Uniform`](bevy_render::view::ViewUniform)
81
/// and the [`Globals Uniform`](bevy_render::globals::GlobalsUniform).
82
///
83
/// ```wgsl
84
/// #import bevy_ui::ui_vertex_output UiVertexOutput
85
///
86
/// struct CustomMaterial {
87
/// color: vec4<f32>,
88
/// }
89
///
90
/// @group(1) @binding(0)
91
/// var<uniform> material: CustomMaterial;
92
/// @group(1) @binding(1)
93
/// var color_texture: texture_2d<f32>;
94
/// @group(1) @binding(2)
95
/// var color_sampler: sampler;
96
///
97
/// @fragment
98
/// fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
99
///
100
/// }
101
/// ```
102
pub trait UiMaterial: AsBindGroup + Asset + Clone + Sized {
103
/// Returns this materials vertex shader. If [`ShaderRef::Default`] is returned, the default UI
104
/// vertex shader will be used.
105
fn vertex_shader() -> ShaderRef {
106
ShaderRef::Default
107
}
108
109
/// Returns this materials fragment shader. If [`ShaderRef::Default`] is returned, the default
110
/// UI fragment shader will be used.
111
fn fragment_shader() -> ShaderRef {
112
ShaderRef::Default
113
}
114
115
#[expect(
116
unused_variables,
117
reason = "The parameters here are intentionally unused by the default implementation; however, putting underscores here will result in the underscores being copied by rust-analyzer's tab completion."
118
)]
119
#[inline]
120
fn specialize(descriptor: &mut RenderPipelineDescriptor, key: UiMaterialKey<Self>) {}
121
}
122
123
pub struct UiMaterialKey<M: UiMaterial> {
124
pub hdr: bool,
125
pub bind_group_data: M::Data,
126
}
127
128
impl<M: UiMaterial> Eq for UiMaterialKey<M> where M::Data: PartialEq {}
129
130
impl<M: UiMaterial> PartialEq for UiMaterialKey<M>
131
where
132
M::Data: PartialEq,
133
{
134
fn eq(&self, other: &Self) -> bool {
135
self.hdr == other.hdr && self.bind_group_data == other.bind_group_data
136
}
137
}
138
139
impl<M: UiMaterial> Clone for UiMaterialKey<M>
140
where
141
M::Data: Clone,
142
{
143
fn clone(&self) -> Self {
144
Self {
145
hdr: self.hdr,
146
bind_group_data: self.bind_group_data.clone(),
147
}
148
}
149
}
150
151
impl<M: UiMaterial> core::hash::Hash for UiMaterialKey<M>
152
where
153
M::Data: core::hash::Hash,
154
{
155
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
156
self.hdr.hash(state);
157
self.bind_group_data.hash(state);
158
}
159
}
160
161
#[derive(
162
Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq, ExtractComponent, From,
163
)]
164
#[reflect(Component, Default)]
165
#[require(Node)]
166
pub struct MaterialNode<M: UiMaterial>(pub Handle<M>);
167
168
impl<M: UiMaterial> Default for MaterialNode<M> {
169
fn default() -> Self {
170
Self(Handle::default())
171
}
172
}
173
174
impl<M: UiMaterial> From<MaterialNode<M>> for AssetId<M> {
175
fn from(material: MaterialNode<M>) -> Self {
176
material.id()
177
}
178
}
179
180
impl<M: UiMaterial> From<&MaterialNode<M>> for AssetId<M> {
181
fn from(material: &MaterialNode<M>) -> Self {
182
material.id()
183
}
184
}
185
186