shader_material_wesl/
shader_material_wesl.rs1use bevy::{
4 mesh::MeshVertexBufferLayoutRef,
5 pbr::{MaterialPipeline, MaterialPipelineKey},
6 prelude::*,
7 reflect::TypePath,
8 render::render_resource::{
9 AsBindGroup, RenderPipelineDescriptor, SpecializedMeshPipelineError,
10 },
11 shader::{ShaderDefVal, ShaderRef},
12};
13
14const FRAGMENT_SHADER_ASSET_PATH: &str = "shaders/custom_material.wesl";
16
17fn main() {
18 App::new()
19 .add_plugins((
20 DefaultPlugins,
21 MaterialPlugin::<CustomMaterial>::default(),
22 CustomMaterialPlugin,
23 ))
24 .add_systems(Startup, setup)
25 .add_systems(Update, update)
26 .run();
27}
28
29pub struct CustomMaterialPlugin;
31
32#[expect(
34 dead_code,
35 reason = "used to kept a strong handle, shader is referenced by the material"
36)]
37#[derive(Resource)]
38struct UtilityShader(Handle<Shader>);
39
40impl Plugin for CustomMaterialPlugin {
41 fn build(&self, app: &mut App) {
42 let handle = app
43 .world_mut()
44 .resource_mut::<AssetServer>()
45 .load::<Shader>("shaders/util.wesl");
46 app.insert_resource(UtilityShader(handle));
47 }
48}
49
50fn setup(
52 mut commands: Commands,
53 mut meshes: ResMut<Assets<Mesh>>,
54 mut materials: ResMut<Assets<CustomMaterial>>,
55) {
56 commands.spawn((
58 Mesh3d(meshes.add(Cuboid::default())),
59 MeshMaterial3d(materials.add(CustomMaterial {
60 time: Vec4::ZERO,
61 party_mode: false,
62 })),
63 Transform::from_xyz(0.0, 0.5, 0.0),
64 ));
65
66 commands.spawn((
68 Camera3d::default(),
69 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
70 ));
71}
72
73fn update(
74 time: Res<Time>,
75 mut query: Query<(&MeshMaterial3d<CustomMaterial>, &mut Transform)>,
76 mut materials: ResMut<Assets<CustomMaterial>>,
77 keys: Res<ButtonInput<KeyCode>>,
78) {
79 for (material, mut transform) in query.iter_mut() {
80 let material = materials.get_mut(material).unwrap();
81 material.time.x = time.elapsed_secs();
82 if keys.just_pressed(KeyCode::Space) {
83 material.party_mode = !material.party_mode;
84 }
85
86 if material.party_mode {
87 transform.rotate(Quat::from_rotation_y(0.005));
88 }
89 }
90}
91
92#[derive(Asset, TypePath, AsBindGroup, Clone)]
94#[bind_group_data(CustomMaterialKey)]
95struct CustomMaterial {
96 #[uniform(0)]
98 time: Vec4,
99 party_mode: bool,
100}
101
102#[repr(C)]
103#[derive(Eq, PartialEq, Hash, Copy, Clone)]
104struct CustomMaterialKey {
105 party_mode: bool,
106}
107
108impl From<&CustomMaterial> for CustomMaterialKey {
109 fn from(material: &CustomMaterial) -> Self {
110 Self {
111 party_mode: material.party_mode,
112 }
113 }
114}
115
116impl Material for CustomMaterial {
117 fn fragment_shader() -> ShaderRef {
118 FRAGMENT_SHADER_ASSET_PATH.into()
119 }
120
121 fn specialize(
122 _pipeline: &MaterialPipeline,
123 descriptor: &mut RenderPipelineDescriptor,
124 _layout: &MeshVertexBufferLayoutRef,
125 key: MaterialPipelineKey<Self>,
126 ) -> Result<(), SpecializedMeshPipelineError> {
127 let fragment = descriptor.fragment.as_mut().unwrap();
128 fragment.shader_defs.push(ShaderDefVal::Bool(
129 "PARTY_MODE".to_string(),
130 key.bind_group_data.party_mode,
131 ));
132 Ok(())
133 }
134}