Skip to content

Commit 55f82d3

Browse files
committed
Support AsBindGroup for 2d materials as well (#5312)
Port changes made to Material in #5053 to Material2d as well. This is more or less an exact copy of the implementation in bevy_pbr; I simply pretended the API existed, then copied stuff over until it started building and the shapes example was working again. # Objective The changes in #5053 makes it possible to add custom materials with a lot less boiler plate. However, the implementation isn't shared with Material 2d as it's a kind of fork of the bevy_pbr version. It should be possible to use AsBindGroup on the 2d version as well. ## Solution This makes the same kind of changes in Material2d in bevy_sprite. This makes the following work: ```rust //! Draws a circular purple bevy in the middle of the screen using a custom shader use bevy::{ prelude::*, reflect::TypeUuid, render::render_resource::{AsBindGroup, ShaderRef}, sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle}, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(Material2dPlugin::<CustomMaterial>::default()) .add_startup_system(setup) .run(); } /// set up a simple 2D scene fn setup( mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<CustomMaterial>>, asset_server: Res<AssetServer>, ) { commands.spawn_bundle(MaterialMesh2dBundle { mesh: meshes.add(shape::Circle::new(50.).into()).into(), material: materials.add(CustomMaterial { color: Color::PURPLE, color_texture: Some(asset_server.load("branding/icon.png")), }), transform: Transform::from_translation(Vec3::new(-100., 0., 0.)), ..default() }); commands.spawn_bundle(Camera2dBundle::default()); } /// The Material2d trait is very configurable, but comes with sensible defaults for all methods. /// You only need to implement functions for features that need non-default behavior. See the Material api docs for details! impl Material2d for CustomMaterial { fn fragment_shader() -> ShaderRef { "shaders/custom_material.wgsl".into() } } // This is the struct that will be passed to your shader #[derive(AsBindGroup, TypeUuid, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] color: Color, #[texture(1)] #[sampler(2)] color_texture: Option<Handle<Image>>, } ```
1 parent 40d4992 commit 55f82d3

File tree

3 files changed

+340
-391
lines changed

3 files changed

+340
-391
lines changed

crates/bevy_sprite/src/mesh2d/color_material.rs

+16-133
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
use bevy_app::{App, Plugin};
2-
use bevy_asset::{load_internal_asset, AssetServer, Assets, Handle, HandleUntyped};
3-
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
2+
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
43
use bevy_math::Vec4;
54
use bevy_reflect::TypeUuid;
65
use bevy_render::{
7-
color::Color,
8-
prelude::Shader,
9-
render_asset::{PrepareAssetError, RenderAsset, RenderAssets},
10-
render_resource::*,
11-
renderer::RenderDevice,
12-
texture::Image,
6+
color::Color, prelude::Shader, render_asset::RenderAssets, render_resource::*, texture::Image,
137
};
148

15-
use crate::{Material2d, Material2dPipeline, Material2dPlugin, MaterialMesh2dBundle};
9+
use crate::{Material2d, Material2dPlugin, MaterialMesh2dBundle};
1610

1711
pub const COLOR_MATERIAL_SHADER_HANDLE: HandleUntyped =
1812
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3253086872234592509);
@@ -44,10 +38,13 @@ impl Plugin for ColorMaterialPlugin {
4438
}
4539

4640
/// A [2d material](Material2d) that renders [2d meshes](crate::Mesh2dHandle) with a texture tinted by a uniform color
47-
#[derive(Debug, Clone, TypeUuid)]
41+
#[derive(AsBindGroup, Debug, Clone, TypeUuid)]
4842
#[uuid = "e228a544-e3ca-4e1e-bb9d-4d8bc1ad8c19"]
43+
#[uniform(0, ColorMaterialUniform)]
4944
pub struct ColorMaterial {
5045
pub color: Color,
46+
#[texture(1)]
47+
#[sampler(2)]
5148
pub texture: Option<Handle<Image>>,
5249
}
5350

