bevy_sprite_render/mesh2d/
color_material.rs1use crate::{AlphaMode2d, Material2d, Material2dPlugin};
2use bevy_app::{App, Plugin};
3use bevy_asset::{embedded_asset, embedded_path, Asset, AssetApp, AssetPath, Assets, Handle};
4use bevy_color::{Alpha, Color, ColorToComponents, LinearRgba};
5use bevy_image::Image;
6use bevy_math::{Affine2, Mat3, Vec4};
7use bevy_reflect::prelude::*;
8use bevy_render::{render_asset::RenderAssets, render_resource::*, texture::GpuImage};
9use bevy_shader::ShaderRef;
10
11#[derive(Default)]
12pub struct ColorMaterialPlugin;
13
14impl Plugin for ColorMaterialPlugin {
15 fn build(&self, app: &mut App) {
16 embedded_asset!(app, "color_material.wgsl");
17
18 app.add_plugins(Material2dPlugin::<ColorMaterial>::default())
19 .register_asset_reflect::<ColorMaterial>();
20
21 app.world_mut()
23 .resource_mut::<Assets<ColorMaterial>>()
24 .insert(
25 &Handle::<ColorMaterial>::default(),
26 ColorMaterial {
27 color: Color::srgb(1.0, 0.0, 1.0),
28 ..Default::default()
29 },
30 )
31 .unwrap();
32 }
33}
34
35#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
37#[reflect(Default, Debug, Clone)]
38#[uniform(0, ColorMaterialUniform)]
39pub struct ColorMaterial {
40 pub color: Color,
41 pub alpha_mode: AlphaMode2d,
42 pub uv_transform: Affine2,
43 #[texture(1)]
44 #[sampler(2)]
45 pub texture: Option<Handle<Image>>,
46}
47
48impl ColorMaterial {
49 pub fn from_color(color: impl Into<Color>) -> Self {
51 Self::from(color.into())
52 }
53}
54
55impl Default for ColorMaterial {
56 fn default() -> Self {
57 ColorMaterial {
58 color: Color::WHITE,
59 uv_transform: Affine2::default(),
60 texture: None,
61 alpha_mode: AlphaMode2d::Blend,
63 }
64 }
65}
66
67impl From<Color> for ColorMaterial {
68 fn from(color: Color) -> Self {
69 ColorMaterial {
70 color,
71 alpha_mode: if color.alpha() < 1.0 {
72 AlphaMode2d::Blend
73 } else {
74 AlphaMode2d::Opaque
75 },
76 ..Default::default()
77 }
78 }
79}
80
81impl From<Handle<Image>> for ColorMaterial {
82 fn from(texture: Handle<Image>) -> Self {
83 ColorMaterial {
84 texture: Some(texture),
85 ..Default::default()
86 }
87 }
88}
89
90bitflags::bitflags! {
92 #[repr(transparent)]
93 pub struct ColorMaterialFlags: u32 {
94 const TEXTURE = 1 << 0;
95 const ALPHA_MODE_RESERVED_BITS = Self::ALPHA_MODE_MASK_BITS << Self::ALPHA_MODE_SHIFT_BITS;
99 const ALPHA_MODE_OPAQUE = 0 << Self::ALPHA_MODE_SHIFT_BITS;
100 const ALPHA_MODE_MASK = 1 << Self::ALPHA_MODE_SHIFT_BITS;
101 const ALPHA_MODE_BLEND = 2 << Self::ALPHA_MODE_SHIFT_BITS;
102 const NONE = 0;
103 const UNINITIALIZED = 0xFFFF;
104 }
105}
106
107impl ColorMaterialFlags {
108 const ALPHA_MODE_MASK_BITS: u32 = 0b11;
109 const ALPHA_MODE_SHIFT_BITS: u32 = 32 - Self::ALPHA_MODE_MASK_BITS.count_ones();
110}
111
112#[derive(Clone, Default, ShaderType)]
114pub struct ColorMaterialUniform {
115 pub color: Vec4,
116 pub uv_transform: Mat3,
117 pub flags: u32,
118 pub alpha_cutoff: f32,
119}
120
121impl AsBindGroupShaderType<ColorMaterialUniform> for ColorMaterial {
122 fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> ColorMaterialUniform {
123 let mut flags = ColorMaterialFlags::NONE;
124 if self.texture.is_some() {
125 flags |= ColorMaterialFlags::TEXTURE;
126 }
127
128 let mut alpha_cutoff = 0.5;
130 match self.alpha_mode {
131 AlphaMode2d::Opaque => flags |= ColorMaterialFlags::ALPHA_MODE_OPAQUE,
132 AlphaMode2d::Mask(c) => {
133 alpha_cutoff = c;
134 flags |= ColorMaterialFlags::ALPHA_MODE_MASK;
135 }
136 AlphaMode2d::Blend => flags |= ColorMaterialFlags::ALPHA_MODE_BLEND,
137 };
138 ColorMaterialUniform {
139 color: LinearRgba::from(self.color).to_f32_array().into(),
140 uv_transform: self.uv_transform.into(),
141 flags: flags.bits(),
142 alpha_cutoff,
143 }
144 }
145}
146
147impl Material2d for ColorMaterial {
148 fn fragment_shader() -> ShaderRef {
149 ShaderRef::Path(
150 AssetPath::from_path_buf(embedded_path!("color_material.wgsl")).with_source("embedded"),
151 )
152 }
153
154 fn alpha_mode(&self) -> AlphaMode2d {
155 self.alpha_mode
156 }
157}