use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum LightType {
Point,
Directional,
Spot,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Light {
pub position: (f32, f32),
pub color: [u8; 3],
pub intensity: f32,
pub radius: f32,
pub light_type: LightType,
pub direction: f32, pub cone_angle: f32, pub enabled: bool,
}
impl Light {
pub fn point(x: f32, y: f32, radius: f32) -> Self {
Self {
position: (x, y),
color: [255, 255, 255],
intensity: 1.0,
radius,
light_type: LightType::Point,
direction: 0.0,
cone_angle: 45.0,
enabled: true,
}
}
pub fn spot(x: f32, y: f32, direction: f32, cone_angle: f32, radius: f32) -> Self {
Self {
position: (x, y),
color: [255, 255, 255],
intensity: 1.0,
radius,
light_type: LightType::Spot,
direction,
cone_angle,
enabled: true,
}
}
pub fn with_color(mut self, r: u8, g: u8, b: u8) -> Self {
self.color = [r, g, b];
self
}
pub fn with_intensity(mut self, intensity: f32) -> Self {
self.intensity = intensity.clamp(0.0, 1.0);
self
}
pub fn calculate_intensity(&self, x: f32, y: f32) -> f32 {
if !self.enabled {
return 0.0;
}
let dx = x - self.position.0;
let dy = y - self.position.1;
let distance = (dx * dx + dy * dy).sqrt();
if distance > self.radius {
return 0.0;
}
match self.light_type {
LightType::Point => {
let attenuation = 1.0 - (distance / self.radius);
attenuation * self.intensity
}
LightType::Spot => {
let angle_to_point = dy.atan2(dx).to_degrees();
let angle_diff = (angle_to_point - self.direction).abs();
if angle_diff > self.cone_angle {
return 0.0;
}
let attenuation = 1.0 - (distance / self.radius);
let cone_falloff = 1.0 - (angle_diff / self.cone_angle);
attenuation * cone_falloff * self.intensity
}
LightType::Directional => self.intensity,
}
}
}
#[derive(Debug)]
pub struct LightingSystem {
pub lights: Vec<Light>,
pub ambient_color: [u8; 3],
pub ambient_intensity: f32,
}
impl LightingSystem {
pub fn new() -> Self {
Self {
lights: Vec::new(),
ambient_color: [50, 50, 50],
ambient_intensity: 0.2,
}
}
pub fn add_light(&mut self, light: Light) -> usize {
self.lights.push(light);
self.lights.len() - 1
}
pub fn remove_light(&mut self, index: usize) {
if index < self.lights.len() {
self.lights.remove(index);
}
}
pub fn set_ambient(&mut self, r: u8, g: u8, b: u8, intensity: f32) {
self.ambient_color = [r, g, b];
self.ambient_intensity = intensity.clamp(0.0, 1.0);
}
pub fn apply_lighting(&self, x: f32, y: f32, base_color: [u8; 4]) -> [u8; 4] {
let mut total_r = self.ambient_color[0] as f32 * self.ambient_intensity;
let mut total_g = self.ambient_color[1] as f32 * self.ambient_intensity;
let mut total_b = self.ambient_color[2] as f32 * self.ambient_intensity;
for light in &self.lights {
let intensity = light.calculate_intensity(x, y);
if intensity > 0.0 {
total_r += light.color[0] as f32 * intensity;
total_g += light.color[1] as f32 * intensity;
total_b += light.color[2] as f32 * intensity;
}
}
let r = ((base_color[0] as f32 * total_r / 255.0).min(255.0)) as u8;
let g = ((base_color[1] as f32 * total_g / 255.0).min(255.0)) as u8;
let b = ((base_color[2] as f32 * total_b / 255.0).min(255.0)) as u8;
[r, g, b, base_color[3]]
}
}