use bevy::app::App;
use bevy::asset::{Asset, Handle};
use bevy::color::{Color, LinearRgba};
use bevy::image::Image;
use bevy::material::AlphaMode;
use bevy::math::Vec4;
use bevy::mesh::MeshVertexBufferLayoutRef;
use bevy::pbr::{Material, MaterialPipeline, MaterialPipelineKey};
use bevy::reflect::TypePath;
use bevy::render::render_resource::{
AsBindGroup, RenderPipelineDescriptor, ShaderType, SpecializedMeshPipelineError,
};
use bevy::shader::ShaderRef;
use bevy::render::storage::ShaderBuffer;
use crate::params::RenderStyle;
use crate::shading::{FloraUniform, FloraWindBuffer};
#[derive(Asset, AsBindGroup, TypePath, Debug, Clone)]
#[bind_group_data(ImpostorKey)]
pub struct ImpostorMaterial {
#[texture(0)]
#[sampler(1)]
pub albedo: Handle<Image>,
#[texture(2)]
pub surface: Handle<Image>,
#[uniform(3)]
pub params: Vec4,
#[uniform(101)]
pub flora: FloraUniform,
#[storage(102, read_only)]
pub wind: Handle<ShaderBuffer>,
#[uniform(4)]
pub tint: ImpostorTint,
pub see_through: bool,
pub fire: bool,
}
#[derive(ShaderType, Clone, Copy, Debug, PartialEq, Default)]
pub struct ImpostorTint {
pub wood: Vec4,
pub leaf: Vec4,
}
impl ImpostorTint {
pub fn new(wood: Color, leaves: Color) -> Self {
let twice = |color: Color| {
let c = color.to_linear();
Vec4::new(2.0 * c.red, 2.0 * c.green, 2.0 * c.blue, 1.0)
};
Self { wood: twice(wood), leaf: twice(leaves) }
}
}
impl ImpostorMaterial {
#[allow(clippy::too_many_arguments)]
pub fn new(
albedo: Handle<Image>,
surface: Handle<Image>,
views: u32,
style: RenderStyle,
flora: FloraUniform,
wind: &FloraWindBuffer,
tint: ImpostorTint,
unlit: bool,
) -> Self {
Self {
albedo,
surface,
params: Vec4::new(views as f32, style as u32 as f32, 0.0, f32::from(u8::from(unlit))),
flora,
tint,
wind: wind.0.clone(),
see_through: false,
fire: false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ImpostorKey {
pub see_through: bool,
pub fire: bool,
}
impl From<&ImpostorMaterial> for ImpostorKey {
fn from(material: &ImpostorMaterial) -> Self {
Self { see_through: material.see_through, fire: material.fire }
}
}
impl Material for ImpostorMaterial {
fn vertex_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor.wesl".into()
}
fn fragment_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor.wesl".into()
}
fn alpha_mode(&self) -> AlphaMode {
AlphaMode::AlphaToCoverage
}
fn prepass_vertex_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor_prepass.wesl".into()
}
fn prepass_fragment_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor_prepass.wesl".into()
}
fn enable_prepass() -> bool {
false
}
fn enable_shadows() -> bool {
true
}
fn specialize(
_pipeline: &MaterialPipeline,
descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
descriptor.primitive.cull_mode = None;
let defs = [(key.bind_group_data.see_through, "FLORA_SEE_THROUGH"), (key.bind_group_data.fire, "FLORA_FIRE")];
for (on, def) in defs {
if on && let Some(fragment) = descriptor.fragment.as_mut() {
fragment.shader_defs.push(def.into());
}
}
let prepass = descriptor.vertex.shader_defs.contains(&"PREPASS_PIPELINE".into());
if !prepass {
for def in ["FLORA_GLIDE", "VERTEX_OUTPUT_INSTANCE_INDEX"] {
descriptor.vertex.shader_defs.push(def.into());
if let Some(fragment) = descriptor.fragment.as_mut() {
fragment.shader_defs.push(def.into());
}
}
}
Ok(())
}
}
#[derive(Asset, AsBindGroup, TypePath, Debug, Clone)]
pub(crate) struct ImpostorBakeMaterial {
#[uniform(0)]
pub color: LinearRgba,
#[uniform(1)]
pub params: Vec4,
}
impl ImpostorBakeMaterial {
pub(crate) fn colour(color: LinearRgba) -> Self {
Self { color, params: Vec4::new(0.0, 0.0, 1.0, 0.0) }
}
pub(crate) fn surface(color: LinearRgba, translucency: f32) -> Self {
Self { color, params: Vec4::new(1.0, translucency, 1.0, 0.0) }
}
}
impl Material for ImpostorBakeMaterial {
fn vertex_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor_bake.wesl".into()
}
fn fragment_shader() -> ShaderRef {
"embedded://bevy_flora/impostor/impostor_bake.wesl".into()
}
fn enable_prepass() -> bool {
false
}
fn enable_shadows() -> bool {
false
}
}
pub(crate) fn add_shaders(app: &mut App) {
bevy::shader::load_shader_library!(app, "impostor_common.wesl");
bevy::asset::embedded_asset!(app, "impostor.wesl");
bevy::asset::embedded_asset!(app, "impostor_prepass.wesl");
bevy::asset::embedded_asset!(app, "impostor_bake.wesl");
}