1use bevy_asset::Asset;
2use bevy_ecs::system::SystemParamItem;
3use bevy_material::{AlphaMode, OpaqueRendererMethod};
4use bevy_mesh::MeshVertexBufferLayoutRef;
5use bevy_reflect::{impl_type_path, Reflect};
6use bevy_render::{
7 combined_bind_group as cbg,
8 render_resource::{
9 AsBindGroup, AsBindGroupError, BindGroupBuilder, BindGroupLayout, BindGroupLayoutEntry,
10 BindlessDescriptor, BindlessSlabResourceLimit, RenderPipelineDescriptor,
11 SpecializedMeshPipelineError,
12 },
13 renderer::RenderDevice,
14};
15use bevy_shader::ShaderRef;
16
17use crate::{Material, MaterialPipeline, MaterialPipelineKey, MeshPipeline, MeshPipelineKey};
18
19pub struct MaterialExtensionPipeline {
20 pub mesh_pipeline: MeshPipeline,
21}
22
23pub struct MaterialExtensionKey<E: MaterialExtension> {
24 pub mesh_key: MeshPipelineKey,
25 pub bind_group_data: E::Data,
26}
27
28pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
32 fn vertex_shader() -> ShaderRef {
35 ShaderRef::Default
36 }
37
38 fn fragment_shader() -> ShaderRef {
41 ShaderRef::Default
42 }
43
44 fn alpha_mode() -> Option<AlphaMode> {
46 None
47 }
48
49 #[inline]
52 fn enable_prepass() -> bool {
53 true
54 }
55
56 #[inline]
58 fn enable_shadows() -> bool {
59 true
60 }
61
62 #[inline]
66 fn enable_oit() -> bool {
67 true
68 }
69
70 fn prepass_vertex_shader() -> ShaderRef {
73 ShaderRef::Default
74 }
75
76 fn prepass_fragment_shader() -> ShaderRef {
79 ShaderRef::Default
80 }
81
82 fn deferred_vertex_shader() -> ShaderRef {
85 ShaderRef::Default
86 }
87
88 fn deferred_fragment_shader() -> ShaderRef {
91 ShaderRef::Default
92 }
93
94 #[cfg(feature = "meshlet")]
97 fn meshlet_mesh_fragment_shader() -> ShaderRef {
98 ShaderRef::Default
99 }
100
101 #[cfg(feature = "meshlet")]
104 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
105 ShaderRef::Default
106 }
107
108 #[cfg(feature = "meshlet")]
111 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
112 ShaderRef::Default
113 }
114
115 #[expect(
119 unused_variables,
120 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."
121 )]
122 #[inline]
123 fn specialize(
124 pipeline: &MaterialExtensionPipeline,
125 descriptor: &mut RenderPipelineDescriptor,
126 layout: &MeshVertexBufferLayoutRef,
127 key: MaterialExtensionKey<Self>,
128 ) -> Result<(), SpecializedMeshPipelineError> {
129 Ok(())
130 }
131}
132
133#[derive(Asset, Clone, Debug, Reflect)]
149#[reflect(type_path = false)]
150#[reflect(Clone)]
151pub struct ExtendedMaterial<B: Material, E: MaterialExtension> {
152 pub base: B,
153 pub extension: E,
154}
155
156impl<B, E> Default for ExtendedMaterial<B, E>
157where
158 B: Material + Default,
159 E: MaterialExtension + Default,
160{
161 fn default() -> Self {
162 Self {
163 base: B::default(),
164 extension: E::default(),
165 }
166 }
167}
168
169impl_type_path!((in bevy_pbr::extended_material) ExtendedMaterial<B: Material, E: MaterialExtension>);
172
173impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
174 type Data = cbg::CombinedBindGroupData<B::Data, E::Data>;
175 type Param = (B::Param, E::Param);
176
177 fn bindless_slot_count() -> Option<BindlessSlabResourceLimit> {
178 cbg::bindless_slot_count::<B, E>()
179 }
180
181 fn bindless_supported(render_device: &RenderDevice) -> bool {
182 B::bindless_supported(render_device) && E::bindless_supported(render_device)
183 }
184
185 fn label() -> &'static str {
186 E::label()
187 }
188
189 fn bind_group_data(&self) -> Self::Data {
190 cbg::bind_group_data(&self.base, &self.extension)
191 }
192
193 fn build_bind_group(
194 &self,
195 layout: &BindGroupLayout,
196 render_device: &RenderDevice,
197 param: &mut SystemParamItem<'_, '_, Self::Param>,
198 force_no_bindless: bool,
199 output: &mut BindGroupBuilder,
200 ) -> Result<(), AsBindGroupError> {
201 cbg::build_bind_group(
202 &self.base,
203 &self.extension,
204 layout,
205 render_device,
206 param,
207 force_no_bindless,
208 output,
209 )
210 }
211
212 fn bind_group_layout_entries(
213 render_device: &RenderDevice,
214 force_no_bindless: bool,
215 ) -> Vec<BindGroupLayoutEntry>
216 where
217 Self: Sized,
218 {
219 cbg::bind_group_layout_entries::<B, E>(render_device, force_no_bindless)
220 }
221
222 fn bindless_descriptor() -> Option<BindlessDescriptor> {
223 cbg::bindless_descriptor::<B, E>()
224 }
225}
226
227impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
228 fn vertex_shader() -> ShaderRef {
229 match E::vertex_shader() {
230 ShaderRef::Default => B::vertex_shader(),
231 specified => specified,
232 }
233 }
234
235 fn fragment_shader() -> ShaderRef {
236 match E::fragment_shader() {
237 ShaderRef::Default => B::fragment_shader(),
238 specified => specified,
239 }
240 }
241
242 fn alpha_mode(&self) -> AlphaMode {
243 match E::alpha_mode() {
244 Some(specified) => specified,
245 None => B::alpha_mode(&self.base),
246 }
247 }
248
249 fn opaque_render_method(&self) -> OpaqueRendererMethod {
250 B::opaque_render_method(&self.base)
251 }
252
253 fn depth_bias(&self) -> f32 {
254 B::depth_bias(&self.base)
255 }
256
257 fn reads_view_transmission_texture(&self) -> bool {
258 B::reads_view_transmission_texture(&self.base)
259 }
260
261 fn enable_prepass() -> bool {
262 E::enable_prepass()
263 }
264
265 fn enable_shadows() -> bool {
266 E::enable_shadows()
267 }
268
269 fn enable_oit() -> bool {
270 E::enable_oit()
271 }
272
273 fn prepass_vertex_shader() -> ShaderRef {
274 match E::prepass_vertex_shader() {
275 ShaderRef::Default => B::prepass_vertex_shader(),
276 specified => specified,
277 }
278 }
279
280 fn prepass_fragment_shader() -> ShaderRef {
281 match E::prepass_fragment_shader() {
282 ShaderRef::Default => B::prepass_fragment_shader(),
283 specified => specified,
284 }
285 }
286
287 fn deferred_vertex_shader() -> ShaderRef {
288 match E::deferred_vertex_shader() {
289 ShaderRef::Default => B::deferred_vertex_shader(),
290 specified => specified,
291 }
292 }
293
294 fn deferred_fragment_shader() -> ShaderRef {
295 match E::deferred_fragment_shader() {
296 ShaderRef::Default => B::deferred_fragment_shader(),
297 specified => specified,
298 }
299 }
300
301 #[cfg(feature = "meshlet")]
302 fn meshlet_mesh_fragment_shader() -> ShaderRef {
303 match E::meshlet_mesh_fragment_shader() {
304 ShaderRef::Default => B::meshlet_mesh_fragment_shader(),
305 specified => specified,
306 }
307 }
308
309 #[cfg(feature = "meshlet")]
310 fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
311 match E::meshlet_mesh_prepass_fragment_shader() {
312 ShaderRef::Default => B::meshlet_mesh_prepass_fragment_shader(),
313 specified => specified,
314 }
315 }
316
317 #[cfg(feature = "meshlet")]
318 fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
319 match E::meshlet_mesh_deferred_fragment_shader() {
320 ShaderRef::Default => B::meshlet_mesh_deferred_fragment_shader(),
321 specified => specified,
322 }
323 }
324
325 fn specialize(
326 pipeline: &MaterialPipeline,
327 descriptor: &mut RenderPipelineDescriptor,
328 layout: &MeshVertexBufferLayoutRef,
329 key: MaterialPipelineKey<Self>,
330 ) -> Result<(), SpecializedMeshPipelineError> {
331 let base_key = MaterialPipelineKey::<B> {
333 mesh_key: key.mesh_key,
334 bind_group_data: key.bind_group_data.base,
335 };
336 B::specialize(pipeline, descriptor, layout, base_key)?;
337
338 E::specialize(
340 &MaterialExtensionPipeline {
341 mesh_pipeline: pipeline.mesh_pipeline.clone(),
342 },
343 descriptor,
344 layout,
345 MaterialExtensionKey {
346 mesh_key: key.mesh_key,
347 bind_group_data: key.bind_group_data.extension,
348 },
349 )
350 }
351}
352
353#[deprecated = "Use `bevy_render::combined_bind_group::CombinedBindGroupData` instead"]
354pub type MaterialExtensionBindGroupData<B, E> = cbg::CombinedBindGroupData<B, E>;