use cgmath::AbsDiffEq; use cgmath::{InnerSpace, MetricSpace, Vector3}; use crate::vertex; use super::memorizable::Memorizable; use super::AppData; use super::Scene; pub enum LightType { POINT, DIRECTION } pub trait Light { fn get_light_type(&self) -> LightType; fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32; fn get_direction(&self, pos: Vector3<f32>) -> Vector3<f32>; } pub trait LightSource: Light + Memorizable {} #[derive(Clone, Debug, PartialEq)] pub struct PointLight{ pos: vertex::Vec3, color: vertex::Vec3, pub memory_start: usize, dirty: bool, } impl PointLight { pub fn init(pos: vertex::Vec3, color: vertex::Vec3) -> Self { Self { pos: pos, color: color, memory_start: 0, dirty: true } } pub fn set_pos(&mut self, pos: vertex::Vec3) { self.pos = pos; self.dirty = true; } } impl Memorizable for PointLight { fn get_buffer_mem_size(&self, data: &AppData) -> u32 { 1 + 3 + 3 } fn insert_into_memory(&mut self, mut v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32> { v[self.memory_start] = LightType::POINT as u32; v[self.memory_start + 1] = u32::from_ne_bytes(self.pos.x.to_ne_bytes()); v[self.memory_start + 2] = u32::from_ne_bytes(self.pos.y.to_ne_bytes()); v[self.memory_start + 3] = u32::from_ne_bytes(self.pos.z.to_ne_bytes()); v[self.memory_start + 4] = (self.color.x * 255.0) as u32; v[self.memory_start + 5] = (self.color.y * 255.0) as u32; v[self.memory_start + 6] = (self.color.z * 255.0) as u32; self.dirty = false; v } fn get_memory_start(&self) -> usize { self.memory_start } fn set_memory_start(&mut self, memory_start: usize) { self.memory_start = memory_start; } fn get_prev_buffer_mem_size(&self) -> u32 { 1 + 3 + 3 } fn is_dirty(&self) -> bool { self.dirty } } impl Light for PointLight { fn get_light_type(&self) -> LightType { LightType::POINT } fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32 { let low_end = vertex::Vec3{x: pos.x as f32, y: pos.y as f32, z: pos.z as f32}; let high_end = pos + size; let distance; if low_end.x <= self.pos.x && self.pos.x <= high_end.x && low_end.y <= self.pos.y && self.pos.y <= high_end.y && low_end.z <= self.pos.z && self.pos.z <= high_end.z { let diff_low = self.pos - low_end; let diff_high = self.pos - high_end; distance = diff_low.x.abs().min(diff_low.y.abs().min(diff_low.z.abs().min(diff_high.x.abs().min(diff_high.y.abs().min(diff_high.z.abs()))))); } else { let mut offset_vec = vertex::Vec3 {x: 0.0, y: 0.0, z: 0.0}; while !(low_end.x <= self.pos.x + offset_vec.x && self.pos.x + offset_vec.x <= high_end.x && low_end.y <= self.pos.y + offset_vec.y && self.pos.y + offset_vec.y <= high_end.y && low_end.z <= self.pos.z + offset_vec.z && self.pos.z + offset_vec.z <= high_end.z) { let diff_low = (self.pos + offset_vec) - low_end; let diff_high = (self.pos + offset_vec) - high_end; let mut new_diff = vertex::Vec3 {x: 0.0, y: 0.0, z: 0.0}; if diff_low.x.abs() < diff_high.x.abs() { new_diff.x += diff_low.x; } else { new_diff.x += diff_high.x; } if diff_low.y.abs() < diff_high.y.abs() { new_diff.y += diff_low.y; } else { new_diff.y += diff_high.y; } if diff_low.z.abs() < diff_high.z.abs() { new_diff.z += diff_low.z; } else { new_diff.z += diff_high.z; } if ((new_diff.x < new_diff.y || new_diff.y == 0.0) && (new_diff.x < new_diff.z || new_diff.z == 0.0) && new_diff.x != 0.0) { offset_vec.x -= new_diff.x; } else { if (new_diff.y < new_diff.z || new_diff.z == 0.0) && new_diff.y != 0.0 { offset_vec.y -= new_diff.y; } else { offset_vec.z -= new_diff.z; } } } distance = offset_vec.magnitude(); } let light_intensity = self.color.magnitude(); light_intensity / distance.powf(2.0) } fn get_direction(&self, pos: Vector3<f32>) -> Vector3<f32> { (pos - self.pos).normalize() } } impl LightSource for PointLight {} #[derive(Clone, Debug, PartialEq)] pub struct DirectionalLight{ direction: vertex::Vec3, color: vertex::Vec3, pub memory_start: usize, dirty: bool, } impl DirectionalLight { pub fn init(direction: vertex::Vec3, color: vertex::Vec3) -> Self { Self { direction: direction, color: color, memory_start: 0, dirty: true } } } impl Memorizable for DirectionalLight { fn get_buffer_mem_size(&self, data: &AppData) -> u32 { 1 + 3 + 3 } fn insert_into_memory(&mut self, mut v: Vec<u32>, data: &AppData, scene: &Scene) -> Vec<u32> { v[self.memory_start] = LightType::DIRECTION as u32; v[self.memory_start + 1] = u32::from_ne_bytes(self.direction.x.to_ne_bytes()); v[self.memory_start + 2] = u32::from_ne_bytes(self.direction.y.to_ne_bytes()); v[self.memory_start + 3] = u32::from_ne_bytes(self.direction.z.to_ne_bytes()); v[self.memory_start + 4] = (self.color.x * 255.0) as u32; v[self.memory_start + 5] = (self.color.y * 255.0) as u32; v[self.memory_start + 6] = (self.color.z * 255.0) as u32; self.dirty = false; v } fn get_memory_start(&self) -> usize { self.memory_start } fn set_memory_start(&mut self, memory_start: usize) { self.memory_start = memory_start; } fn get_prev_buffer_mem_size(&self) -> u32 { 1 + 3 + 3 } fn is_dirty(&self) -> bool { self.dirty } } impl Light for DirectionalLight { fn get_light_type(&self) -> LightType { LightType::DIRECTION } fn weighted_distance(&self, pos: Vector3<f32>, size: Vector3<f32>) -> f32 { let light_intensity = self.color.magnitude(); light_intensity } fn get_direction(&self, pos: Vector3<f32>) -> Vector3<f32> { self.direction.clone().normalize() } } impl LightSource for DirectionalLight {} #[cfg(test)] mod test { use super::*; #[test] fn test_memorizable() { let mut p = PointLight::init(vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}); let data= AppData::default(); let scene = Scene::default(); let mem_size = p.get_buffer_mem_size(&data); assert!(mem_size == 7); let mut memory = vec![0; 7]; assert!(!p.dirty); p.insert_into_memory(memory, &data, &scene); assert!(p.dirty); } #[test] #[should_panic] fn test_mem_size() { let mut p = PointLight::init(vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}); let data= AppData::default(); let scene = Scene::default(); let mem_size = p.get_buffer_mem_size(&data); assert!(mem_size == 7); let mut memory = vec![0; 6]; assert!(!p.dirty); p.insert_into_memory(memory, &data, &scene); assert!(p.dirty); } #[test] fn test_memorizable_directional_light() { let mut p = DirectionalLight::init(vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}); let data= AppData::default(); let scene = Scene::default(); let mem_size = p.get_buffer_mem_size(&data); assert!(mem_size == 7); let mut memory = vec![0; 7]; assert!(!p.dirty); p.insert_into_memory(memory, &data, &scene); assert!(p.dirty); } #[test] #[should_panic] fn test_mem_size_directional_light() { let mut p = DirectionalLight::init(vertex::Vec3 { x: 1.0, y: 2.0, z: 3.0}, vertex::Vec3 { x: 0.0, y: 1.0, z: 0.0}); let data= AppData::default(); let scene = Scene::default(); let mem_size = p.get_buffer_mem_size(&data); assert!(mem_size == 7); let mut memory = vec![0; 6]; assert!(!p.dirty); p.insert_into_memory(memory, &data, &scene); assert!(p.dirty); } }