use bevy::camera::{Camera, Camera3d};
use bevy::color::LinearRgba;
use bevy::core_pipeline::prepass::DepthPrepass;
use bevy::prelude::*;
use bevy::render::extract_component::ExtractComponent;
use bevy::render::render_resource::ShaderType;
#[derive(Debug, Clone, Reflect)]
pub struct ExponentialHeightFogLayer {
pub enabled: bool,
pub density: f32,
pub height_falloff: f32,
pub height_offset: f32,
}
impl Default for ExponentialHeightFogLayer {
fn default() -> Self {
Self {
enabled: true,
density: 0.02,
height_falloff: 0.2,
height_offset: 0.0,
}
}
}
#[derive(Debug, Clone, Copy, Reflect, Default, PartialEq, Eq)]
pub enum ExponentialHeightFogDistanceMode {
#[default]
Camera,
SuppliedWorldCoordinates,
}
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component, Default, Clone)]
#[require(Camera3d, DepthPrepass)]
pub struct ExponentialHeightFog {
pub enabled: bool,
pub fog_color: LinearRgba,
pub primary: ExponentialHeightFogLayer,
pub secondary: ExponentialHeightFogLayer,
pub distance_mode: ExponentialHeightFogDistanceMode,
pub distance_reference_world_position: Vec3,
pub start_distance: f32,
pub cutoff_distance: f32,
pub distance_fade: f32,
pub max_opacity: f32,
pub min_density_weight: f32,
pub min_fog_alpha: f32,
pub sky_depth_threshold: f32,
}
impl Default for ExponentialHeightFog {
fn default() -> Self {
Self {
enabled: true,
fog_color: LinearRgba::new(0.60, 0.66, 0.72, 1.0),
primary: ExponentialHeightFogLayer::default(),
secondary: ExponentialHeightFogLayer {
enabled: false,
density: 0.01,
height_falloff: 0.35,
height_offset: 2.5,
},
distance_mode: ExponentialHeightFogDistanceMode::Camera,
distance_reference_world_position: Vec3::ZERO,
start_distance: 0.0,
cutoff_distance: 0.0,
distance_fade: 2.0,
max_opacity: 1.0,
min_density_weight: 0.0,
min_fog_alpha: 0.001,
sky_depth_threshold: 0.999999,
}
}
}
#[derive(Component, ShaderType, Clone)]
pub struct ExponentialHeightFogUniform {
pub fog_color: Vec4,
pub primary_params: Vec4,
pub secondary_params: Vec4,
pub distance_params: Vec4,
pub distance_reference_params: Vec4,
pub optimization_params: Vec4,
}
impl ExtractComponent for ExponentialHeightFog {
type QueryData = &'static Self;
type QueryFilter = With<Camera>;
type Out = ExponentialHeightFogUniform;
fn extract_component(item: bevy::ecs::query::QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
let fog_color = item.fog_color.to_vec4();
let primary_params = Vec4::new(
item.primary.density.max(0.0),
item.primary.height_falloff.max(0.00001),
item.primary.height_offset,
if item.primary.enabled { 1.0 } else { 0.0 },
);
let secondary_params = Vec4::new(
item.secondary.density.max(0.0),
item.secondary.height_falloff.max(0.00001),
item.secondary.height_offset,
if item.secondary.enabled { 1.0 } else { 0.0 },
);
let distance_params = Vec4::new(
item.start_distance.max(0.0),
item.cutoff_distance.max(0.0),
item.distance_fade.max(0.001),
item.max_opacity.clamp(0.0, 1.0),
);
let distance_reference_params = Vec4::new(
item.distance_reference_world_position.x,
item.distance_reference_world_position.y,
item.distance_reference_world_position.z,
match item.distance_mode {
ExponentialHeightFogDistanceMode::Camera => 0.0,
ExponentialHeightFogDistanceMode::SuppliedWorldCoordinates => 1.0,
},
);
let optimization_params = Vec4::new(
item.min_density_weight.clamp(0.0, 1.0),
item.min_fog_alpha.clamp(0.0, 1.0),
item.sky_depth_threshold.clamp(0.0, 1.0),
if item.enabled { 1.0 } else { 0.0 },
);
Some(ExponentialHeightFogUniform {
fog_color,
primary_params,
secondary_params,
distance_params,
distance_reference_params,
optimization_params,
})
}
}