use bevy_asset::{Asset, Handle};
use bevy_reflect::{impl_type_path, Reflect};
use bevy_render::{
mesh::MeshVertexBufferLayout,
render_asset::RenderAssets,
render_resource::{
AsBindGroup, AsBindGroupError, BindGroupLayout, RenderPipelineDescriptor, Shader,
ShaderRef, SpecializedMeshPipelineError, UnpreparedBindGroup,
},
renderer::RenderDevice,
texture::{FallbackImage, Image},
};
use crate::{Material, MaterialPipeline, MaterialPipelineKey, MeshPipeline, MeshPipelineKey};
pub struct MaterialExtensionPipeline {
pub mesh_pipeline: MeshPipeline,
pub material_layout: BindGroupLayout,
pub vertex_shader: Option<Handle<Shader>>,
pub fragment_shader: Option<Handle<Shader>>,
}
pub struct MaterialExtensionKey<E: MaterialExtension> {
pub mesh_key: MeshPipelineKey,
pub bind_group_data: E::Data,
}
pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
fn vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn fragment_shader() -> ShaderRef {
ShaderRef::Default
}
fn prepass_vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn prepass_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
fn deferred_vertex_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
fn deferred_fragment_shader() -> ShaderRef {
ShaderRef::Default
}
#[allow(unused_variables)]
#[inline]
fn specialize(
pipeline: &MaterialExtensionPipeline,
descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout,
key: MaterialExtensionKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
Ok(())
}
}
#[derive(Asset, Clone, Reflect)]
#[reflect(type_path = false)]
pub struct ExtendedMaterial<B: Material, E: MaterialExtension> {
pub base: B,
pub extension: E,
}
impl_type_path!((in bevy_pbr::extended_material) ExtendedMaterial<B: Material, E: MaterialExtension>);
impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
type Data = (<B as AsBindGroup>::Data, <E as AsBindGroup>::Data);
fn unprepared_bind_group(
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
fallback_image: &FallbackImage,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
let UnpreparedBindGroup {
mut bindings,
data: base_data,
} = B::unprepared_bind_group(&self.base, layout, render_device, images, fallback_image)?;
let extended_bindgroup = E::unprepared_bind_group(
&self.extension,
layout,
render_device,
images,
fallback_image,
)?;
bindings.extend(extended_bindgroup.bindings);
Ok(UnpreparedBindGroup {
bindings,
data: (base_data, extended_bindgroup.data),
})
}
fn bind_group_layout_entries(
render_device: &RenderDevice,
) -> Vec<bevy_render::render_resource::BindGroupLayoutEntry>
where
Self: Sized,
{
let mut entries = B::bind_group_layout_entries(render_device);
entries.extend(E::bind_group_layout_entries(render_device));
entries
}
}
impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
fn vertex_shader() -> ShaderRef {
match E::vertex_shader() {
ShaderRef::Default => B::vertex_shader(),
specified => specified,
}
}
fn fragment_shader() -> ShaderRef {
match E::fragment_shader() {
ShaderRef::Default => B::fragment_shader(),
specified => specified,
}
}
fn alpha_mode(&self) -> crate::AlphaMode {
B::alpha_mode(&self.base)
}
fn opaque_render_method(&self) -> crate::OpaqueRendererMethod {
B::opaque_render_method(&self.base)
}
fn depth_bias(&self) -> f32 {
B::depth_bias(&self.base)
}
fn reads_view_transmission_texture(&self) -> bool {
B::reads_view_transmission_texture(&self.base)
}
fn prepass_vertex_shader() -> ShaderRef {
match E::prepass_vertex_shader() {
ShaderRef::Default => B::prepass_vertex_shader(),
specified => specified,
}
}
fn prepass_fragment_shader() -> ShaderRef {
match E::prepass_fragment_shader() {
ShaderRef::Default => B::prepass_fragment_shader(),
specified => specified,
}
}
fn deferred_vertex_shader() -> ShaderRef {
match E::deferred_vertex_shader() {
ShaderRef::Default => B::deferred_vertex_shader(),
specified => specified,
}
}
fn deferred_fragment_shader() -> ShaderRef {
match E::deferred_fragment_shader() {
ShaderRef::Default => B::deferred_fragment_shader(),
specified => specified,
}
}
fn specialize(
pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout,
key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
let MaterialPipeline::<Self> {
mesh_pipeline,
material_layout,
vertex_shader,
fragment_shader,
..
} = pipeline.clone();
let base_pipeline = MaterialPipeline::<B> {
mesh_pipeline,
material_layout,
vertex_shader,
fragment_shader,
marker: Default::default(),
};
let base_key = MaterialPipelineKey::<B> {
mesh_key: key.mesh_key,
bind_group_data: key.bind_group_data.0,
};
B::specialize(&base_pipeline, descriptor, layout, base_key)?;
let MaterialPipeline::<Self> {
mesh_pipeline,
material_layout,
vertex_shader,
fragment_shader,
..
} = pipeline.clone();
E::specialize(
&MaterialExtensionPipeline {
mesh_pipeline,
material_layout,
vertex_shader,
fragment_shader,
},
descriptor,
layout,
MaterialExtensionKey {
mesh_key: key.mesh_key,
bind_group_data: key.bind_group_data.1,
},
)
}
}