Path: blob/main/crates/bevy_ui_render/src/ui_material.rs
6596 views
use crate::Node;1use bevy_asset::{Asset, AssetId, Handle};2use bevy_derive::{Deref, DerefMut};3use bevy_ecs::{component::Component, reflect::ReflectComponent};4use bevy_reflect::{prelude::ReflectDefault, Reflect};5use bevy_render::{6extract_component::ExtractComponent,7render_resource::{AsBindGroup, RenderPipelineDescriptor},8};9use bevy_shader::ShaderRef;10use derive_more::derive::From;1112/// Materials are used alongside [`UiMaterialPlugin`](crate::UiMaterialPlugin) and [`MaterialNode`]13/// to spawn entities that are rendered with a specific [`UiMaterial`] type. They serve as an easy to use high level14/// way to render `Node` entities with custom shader logic.15///16/// `UiMaterials` must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders.17/// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details.18///19/// Materials must also implement [`Asset`] so they can be treated as such.20///21/// If you are only using the fragment shader, make sure your shader imports the `UiVertexOutput`22/// from `bevy_ui::ui_vertex_output` and uses it as the input of your fragment shader like the23/// example below does.24///25/// # Example26///27/// Here is a simple [`UiMaterial`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available,28/// check out the [`AsBindGroup`] documentation.29/// ```30/// # use bevy_ui::prelude::*;31/// # use bevy_ecs::prelude::*;32/// # use bevy_image::Image;33/// # use bevy_reflect::TypePath;34/// # use bevy_render::render_resource::AsBindGroup;35/// # use bevy_color::LinearRgba;36/// # use bevy_shader::ShaderRef;37/// # use bevy_asset::{Handle, AssetServer, Assets, Asset};38/// # use bevy_ui_render::prelude::*;39///40/// #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]41/// pub struct CustomMaterial {42/// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to43/// // its shader-compatible equivalent. Most core math types already implement `ShaderType`.44/// #[uniform(0)]45/// color: LinearRgba,46/// // Images can be bound as textures in shaders. If the Image's sampler is also needed, just47/// // add the sampler attribute with a different binding index.48/// #[texture(1)]49/// #[sampler(2)]50/// color_texture: Handle<Image>,51/// }52///53/// // All functions on `UiMaterial` have default impls. You only need to implement the54/// // functions that are relevant for your material.55/// impl UiMaterial for CustomMaterial {56/// fn fragment_shader() -> ShaderRef {57/// "shaders/custom_material.wgsl".into()58/// }59/// }60///61/// // Spawn an entity using `CustomMaterial`.62/// fn setup(mut commands: Commands, mut materials: ResMut<Assets<CustomMaterial>>, asset_server: Res<AssetServer>) {63/// commands.spawn((64/// MaterialNode(materials.add(CustomMaterial {65/// color: LinearRgba::RED,66/// color_texture: asset_server.load("some_image.png"),67/// })),68/// Node {69/// width: Val::Percent(100.0),70/// ..Default::default()71/// },72/// ));73/// }74/// ```75/// In WGSL shaders, the material's binding would look like this:76///77/// If you only use the fragment shader make sure to import `UiVertexOutput` from78/// `bevy_ui::ui_vertex_output` in your wgsl shader.79/// Also note that bind group 0 is always bound to the [`View Uniform`](bevy_render::view::ViewUniform)80/// and the [`Globals Uniform`](bevy_render::globals::GlobalsUniform).81///82/// ```wgsl83/// #import bevy_ui::ui_vertex_output UiVertexOutput84///85/// struct CustomMaterial {86/// color: vec4<f32>,87/// }88///89/// @group(1) @binding(0)90/// var<uniform> material: CustomMaterial;91/// @group(1) @binding(1)92/// var color_texture: texture_2d<f32>;93/// @group(1) @binding(2)94/// var color_sampler: sampler;95///96/// @fragment97/// fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {98///99/// }100/// ```101pub trait UiMaterial: AsBindGroup + Asset + Clone + Sized {102/// Returns this materials vertex shader. If [`ShaderRef::Default`] is returned, the default UI103/// vertex shader will be used.104fn vertex_shader() -> ShaderRef {105ShaderRef::Default106}107108/// Returns this materials fragment shader. If [`ShaderRef::Default`] is returned, the default109/// UI fragment shader will be used.110fn fragment_shader() -> ShaderRef {111ShaderRef::Default112}113114#[expect(115unused_variables,116reason = "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."117)]118#[inline]119fn specialize(descriptor: &mut RenderPipelineDescriptor, key: UiMaterialKey<Self>) {}120}121122pub struct UiMaterialKey<M: UiMaterial> {123pub hdr: bool,124pub bind_group_data: M::Data,125}126127impl<M: UiMaterial> Eq for UiMaterialKey<M> where M::Data: PartialEq {}128129impl<M: UiMaterial> PartialEq for UiMaterialKey<M>130where131M::Data: PartialEq,132{133fn eq(&self, other: &Self) -> bool {134self.hdr == other.hdr && self.bind_group_data == other.bind_group_data135}136}137138impl<M: UiMaterial> Clone for UiMaterialKey<M>139where140M::Data: Clone,141{142fn clone(&self) -> Self {143Self {144hdr: self.hdr,145bind_group_data: self.bind_group_data.clone(),146}147}148}149150impl<M: UiMaterial> core::hash::Hash for UiMaterialKey<M>151where152M::Data: core::hash::Hash,153{154fn hash<H: core::hash::Hasher>(&self, state: &mut H) {155self.hdr.hash(state);156self.bind_group_data.hash(state);157}158}159160#[derive(161Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq, ExtractComponent, From,162)]163#[reflect(Component, Default)]164#[require(Node)]165pub struct MaterialNode<M: UiMaterial>(pub Handle<M>);166167impl<M: UiMaterial> Default for MaterialNode<M> {168fn default() -> Self {169Self(Handle::default())170}171}172173impl<M: UiMaterial> From<MaterialNode<M>> for AssetId<M> {174fn from(material: MaterialNode<M>) -> Self {175material.id()176}177}178179impl<M: UiMaterial> From<&MaterialNode<M>> for AssetId<M> {180fn from(material: &MaterialNode<M>) -> Self {181material.id()182}183}184185186