Skip to main content

bevy_pbr/
extended_material.rs

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
28/// A subset of the `Material` trait for defining extensions to a base `Material`, such as the builtin `StandardMaterial`.
29///
30/// A user type implementing the trait should be used as the `E` generic param in an `ExtendedMaterial` struct.
31pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
32    /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the base material mesh vertex shader
33    /// will be used.
34    fn vertex_shader() -> ShaderRef {
35        ShaderRef::Default
36    }
37
38    /// Returns this material's fragment shader. If [`ShaderRef::Default`] is returned, the base material mesh fragment shader
39    /// will be used.
40    fn fragment_shader() -> ShaderRef {
41        ShaderRef::Default
42    }
43
44    // Returns this material’s AlphaMode. If None is returned, the base material alpha mode will be used.
45    fn alpha_mode() -> Option<AlphaMode> {
46        None
47    }
48
49    /// Controls if the prepass is enabled for the Material.
50    /// For more information about what a prepass is, see the [`bevy_core_pipeline::prepass`] docs.
51    #[inline]
52    fn enable_prepass() -> bool {
53        true
54    }
55
56    /// Controls if shadows are enabled for the Material.
57    #[inline]
58    fn enable_shadows() -> bool {
59        true
60    }
61
62    /// Controls whether order independent transparency is enabled for the Material.
63    /// This is ignored if the camera does not have [`bevy_core_pipeline::oit::OrderIndependentTransparencySettings`]
64    /// or if [`Self::alpha_mode`] is not supported by OIT.
65    #[inline]
66    fn enable_oit() -> bool {
67        true
68    }
69
70    /// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the base material prepass vertex shader
71    /// will be used.
72    fn prepass_vertex_shader() -> ShaderRef {
73        ShaderRef::Default
74    }
75
76    /// Returns this material's prepass fragment shader. If [`ShaderRef::Default`] is returned, the base material prepass fragment shader
77    /// will be used.
78    fn prepass_fragment_shader() -> ShaderRef {
79        ShaderRef::Default
80    }
81
82    /// Returns this material's deferred vertex shader. If [`ShaderRef::Default`] is returned, the base material deferred vertex shader
83    /// will be used.
84    fn deferred_vertex_shader() -> ShaderRef {
85        ShaderRef::Default
86    }
87
88    /// Returns this material's prepass fragment shader. If [`ShaderRef::Default`] is returned, the base material deferred fragment shader
89    /// will be used.
90    fn deferred_fragment_shader() -> ShaderRef {
91        ShaderRef::Default
92    }
93
94    /// Returns this material's [`crate::meshlet::MeshletMesh`] fragment shader. If [`ShaderRef::Default`] is returned,
95    /// the default meshlet mesh fragment shader will be used.
96    #[cfg(feature = "meshlet")]
97    fn meshlet_mesh_fragment_shader() -> ShaderRef {
98        ShaderRef::Default
99    }
100
101    /// Returns this material's [`crate::meshlet::MeshletMesh`] prepass fragment shader. If [`ShaderRef::Default`] is returned,
102    /// the default meshlet mesh prepass fragment shader will be used.
103    #[cfg(feature = "meshlet")]
104    fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
105        ShaderRef::Default
106    }
107
108    /// Returns this material's [`crate::meshlet::MeshletMesh`] deferred fragment shader. If [`ShaderRef::Default`] is returned,
109    /// the default meshlet mesh deferred fragment shader will be used.
110    #[cfg(feature = "meshlet")]
111    fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
112        ShaderRef::Default
113    }
114
115    /// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
116    /// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
117    /// Specialization for the base material is applied before this function is called.
118    #[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/// A material that extends a base [`Material`] with additional shaders and data.
134///
135/// The data from both materials will be combined and made available to the shader
136/// so that shader functions built for the base material (and referencing the base material
137/// bindings) will work as expected, and custom alterations based on custom data can also be used.
138///
139/// If the extension `E` returns a non-default result from `vertex_shader()` it will be used in place of the base
140/// material's vertex shader.
141///
142/// If the extension `E` returns a non-default result from `fragment_shader()` it will be used in place of the base
143/// fragment shader.
144///
145/// When used with `StandardMaterial` as the base, all the standard material fields are
146/// present, so the `pbr_fragment` shader functions can be called from the extension shader (see
147/// the `extended_material` example).
148#[derive(impl<B: Material, E: MaterialExtension> bevy_asset::Asset for
    ExtendedMaterial<B, E> {}