@@ -90,142 +87,28 @@ bitflags::bitflags! {
9087

9188
/// The GPU representation of the uniform data of a [`ColorMaterial`].
9289
#[derive(Clone, Default, ShaderType)]
93-
pub struct ColorMaterialUniformData {
90+
pub struct ColorMaterialUniform {
9491
pub color: Vec4,
9592
pub flags: u32,
9693
}
9794

98-
/// The GPU representation of a [`ColorMaterial`].
99-
#[derive(Debug, Clone)]
100-
pub struct GpuColorMaterial {
101-
/// A buffer containing the [`ColorMaterialUniformData`] of the material.
102-
pub buffer: Buffer,
103-
/// The bind group specifying how the [`ColorMaterialUniformData`] and
104-
/// the texture of the material are bound.
105-
pub bind_group: BindGroup,
106-
pub flags: ColorMaterialFlags,
107-
pub texture: Option<Handle<Image>>,
108-
}
109-
110-
impl RenderAsset for ColorMaterial {
111-
type ExtractedAsset = ColorMaterial;
112-
type PreparedAsset = GpuColorMaterial;
113-
type Param = (
114-
SRes<RenderDevice>,
115-
SRes<Material2dPipeline<ColorMaterial>>,
116-
SRes<RenderAssets<Image>>,
117-
);
118-
119-
fn extract_asset(&self) -> Self::ExtractedAsset {
120-
self.clone()
121-
}
122-
123-
fn prepare_asset(
124-
material: Self::ExtractedAsset,
125-
(render_device, color_pipeline, gpu_images): &mut SystemParamItem<Self::Param>,
126-
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
127-
let (texture_view, sampler) = if let Some(result) = color_pipeline
128-
.mesh2d_pipeline
129-
.get_image_texture(gpu_images, &material.texture)
130-
{
131-
result
132-
} else {
133-
return Err(PrepareAssetError::RetryNextUpdate(material));
134-
};
135-
95+
impl AsBindGroupShaderType<ColorMaterialUniform> for ColorMaterial {
96+
fn as_bind_group_shader_type(&self, _images: &RenderAssets<Image>) -> ColorMaterialUniform {
13697
let mut flags = ColorMaterialFlags::NONE;
137-
if material.texture.is_some() {
98+
if self.texture.is_some() {
13899
flags |= ColorMaterialFlags::TEXTURE;
139100
}
140101

141-
let value = ColorMaterialUniformData {
142-
color: material.color.as_linear_rgba_f32().into(),
102+
ColorMaterialUniform {
103+
color: self.color.as_linear_rgba_f32().into(),
143104
flags: flags.bits(),
144-
};
145-
146-
let byte_buffer = [0u8; ColorMaterialUniformData::SHADER_SIZE.get() as usize];
147-
let mut buffer = encase::UniformBuffer::new(byte_buffer);
148-
buffer.write(&value).unwrap();
149-
150-
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
151-
label: Some("color_material_uniform_buffer"),
152-
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
153-
contents: buffer.as_ref(),
154-
});
155-
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
156-
entries: &[
157-
BindGroupEntry {
158-
binding: 0,
159-
resource: buffer.as_entire_binding(),
160-
},
161-
BindGroupEntry {
162-
binding: 1,
163-
resource: BindingResource::TextureView(texture_view),
164-
},
165-
BindGroupEntry {
166-
binding: 2,
167-
resource: BindingResource::Sampler(sampler),
168-
},
169-
],
170-
label: Some("color_material_bind_group"),
171-
layout: &color_pipeline.material2d_layout,
172-
});
173-
174-
Ok(GpuColorMaterial {
175-
buffer,
176-
bind_group,
177-
flags,
178-
texture: material.texture,
179-
})
105+
}
180106
}
181107
}
182108

183109
impl Material2d for ColorMaterial {
184-
fn fragment_shader(_asset_server: &AssetServer) -> Option<Handle<Shader>> {
185-
Some(COLOR_MATERIAL_SHADER_HANDLE.typed())
186-
}
187-
188-
#[inline]
189-
fn bind_group(render_asset: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup {
190-
&render_asset.bind_group
191-
}
192-
193-
fn bind_group_layout(
194-
render_device: &RenderDevice,
195-
) -> bevy_render::render_resource::BindGroupLayout {
196-
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
197-
entries: &[
198-
BindGroupLayoutEntry {
199-
binding: 0,
200-
visibility: ShaderStages::FRAGMENT,
201-
ty: BindingType::Buffer {
202-
ty: BufferBindingType::Uniform,
203-
has_dynamic_offset: false,
204-
min_binding_size: Some(ColorMaterialUniformData::min_size()),
205-
},
206-
count: None,
207-
},
208-
// Texture
209-
BindGroupLayoutEntry {
210-
binding: 1,
211-
visibility: ShaderStages::FRAGMENT,
212-
ty: BindingType::Texture {
213-
multisampled: false,
214-
sample_type: TextureSampleType::Float { filterable: true },
215-
view_dimension: TextureViewDimension::D2,
216-
},
217-
count: None,
218-
},
219-
// Texture Sampler
220-
BindGroupLayoutEntry {
221-
binding: 2,
222-
visibility: ShaderStages::FRAGMENT,
223-
ty: BindingType::Sampler(SamplerBindingType::Filtering),
224-
count: None,
225-
},
226-
],
227-
label: Some("color_material_layout"),
228-
})
110+
fn fragment_shader() -> ShaderRef {
111+
COLOR_MATERIAL_SHADER_HANDLE.typed().into()
229112
}
230113
}
231114

0 commit comments

Comments
 (0)