use bevy_asset::Handle;
use bevy_camera::{
primitives::{CascadesFrusta, Frustum},
visibility::{self, CascadesVisibleEntities, ViewVisibility, Visibility, VisibilityClass},
Camera,
};
use bevy_color::Color;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_reflect::prelude::*;
use bevy_transform::components::Transform;
use tracing::warn;
use super::{
cascade::CascadeShadowConfig, cluster::ClusterVisibilityClass, light_consts, Cascades,
};
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
#[require(
Cascades,
CascadesFrusta,
CascadeShadowConfig,
CascadesVisibleEntities,
Transform,
Visibility,
VisibilityClass
)]
#[component(on_add = visibility::add_visibility_class::<ClusterVisibilityClass>)]
pub struct DirectionalLight {
pub color: Color,
pub illuminance: f32,
pub shadows_enabled: bool,
#[cfg(feature = "experimental_pbr_pcss")]
pub soft_shadow_size: Option<f32>,
pub affects_lightmapped_mesh_diffuse: bool,
pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32,
}
impl Default for DirectionalLight {
fn default() -> Self {
DirectionalLight {
color: Color::WHITE,
illuminance: light_consts::lux::AMBIENT_DAYLIGHT,
shadows_enabled: false,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
affects_lightmapped_mesh_diffuse: true,
#[cfg(feature = "experimental_pbr_pcss")]
soft_shadow_size: None,
}
}
}
impl DirectionalLight {
pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 1.8;
}
#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
#[require(DirectionalLight)]
pub struct DirectionalLightTexture {
pub image: Handle<Image>,
pub tiled: bool,
}
#[derive(Resource, Clone, Debug, Reflect)]
#[reflect(Resource, Debug, Default, Clone)]
pub struct DirectionalLightShadowMap {
pub size: usize,
}
impl Default for DirectionalLightShadowMap {
fn default() -> Self {
Self { size: 2048 }
}
}
pub fn validate_shadow_map_size(mut shadow_map: ResMut<DirectionalLightShadowMap>) {
if shadow_map.is_changed() && !shadow_map.size.is_power_of_two() {
let new_size = shadow_map.size.next_power_of_two();
warn!("Non-power-of-two DirectionalLightShadowMap sizes are not supported, correcting {} to {new_size}", shadow_map.size);
shadow_map.size = new_size;
}
}
pub fn update_directional_light_frusta(
mut views: Query<
(
&Cascades,
&DirectionalLight,
&ViewVisibility,
&mut CascadesFrusta,
),
(
// Prevents this query from conflicting with camera queries.
Without<Camera>,
),
>,
) {
for (cascades, directional_light, visibility, mut frusta) in &mut views {
if !directional_light.shadows_enabled || !visibility.get() {
continue;
}
frusta.frusta = cascades
.cascades
.iter()
.map(|(view, cascades)| {
(
*view,
cascades
.iter()
.map(|c| Frustum::from_clip_from_world(&c.clip_from_world))
.collect::<Vec<_>>(),
)
})
.collect();
}
}
#[derive(Component, Clone)]
#[require(DirectionalLight)]
pub struct SunDisk {
pub angular_size: f32,
pub intensity: f32,
}
impl SunDisk {
pub const EARTH: SunDisk = SunDisk {
angular_size: 0.00930842,
intensity: 1.0,
};
pub const OFF: SunDisk = SunDisk {
angular_size: 0.0,
intensity: 0.0,
};
}
impl Default for SunDisk {
fn default() -> Self {
Self::EARTH
}
}
impl Default for &SunDisk {
fn default() -> Self {
&SunDisk::EARTH
}
}