nightshade 0.13.3

A cross-platform data-oriented game engine.
Documentation
use crate::ecs::graphics::resources::Atmosphere;
use crate::ecs::world::World;

pub fn day_night_cycle_system(world: &mut World) {
    if !world.resources.graphics.day_night.auto_cycle {
        return;
    }

    if !matches!(world.resources.graphics.atmosphere, Atmosphere::DayNight) {
        return;
    }

    let delta_time = world.resources.window.timing.delta_time;
    let speed = world.resources.graphics.day_night.speed;
    world.resources.graphics.day_night.hour += speed * delta_time;

    if world.resources.graphics.day_night.hour >= 24.0 {
        world.resources.graphics.day_night.hour -= 24.0;
    }

    let Some(sun) = world.resources.graphics.day_night.sun_entity else {
        return;
    };

    let hour = world.resources.graphics.day_night.hour;
    let direction = sun_direction(hour);
    let is_night = !(6.0..=18.0).contains(&hour);

    let intensity = if is_night {
        0.0
    } else {
        let elevation = direction.y.max(0.0);
        3.5 * elevation.sqrt()
    };

    let warm = nalgebra_glm::vec3(1.0, 0.7, 0.4);
    let white = nalgebra_glm::vec3(1.0, 0.95, 0.8);
    let color = if is_night {
        nalgebra_glm::vec3(0.0, 0.0, 0.0)
    } else if hour < 7.5 {
        let transition = ((hour - 6.0) / 1.5).clamp(0.0, 1.0);
        nalgebra_glm::lerp(&warm, &white, transition)
    } else if hour > 16.5 {
        let transition = ((18.0 - hour) / 1.5).clamp(0.0, 1.0);
        nalgebra_glm::lerp(&warm, &white, transition)
    } else {
        white
    };

    if let Some(light) = world.core.get_light_mut(sun) {
        light.intensity = intensity;
        light.color = color;
    }

    let sun_position = direction * 100.0;
    if let Some(transform) = world.core.get_local_transform_mut(sun) {
        transform.translation = sun_position;
        let look_direction = -direction;
        let up = nalgebra_glm::Vec3::y();
        let right = nalgebra_glm::normalize(&nalgebra_glm::cross(&look_direction, &up));
        if right.norm() > 0.001 {
            let corrected_up = nalgebra_glm::cross(&right, &look_direction);
            transform.rotation = nalgebra_glm::mat3_to_quat(&nalgebra_glm::Mat3::from_columns(&[
                right,
                corrected_up,
                -look_direction,
            ]));
        }
    }
    crate::ecs::transform::commands::mark_local_transform_dirty(world, sun);

    world.resources.graphics.ambient_light = if is_night {
        [0.05, 0.05, 0.1, 1.0]
    } else if !(7.5..=16.5).contains(&hour) {
        let transition = if hour < 7.5 {
            (hour - 6.0) / 1.5
        } else {
            (18.0 - hour) / 1.5
        };
        [
            0.05 + 0.20 * transition,
            0.05 + 0.17 * transition,
            0.10 + 0.10 * transition,
            1.0,
        ]
    } else {
        [0.25, 0.22, 0.20, 1.0]
    };
}

fn sun_direction(hour: f32) -> nalgebra_glm::Vec3 {
    if !(6.0..=18.0).contains(&hour) {
        nalgebra_glm::vec3(0.0, -1.0, 0.0)
    } else {
        let sun_angle = (hour - 6.0) / 12.0 * std::f32::consts::PI;
        nalgebra_glm::normalize(&nalgebra_glm::vec3(-sun_angle.cos(), sun_angle.sin(), -0.3))
    }
}