impl<B: Material, E: MaterialExtension> bevy_asset::VisitAssetDependencies for
    ExtendedMaterial<B, E> {
    fn visit_dependencies(&self,
        visit: &mut impl ::core::ops::FnMut(bevy_asset::UntypedAssetId)) {}
}Asset, #[automatically_derived]
impl<B: ::core::clone::Clone + Material, E: ::core::clone::Clone +
    MaterialExtension> ::core::clone::Clone for ExtendedMaterial<B, E> {
    #[inline]
    fn clone(&self) -> Self {
        Self {
            base: ::core::clone::Clone::clone(&self.base),
            extension: ::core::clone::Clone::clone(&self.extension),
        }
    }
}Clone, #[automatically_derived]
impl<B: ::core::fmt::Debug + Material, E: ::core::fmt::Debug +
    MaterialExtension> ::core::fmt::Debug for ExtendedMaterial<B, E> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "ExtendedMaterial", "base", &self.base, "extension",
            &&self.extension)
    }
}Debug, const _: () =
    {
        impl<B: Material, E: MaterialExtension>
            bevy_reflect::GetTypeRegistration for ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {
                <B as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
                <E as
                        bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
            }
        }
        impl<B: Material, E: MaterialExtension> bevy_reflect::Typed for
            ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::GenericTypeInfoCell =
                    bevy_reflect::utility::GenericTypeInfoCell::new();
                CELL.get_or_insert::<Self,
                    _>(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<B>("base"),
                                                bevy_reflect::NamedField::new::<E>("extension")]))
                        })
            }
        }
        impl<B: Material, E: MaterialExtension> bevy_reflect::Reflect for
            ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        impl<B: Material, E: MaterialExtension> bevy_reflect::structs::Struct
            for ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name {
                    "base" => ::core::option::Option::Some(&self.base),
                    "extension" =>
                        ::core::option::Option::Some(&self.extension),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name {
                    "base" => ::core::option::Option::Some(&mut self.base),
                    "extension" =>
                        ::core::option::Option::Some(&mut self.extension),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&self.base),
                    1usize => ::core::option::Option::Some(&self.extension),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index {
                    0usize => ::core::option::Option::Some(&mut self.base),
                    1usize => ::core::option::Option::Some(&mut self.extension),
                    _ => ::core::option::Option::None,
                }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index {
                    0usize => ::core::option::Option::Some("base"),
                    1usize => ::core::option::Option::Some("extension"),
                    _ => ::core::option::Option::None,
                }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name {
                    "base" => ::core::option::Option::Some(0usize),
                    "extension" => ::core::option::Option::Some(1usize),
                    _ => ::core::option::Option::None,
                }
            }
            fn field_len(&self) -> usize { 2usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                dynamic.insert_boxed("base",
                    bevy_reflect::PartialReflect::to_dynamic(&self.base)?);
                dynamic.insert_boxed("extension",
                    bevy_reflect::PartialReflect::to_dynamic(&self.extension)?);
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl<B: Material, E: MaterialExtension> bevy_reflect::PartialReflect
            for ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            #[inline]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
            }
        }
        impl<B: Material, E: MaterialExtension> bevy_reflect::FromReflect for
            ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::FromReflect +
            bevy_reflect::TypePath + bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection,
            E: bevy_reflect::FromReflect + bevy_reflect::TypePath +
            bevy_reflect::MaybeTyped +
            bevy_reflect::__macro_exports::RegisterForReflection {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let __this =
                        Self {
                            base: <B as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "base")?)?,
                            extension: <E as
                                        bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
                                            "extension")?)?,
                        };
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };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
169// We don't use the `TypePath` derive here due to a bug where `#[reflect(type_path = false)]`
170// causes the `TypePath` derive to not generate an implementation.
171const _: () =
    {
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl<B: Material, E: MaterialExtension> bevy_reflect::TypePath for
            ExtendedMaterial<B, E> where
            ExtendedMaterial<B, E>: ::core::any::Any + ::core::marker::Send +
            ::core::marker::Sync, B: bevy_reflect::TypePath,
            E: bevy_reflect::TypePath {
            fn type_path() -> &'static str {
                static CELL: bevy_reflect::utility::GenericTypePathCell =
                    bevy_reflect::utility::GenericTypePathCell::new();
                CELL.get_or_insert::<Self,
                    _>(#[inline(never)] ||
                        {
                            ::core::ops::Add::<&str>::add(::core::ops::Add::<&str>::add(bevy_reflect::__macro_exports::alloc_utils::ToString::to_string("bevy_pbr::extended_material::ExtendedMaterial<"),
                                    &::core::ops::Add::<&str>::add(::core::ops::Add::<&str>::add(bevy_reflect::__macro_exports::alloc_utils::ToString::to_string(<B
                                                            as bevy_reflect::TypePath>::type_path()), ", "),
                                            <E as bevy_reflect::TypePath>::type_path())), ">")
                        })
            }
            fn short_type_path() -> &'static str {
                static CELL: bevy_reflect::utility::GenericTypePathCell =
                    bevy_reflect::utility::GenericTypePathCell::new();
                CELL.get_or_insert::<Self,
                    _>(#[inline(never)] ||
                        {
                            ::core::ops::Add::<&str>::add(::core::ops::Add::<&str>::add(bevy_reflect::__macro_exports::alloc_utils::ToString::to_string("ExtendedMaterial<"),
                                    &::core::ops::Add::<&str>::add(::core::ops::Add::<&str>::add(bevy_reflect::__macro_exports::alloc_utils::ToString::to_string(<B
                                                            as bevy_reflect::TypePath>::short_type_path()), ", "),
                                            <E as bevy_reflect::TypePath>::short_type_path())), ">")
                        })
            }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("ExtendedMaterial")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_pbr")
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_pbr::extended_material")
            }
        }
    };impl_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        // Call the base material's specialize function
332        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        // Call the extended material's specialize function afterwards
339        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>;